JS 作用域-闭包

这是概念:

运行期上下文:
函数执行的时候,会创建一个称为执行期上下文的内部对象。(类似于我们在预编译中所讲的AO对象)一个执行期上下文定义一个函数执行时的环境。函数每次执行时对应的执行期上下文 都是独一无二的,所以多次调用同一个函数会导致产生多个执行期上下问,当函数执行完毕,它所产生的执行期上下文将被销毁。

作用域:
[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。

作用域链:
[[scope]]中存储的集合呈链式链接,这种链式链接被称为作用域链。

其实 [[scope]]中存储的是作用域链,作用于域由多个执行期上下文链接起来
在那个查找变量:从这个函数作用域链的顶端依次向下查找。


认识作用域:

那我们用一个例子来说起作用域吧

function a(){
    function b(){
        var b=234;
    }
    var a=123;
    b();
}
var glob=100;
a();

作用域的分析:(注:所有的AO 都是同一个AO)

//a 的作用域
a defined a.[[scope]]  -->0:GO{}  //a 被定义的时候
a doing   a.[[scope]]  -->0:AO{}  //a 被执行的时候 
                       -->1:GO{}

//a 执行产生了 b 的定义(b在a中产生,保存a的成果)
b defined b.[[scope]]  -->0:a AO{}   //b 被定义的时候
                       -->1:GO{}
b doing    b.[[scope]] -->0:b AO{}  //b 被执行的时候
                       -->1:a AO{}
                       -->2:GO{}

来看一下图解:
这里写图片描述
这里写图片描述
这里写图片描述
当b 函数执行完毕后 他会销毁自己之前的执行期上下文,即b 自己的AO 指向线被切断了。
这里写图片描述
再次b的执行完毕伴随a 函数的执行完毕
与b相同 a 自己的AO 指向线被切断了。至此所有的执行完毕

再用一个例子我们来分析一下吧:

function a(){
    function b(){
        function c(){
        }
        c();  //c 的调用语句
    }
    b();  //b 执行才能导致 c定义
}
a();  //a 执行才能导致 b定义

// a defined a.[[scope]]  -->0:GO
// a doing   a.[[scope]]  -->0:a AO
                          -->1:GO

// b defined b.[[scope]]  -->0:a AO
                          -->1:GO
// b doing   b.[[scope]]  -->0:b AO
                          -->1:b AO
                          -->2:GO

// c defined c.[[scope]]  -->0:b AO
                          -->1:a AO
                          -->2:GO

// c doing   c.[[scope]]  -->0:c AO
                          -->2:b AO
                          -->3:a AO
                          -->4:GO
  • 所有的 a AO 都是同一个。(b,c 也一样)

一个闭包过程!

我们来用一个例子引出闭包吧:

function a(){
    function b(){
        var bbb=234;
        console.log(aaa);  //结果:123
    }
    var aaa=123;
    return b;
}
var glob =100;
var demo =a();
demo();

我们来分析一下:
1) var demo =a(); a 执行,产生自己的作用域链AO GO。

2)a的执行伴随 b的定义(b 函数仅仅定义,并没有被执行),b在a中定义 所以它的作用域链与a 相同。

扫描二维码关注公众号,回复: 3208489 查看本文章

3)return b;将 b的结果保存在demo 中,至此a 执行完毕 ,a 与AO的连线被剪断。

4)demo(); 执行b 此时b仍保存着 a 的AO。(a 只是单方面切断了自己的链接)

现在问题来了,a执行完毕剪断了自己与自己AO的连线,就算b 执行完自己切断了自己的AO(b 的AO)连线,但是return b;demo 将b函数保存了下来,这个函数任然存在,a 的AO 的连线就一直没有断。除非将 (demo=null;) 这个a AO对象才会被销毁。——这就是闭包
这里写图片描述
这里写图片描述
总结一下:当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域不释放,造成内存泄漏。

闭包有什么作用呢?
1)实现公有变量
2)可以做缓存(存储结构)
3)可以实现封装,属性私有
4)模块化开发,防止污染全局变量


神奇的闭包:

function  text(){
    var arr=[];
    for(var i=0;i<10;i++)
    {
        arr[i]=function(){
            document.write(i+' ');
        }
    }
    return arr;
}
var myArr=text();
for(var j=0;j<10;j++){
    myArr[j]();
}

输出结果为 10个10
这个结果和你想的一样吗?
这里写图片描述
来 我们来分析一下:

function  text(){  //text 函数与 其内部函数形成闭包
    var arr=[];
    //var i=0;
    for(var i=0;i<10;i++)  //i 变量也在text 中产生,相当在上面声明 i
    {
        arr[i]=function(){   //(函数没有执行 仅仅是引用,相当于把函数折叠起来,系统暂时没有执行函数内部的语句)
            document.write(i+' ');
        }
    }
    return arr;  //返回数组,数组中存储着 未被执行的十个函数
}
var myArr=text();
for(var j=0;j<10;j++){
    myArr[j]();  //因为实现闭包,内部函数所共享的 i是在text中产生的,就是i存储在text AO中。
                 //这十个函数都连着这个同一个text AO,所有他们的 i为同一个 i。
}

现在你懂了这个结果的原因,那如何解决这个问题呢?

解决闭包:

//立即执行函数:创建一个独立的作用域。
//这个作用域里面的变量,外面访问不到(即避免「变量污染」)。

function  text(){
    var arr=[];
    for(var i=0;i<10;i++)
    {
        (function(j){   //立即执行函数读到马上执行
            arr[j]=function(){ //里面的函数仍不被执行 因为不管立即函数执行不执行里面的函数都只是一条函数声明语句
                document.write(j+' ');
            }
        }(i));  //将实参i 的值赋给j,此后j的值一直不变
    }
    return arr;
}
var myArr=text();
for(var j=0;j<10;j++){
    myArr[j]();
}

猜你喜欢

转载自blog.csdn.net/qq_41853863/article/details/81275727