美文网首页
Javascript 作用域链、闭包的理解和应用

Javascript 作用域链、闭包的理解和应用

作者: web前端_ElonWu | 来源:发表于2019-02-18 22:18 被阅读54次

    1. 什么是作用域,作用域链

    作用域,指代码所在的执行环境。
    代码执行时产生的最先产生的执行环境,称为全局作用域, 如浏览器为 window, node 为 global. 作用域内部执行时产生的、新的作用域为局部作用域。(es5 不存在块级作用域,作用域仅通过函数作用域分隔。而 es6 中通过 let 和 const 声明的变量和常量,在 if 和 for 的执行部分拥有块级作用域。)

        // 块级作用域
        for(let i = 0; i < 3; i++) {
            console.log(i) // 依次打印 1, 2, 3
        }
        console.log(i) // 报错: Uncaught ReferenceError: i is not defined
    

    由此逐层衍生、逐层嵌套的作用域,称之为作用域链。它们拥有以下的特性:

    1-1 外层作用域无法调用内层作用域的变量和常量。比如,全局作用域无法调用局部作用域声明的变量或常量。

    function func(){
        var foo = 111;
        console.log(foo)
    }
    func() // 111
    console.log(foo) // 报错: Uncaught ReferenceError: foo is not defined
    

    1-2 代码执行需要时,优先引用当前所在执行环境中的变量和常量;当前执行环境不存在引用到的对象时,则追测其所在作用域链的上游。因此:任何局部作用域都可引用全局变量。

    var foo = 111;
    function func(){
        console.log(foo)
    }
    func() // 111
    

    1-3 直至追溯到最外层作用域(如上所说的 window 或 global)仍未定义的引用,即为 not defined.

    2. 作用域的特殊情况

    es5 非严格模式下,当在局部作用域不声明、赋值一个变量时, 产生的是一个全局变量。

    // 非严格模式下
    function test() {
        a = 111;
    }
    test();
    console.log(a); // 111
    

    3. 什么是闭包,如何产生?有哪些应用?优缺点分别是什么?

    当函数执行完成后,返回一个值时,当前执行环境会被清理掉。但是当函数返回一个调用了局部变量的函数时,外层函数的执行环境不会被清理掉,此作用域内的变量仍能被返回的函数引用。由此形成的即为闭包。

    闭包应用

    3-1 例1:对应位置的斐波那契数

    const fib = (() => {
      // 利用闭包避免重复计算
      let cache = { 1: 1, 2: 1 };
      const func = num => {
        // 避免误传非正整数造成死循环
        let n = parseInt(num) >= 1 ? parseInt(num) : 1;
        if (cache[n]) return cache[n];
        // 递归
        return (cache[n] = func(n - 2) + func(n - 1));
      };
      return func;
    })();
    
    console.log( fib(5) ) // 8
    

    3-2 例2:通过调用返回的函数, 形成一个外部作用域能够获取、修改局部变量的间接渠道。

     function closure() {
         let n = 0;
         return {
             add: (m = 1 ) => n+=m,
             minus: (m = 1) => n-=m,
             getN: () => n
         }
     }
     
     // 外层作用域无法直接获取和修改 n; 但是可以通过 closure 返回的函数,进行获取和操作。
     const {add, minus, getN} = closure()
    getN(); // 0
    add(2);
    getN(); // 2
    minus();
    getN(); // 1
    
    优缺点

    优点:对于可能重复的运算,进行缓存;

    缺点:当运算数字过大时,会产生内存占用;影响整体性能;

    关联文章:

    再谈对 Javascript 作用域的理解

    相关文章

      网友评论

          本文标题:Javascript 作用域链、闭包的理解和应用

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