美文网首页
函数全解

函数全解

作者: YM雨蒙 | 来源:发表于2020-04-18 17:53 被阅读0次

    函数的环境

    函数是什么

    函数的返回值有什么确定

    • 影响因素
      • 调用时输入的参数 params
      • 定义时的环境 env
    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 是参数 (不包含箭头函数)
      • this 就是一个隐式参数而已

    this 的确定

    • 显式this
    fn.call(asThis, 1, 3)
    
    fn.bind(asThis, 1, 2)()
    
    obj.method.call(obj, 'hi')
    
    • 隐式this
    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(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
    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)
        }
    })
    

    相关文章

      网友评论

          本文标题:函数全解

          本文链接:https://www.haomeiwen.com/subject/wfoiuhtx.html