函数全解

2020-04-18  本文已影响0人  YM雨蒙

函数的环境

函数是什么

函数的返回值有什么确定

let x = 'x'
let a = 1
function f1(x) {
  // x 是参数 params
  // a 是环境 由f1 所处的环境确定
  return x + a  // a 只是确定是哪个a, 而不是值
}

{
  let a = 2
  f1('x')
}
"x1"
// a 是 定义时的 a , 而不是 执行时的 a
let x = 'x'
let a = 1
function f1(x) {
  // x 是参数 params
  // a 是环境 由f1 所处的环境确定
  return x + a  // a 只是确定是哪个a, 而不是值
}
a = '3'
{
  let a = 2
  f1('x')
}

// a 是 定义时的 a , 而不是 执行时的 a
"x3"
// 例2

let x = 'x'
let a = 1
function f1(c) {
  c()
}

{
  let a = '2'
  // x a 都不是参数 找定义时的环境
  function f2() {
    console.log(x + a)
  }
  f1(f2)
}

x2

上面的例子: 在函数里面能访问外面的变量, 这不是天经地义吗? 并不是, 比如 Ruby

def f1
  a = 1 # 局部变量 a
  def f2
    print a # 打印 a
  end
  f2()
end
f1()

# 会报错, 找不到 a, 不能访问外部变量

# 需要访问的话
def f1
  a = 1
  f2 = lambda { print a } # 使用 lambda 打印 a
  f2.call()
end
f1()

1

闭包

如果在函数里面可以访问外面的变量, 那么这个函数 + 这些变量 = 闭包

for(var i = 0; i < 6; i++) {
  setTimeout(() => {  // 箭头函数访问了 i
    console.log(i)
  })
}
// 6 6 6 6 6 6

1.  把 var ==> let  //  0 1 2 3 4 5

2. 
for(var i = 0; i < 6; i++) {
  !function(j){
    setTimeout(() => {
      console.log(j)
    })
  }(i)
}

结论


this变态面试题

复习: 声明一个函数

cosnt f1 = new Function('x', 'y', 'return x + y')

function f2(x, y) {return x + y}

const f3 = function(x, y){return x + y}

const f4 = (x, y) => x + y

f1 f2 f3 是 ES6之前的东西,  支持 this/arguments/new
f4 是 ES6 的箭头函数, 不支持 this/arguments/new

箭头函数不支持 this

const a = 42
const f1 = () => console.log(a)  // 42

console.log(this)
const f2 = () => console.log(this)

箭头函数如何处理 a  , 就如何处理 this

箭头函数把 this 当做外部的变量, 仅此而已

箭头函数不支持 this: 箭头函数对待 this 和 其他变量一样, 不会不特殊对待 

this 是参数还是环境

this 的确定

fn.call(asThis, 1, 3)

fn.bind(asThis, 1, 2)()

obj.method.call(obj, 'hi')
fn(1, 2)  // fn.call(undefined, 1, 2)

obj.method('hi')  // obj.method.call(obj, 'hi')

array[0]('hi')  // array[0].call(array, 'hi')
// test

button.onclick = function(e) {
  console.log(this)  // this 无法确定, 需要看 用户如何调用,
}

变态面试题

let length = 10
function fn() {console.log(this.length)}

let obj = {
  length: 5,
  method(fn) {
    fn() // ?
    arguments[0]()  // ?
  }
}

obj.method(fn, 1)

这是考察 this
1. obj.method(fn, 1)  fn 指的是函数 fn
2. fn() ==> 考察this 指向 ==> fn.call(undefined) ==> this 指向 window
3. this指向window ==> let 定义的 不挂在 window
4. window.length 指的是 ==> 返回当前窗口中包含的框架数量(框架包括frame和iframe两种元素).  ==> 所以值等于你页面的 iframe

5. arguments[0]()  ==> 考察this ==> arguments.0.call(arguments)
6. arguments.0 ==> 参数第一个 ==> fn ==> fn.call(arguments)
7. tihs.length ==> 指的是arguments.length  ==> 有几个实参就是几

递归 记忆化 & React优化

递归

const j = n => n === 1 ? 1 : n * j(n - 1)

// 理解 代入法
j(3)
= 3 * j(2)
= 3 * (2 * j(1))
= 3 * (2 * 1)
= 6

调用栈的作用:
先押栈  ==> 再把押进去的弹出来 进行计算
F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
// 每一项都是前两项的和
const fib = n => n === 0 ? 0 : n === 1 ? 1 : fib(n - 1) + fib(n - 2)

// 理解 代入法
fib(3)
= fib(2) + fib(1)
= ((fib(1) + fib(0)) + 1) 
= (1 + 0 + 1)
= 2

调用栈用来记忆 [回到哪]
如果需要记忆的过多, 就会爆栈

如何降低押栈/计算次数

使用迭代代替递归

原理:  0, 1, 1, 2, 3, 5, 8, ....

const f = n => f_inner(2, n, 0, 1)

f_inner = (start, end, prev1, prev2) => {
  start === end ? prev1 + prev2
    : f_inner(start + 1, end, prev1 + prev2, prev1)
}

==> 
0, 1, 1, 2, 3  ==> 0+1 1+1  1+ 2 ...

f(4)
f(2, 4, 1, 0) // 开始计算 4项的值, 知道前两项 f(0) = 0 f(1) = 1 先计算前两项的和为第三个参数, 第四个参数是第一个值 
f(3, 4, 1, 1) // 根据上面的尾递归 2 !== 4 进行第3项 第三个参数 为上次计算 prev1 + prev2, 第四个参数为 为上次计算的 prev1 参数,
f(4, 4, 2, 1)  // start === end 4 === 4  结果为 prev1 + prev2  1+2 = 3

-使用数组表示

var fib = function(N) {
    let arr = [0, 1]
    for(let i = 0; i < N - 2; i++ ) {
        arr[i+2] = arr[i] + arr[i+1]
    }
    return arr[arr.length-1]
};

fib(30)
function memozi(fn){
  var r = {}
  return function(n){
    if(r[n] == null){
      r[n] = fn(n)
      return r[n]
    }else{
        return r[n]
    }
  }
}

var fibfn = memozi(function(n){
    if(n==0){
        return 0
    }else if(n==1){
        return 1
    }else{
        return fibfn(n-1) + fibfn(n-2)
    }
})
上一篇下一篇

猜你喜欢

热点阅读