最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 不得不知的闭包

    正文概述 掘金(孤佣)   2021-04-01   461

    闭包的概念

    换言之,闭包是由函数以及声明该函数的词法环境组合而成的。词法环境包含了这个闭包创建时作用域内的任何局部变量

    上下文与作用域

    变量或函数的上下文决定了它们可以访问哪些数据(变量),以及它们的行为。每个上下文都有一个关联的变量对象(variable object),而这个上下文中定义的所有变量和函数都存在于这个对象上。每个函数调用都有自己的上下文。上下文中的代码在执行的时候会创建变量对象(variable object)的一个作用域链(scope chain)。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。

    每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中搜索。换言之,内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文的任何东西。位于最顶端或最外层的上下文称为全局上下文(global context),全局上下文取决于执行环境,如 Node 中的global和 Browser 中的window

    var color = "blue"; 
    function changeColor() { 
       let anotherColor = "red"; 
       function swapColors() { 
         let tempColor = anotherColor; 
         anotherColor = color; 
         color = tempColor; 
         // 这里可以访问 color、anotherColor 和 tempColor 
       } 
       // 这里可以访问 color 和 anotherColor,但访问不到 tempColor 
       swapColors(); 
    } 
    // 这里只能访问 color 
    changeColor();
    

    以上代码涉及 3 个上下文:全局上下文、 changeColor() 的局部上下文和 swapColors() 的局部 上下文。全局上下文中有一个变量 color  和一个函数 changeColor() 。 changeColor() 的局部上下文中有一个变量 anotherColor  和一个函数 swapColors() ,但在这里可以访问全局上下文中的变量 color 。 swapColors() 的局部上下文中有一个变量 tempColor ,只能在这个上下文中访问到。全局上下文和 changeColor() 的局部上下文都无法访问到 tempColor 。而在 swapColors() 中则可以访问另外两个上下文中的变量,因为它们都是父上下文。

    不得不知的闭包

    再举个栗子

    var a = 1
    function out(){
        var a = 2
        inner()
    }
    function inner(){
        console.log(a)
    }
    out()  //====>  1
    

    闭包的原因

    **

    function func() {
        let name = "Closure";
        function alertName() {
            alert(name);
        }
        return alertName;
    }
    const myFunc = func();
    myFunc();
    

    在上面的例子中,myFunc 是执行 func 时创建的 alertName 函数实例的引用。 alertName 的实例维持了一个对它的词法环境(变量 name 存在于其中)的引用。因此,当 myFunc 被调用时,变量 name 仍然可用,其值 Closure 就被 alert 。

    function makeAdder(x) {
      return function(y) {
        return x + y;
      };
    }
    const add5 = makeAdder(5);
    const add10 = makeAdder(10);
    console.log(add5(2));  // 7
    console.log(add10(2)); // 12
    

    在上面的示例中, makeAdder(x) 接受一个参数 x ,并返回一个新的函数。返回的函数接受一个参数 y,并返回 x+y 。创建两个新函数:一个将其参数和 5 求和,另一个和 10 求和。

    add5add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10。

    闭包的作用

    • 保护函数的私有变量不受外部的干扰,把一些函数内的值保存下来。
    • 使用闭包来实现方法和属性的私有化。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。
    const Counter = (function() {
      let privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }
    })();
    console.log(Counter.value()); /* logs 0 */
    Counter.increment();
    Counter.increment();
    console.log(Counter.value()); /* logs 2 */
    Counter.decrement();
    console.log(Counter.value()); /* logs 1 */
    

    在上面的示例中,每个闭包都有它自己的词法环境;而这次只创建了一个词法环境,为三个函数所共享 Counter.increment Counter.decrement Counter.value 。 该共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项:名为 privateCounter 的变量和名为 changeBy 的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。

    常见错误:循环中使用闭包

    for(var i = 0; i < 5; i++) {
        setTimeout(() => {
            console.log(i);
        }, i * 1000);
    }
    

    上面的这段代码,预期是每隔一秒,分别输出 0, 1, 2, 3, 4, 但实际上依次输出的都是 5setTimeout 是个闭包,这五个闭包在循环中被创建,这里的 i 使用 var 进行声明,由于变量提升,所以具有全局作用域,共享同一个词法作用域,在这个作用域中存在一个变量i ,当函数执行完毕 i = 5 ,导致输出的结果都是 5。

    解决办法一:新增匿名闭包

    for(var i = 0; i < 5; i++) {
        (function(item) {
            setTimeout(() => {
                console.log(item);
            }, i * 1000);
        })(i);
    }
    
    for(var i = 0; i < 5; i++) {
        (function() {
            var item = i
            setTimeout(() => {
                console.log(item);
            }, i * 1000);
        })();
    }
    

    解决办法二:使用 let 关键字

    for(let i = 0; i < 5; i++) {
        setTimeout(() => {
            console.log(i);
        }, i * 1000);
    }
    

    使用let而不是var,因此每个闭包都绑定了块作用域的变量,这意味着不再需要额外的闭包。

    解决办法三:使用 forEach

    [0, 1, 2, 3, 4].forEach((i) => {
        setTimeout(() => {
            console.log(i)
        }, i * 1000)
    })
    

    本质上与解法一是一样的,分别产生五个闭包函数,入参分别在对应的上下文中。


    起源地下载网 » 不得不知的闭包

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元