再谈JS闭包(JS闭包系列2)

作者: 夏夜星语 | 来源:发表于2016-03-23 15:49 被阅读770次

    这篇文章,来继续谈谈Javascript闭包的剩余问题。因为在上一篇文章中关于JS闭包(JS闭包系列1)主要简单的示例代码直观的观察了下闭包,这篇文章就来从理论上好好地分析分析JS闭包的有关概念。

    首先应该知道的是JS中的作用域链(scope chain)概念:

    (1)定义
    每个人都有自己的生活环境,代码也一样,JS中的代码都有自己的“执行环境”。当代码在一个环境中定义时(函数调用之前),就会生成变量对象的作用域链。这个链条,从当前环境中的变量对象开始,上溯至父环境(包含环境)中的变量对象,再到父环境的父环境中的变量对象···一直上溯到全局执行环境的变量对象。想象着一条锁链,从最下面开始爬,一直爬到最上顶端。每一个节点,代表着这一层级环境中的变量对象。

    需要注意的是,作用域链是根据 函数定义 时的位置确定的,而不是在调用时确定。这也叫做 词法作用域
    chain
    (2)作用
    作用域链用来保证那些有权访问不同执行环境的代码,在对不同环境里的变量和函数访问时的有序性以及确定性

    (3)应用
    当解析一个标识符时,是沿着作用域链一级一级地上溯(回溯)搜索标识符的过程。如果找不到标识符,就会产生错误。

    还是直接看代码吧:

    var color = "blue";  //全局作用域
    function changeColor(){
        if(color === "blue"){ //寻找`color`,此作用域(函数内)没有,则上溯到父级作用域,(这里是全局)找到了
              color = "red";
        }else{
              color = "blue";
        }
    }
    changeColor();
    alert("Color is now " + color);
    

    在这个例子中,函数changeColor的作用域链包含两个对象:它自己的变量对象(每个函数都有的arguments对象),以及全局环境中的变量对象。

    再来看一个多层作用域链示例:

    var weibing = "在";  //最外层守卫
    var dajiangjun = "不在";  //2号大将军
    function chengqiang(){  //城墙内
        var dajiangjun= "在";  //1号大将军
        function gongdian(){  //宫殿内
            var zaixiang = "不在";  //宰相
            if(zaixiang === "在")
                 console.log("把宰相找来!");
            else if(dajiangjun === "在")
                 console.log("把大将军找来!");
            else
                 console.log("把卫兵找来!");
        }
         return gongdian();
    }
     chengqiang();    
    

    结果可想而知:


    皇帝召见层级

    想象这样一个场景:皇帝身居寝宫,寂寞了,想要招纳美女。那么首先想到的是宰相,他可以直接召见宰相,让宰相给自己办事。假如宰相不在,那么他可以召见大将军(职级低于宰相),让大将军给自己干;碰巧,大将军也去喝酒了,那就直接召见卫兵。在这个例子中,皇帝找到大将军了。但是有人会问,这段代码里,有两个dajiangjun啊!对啊,可是,皇帝他老人家是从自己身边找人的。最外边的那个2号大将军,虽然也在,但是他没上朝,去考察去了。(没在城墙内),所以就召见1号大将军喽。而且自然而然,在城墙外边的人(例如普通老百姓),当然是见不到宰相和皇帝的啦。
    例如在上面代码下面加如下代码:

    function chixinwangxiang(){ //痴心妄想
          alert("把"+zaixiang+"给爷叫出来!");
    }
    

    结果是:


    痴心妄想

    在城墙外边,你肯定是见不到一人之下,万人之上的宰相大人啦!

    (4)注意
    因为在JS中,作用域里没有以花括号包围的“块级作用域”概念,看一个例子:

    if(true){
        var a = 1;
    }
    console.log(a);  //输出1
    

    表面上看来, a在if语句里面定义着,那么console语句应该是访问不了的,然而事实恰恰相反,因为在JS中,if或者for语句的花括号,根本不是独立作用域,只有函数的花括号才起作用。

    总结:现在知道了作用域链的概念,而你应该知道的是,闭包就是由作用域链引起的。下一节,就要带着作用域链的思维来思考闭包,那样就简单多了。因为你会发现那是顺其自然。

    本篇完

    相关文章

      网友评论

      • Bigbang_boy:顶顶顶顶顶顶!楼主讲的不错:smile:
      • 歐吉桑愛設計:皇上召见大将军,楼主真是脑洞大开啊。。这联想真是棒极了!还有if, for的花括号非独立作用域,我涨知识了
        夏夜星语: @歐吉桑愛設計 谢谢😀
        夏夜星语: @歐吉桑愛設計 哈哈哈 😄

      本文标题:再谈JS闭包(JS闭包系列2)

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