深入javascript之作用域链和闭包

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yucihent/article/details/80530044

前言:深入js对学习框架很重要,希望这一系列文章会对你有帮助(持续更新中)


一,前言

有关作用域和执行上下文还不是很清楚的上面有链接,可以去看看。

函数内可以访问到函数外的变量,但函数外无法访问函数内的变量(此处包含函数嵌套函数)为什么?


二,作用域链

在创建一个执行上下文时就会创建一个作用域链,包含自己的变量对象,外层函数上下文变量对象以及全局上下文的变量对象,当某一个函数执行时该函数上下文中的变量对象称为活动对象。

看个例子:

var golContext = 'global';
function foo() {
    var fooContext = 'foo';
    function bar() {
        var barContext = 'bar';
        console.log(golContext);    //global
        console.log(fooContext);    //foo
        console.log(barContext);    //bar
    }
    bar();
    console.log(golContext);        //global
    console.log(fooContext);        //foo
    console.log(barContext);        //Error barContext is not defined
}
foo();
console.log(golContext);            //global
console.log(fooContext);            //Error barContext is not defined
console.log(barContext);            //Error barContext is not defined

结果大家都明白,我们具体分析:

foo()的作用域链包含:foo()的活动对象,全局变量对象
bar()的作用域链包含:bar()的活动对象,foo()的变量对象,全局变量对象

上述作用域链包含的变量对象位置是有要求的,作用域链的前端始终都是当前执行代码所在上下文的变量对象,末端始终都是全局上下文的变量对象。

由此我们再来想想为什么函数内可以访问到函数外的变量,而函数外无法访问函数内部变量:
查找某一个变量或函数时会先在当前函数上下文的活动对象中查找,如果没有,则会按照作用域链的结构一级一级的查找,直到全局变量对象。foo()的作用域链中不包含bar()的变量对象,因此无法在foo()中访问到bar()的变量或函数,即外部无法访问内部变量或函数。

也可以结合js执行上下文的进栈,出栈来分析:

每调用一个函数就会为其创建一个执行上下文并压入执行环境栈栈顶,按照正常逻辑从上往下解析,foo()内部定义了bar()并且调用,bar()处于执行环境栈的栈顶,其次是foo(),因为js执行代码机制,在某一函数执行完成后会弹出环境栈,并且该执行上下文中的活动对象销毁,所以当bar()执行完成之后,活动对象已经销毁,所以bar()外部自然而然访问不到。

三,闭包

何为闭包?

可访问另一个函数作用域中变量的函数就叫做闭包,上述代码中bar()就是一个闭包,下面代码中匿名函数也是一个闭包。

    for(var i = 0; i < 10; i++) {
            setTimeout(function() {
                console.log(i);  
            }, 1000);
        }

有何用途?

可以在函数外访问函数内的变量,无需创建全局变量,防止变量污染;将外层函数的变量对象保存在它的作用域链中,当外层函数执行完毕,其变量依旧可被使用。

具体实现?

    function foo() {
       var num = 10;
           return function() {
               return num;
           }
     }
     var fn = foo();
     console.log(fn());    //10

foo()中匿名函数为一个闭包并返回,赋值给变量fn,执行fn得到结果为10。

你会发现num是foo()中的变量,存在于foo()的变量对象中,外部应该无法访问到,但是return出来赋值给了全局变量fn,foo()执行完后num依旧可以被使用,得到结果为10。

因为有些赶忙,后续会补充完整

猜你喜欢

转载自blog.csdn.net/yucihent/article/details/80530044