阅读 63

javascript之闭包

闭包:当内部函数被保存到外部时,将生成闭包。闭包会导致原有作用域链不会释放,造成内存泄漏。

内存泄漏:产生的闭包会占用内存,闭包越多,可用内存越少,从可用内存角度看,内存就像泄漏一样越来越少。

先看一段代码:

function add() {
    var num=0;
    function a(){
        console.log(++num);
    }
    return a;
}

var myAdd = add();
myAdd();//输出1
myAdd();//输出2

函数a()被保存到函数add()外的全局变量,这里就产生了闭包。

具体讲解得从作用域链讲起。

  1. 在执行add()方法前一刻,生成add()的AO
AO{
    num:undefind,
    myAdd:undefind
}
此时add()作用域链为 0--AO
                   1--GO
  1. 执行add()后AO发生改变
AO{
    num:0,
    myAdd:function a() {}
}
此时add()作用域链为 0--AO
                   1--GO
  1. 执行a()前一刻,生成a()的AO
AO{
    
}
此时a()作用域链为 0--a()自己的AO
                 1--add()的AO
                 2--GO
  1. 执行a()后a()会切断与自己创建出的AO的关联,但还会保留父级函数的AO
此时a()作用域链为0--add()的AO
                1--GO
  1. 程序执行 return a 后add()方法执行完成,切断与自己创建出的AO的关联,但a()并没有切断与其的关联,此时就产生了闭包,add()的AO没有被释放。
此时add()作用域链为0--GO

此时a()作用域链为0--add()的AO
                1--GO
  1. 程序执行 var myAdd = add(); 此时a()被全局变量myAdd接收
  2. 程序执行 myAdd()就等于执行a(),在执行a()创建出自己的AO
此时a()作用域链为 0--a()自己的AO
                 1--add()的AO
                 2--GO

执行a()

function a(){
        console.log(++num);
}

先从自己的AO中找num属性,没有,再去父级的add()中的AO找,此时找到num,值为0。

所以控制台输出1,并把num值更新为1

8.程序再次执行 myAdd()

function a(){
        console.log(++num);
}

还是先从自己的AO中找num属性,没有,再去父级的add()中的AO找,此时找到num,值为1。

所以控制台输出2,并把num值更新为2

下面再来一道面试中常见题

function test() {
    var arr=[];
    for(var i=0;i<10;i++){
        arr[i]=function () {
            console.log(i);
        }
    }
    return arr;
}
var myArr=test();
for (var j=0;j<10;j++){
        myArr[j]();
}

此时控制台输出的是10个10,并不是我们想象中的0~9,接下来我们就用上面的方法来解释这道题。

  1. 在执行test()方法前一刻,生成tset()的AO
AO{
    arr:undefind,
    i:undefind
}
此时test()作用域链为 0--AO
                   1--GO
  1. 执行test()后AO发生改变
AO{
    arr:[arr[i]:function () {
        console.log(i);//注意,此时并不会执行console.log(i),而是直接将这句赋值给对应的arr[i],因为这里并没有执行arr[i](),只是单纯的赋值表达式。
    }],
    i:i//此i为0~10,是真实的值,当i为9时,arr[9]=function() {console.log(i)},i自增为10,此时再进for循环,不满足条件,退出for循环
}

在for循环结束后,arr的值应为

arr[0]=function(){console.log(i)};
arr[1]=function(){console.log(i)};
arr[2]=function(){console.log(i)};
....
arr[8]=function(){console.log(i)};
arr[9]=function(){console.log(i)};
  1. 执行 return arr;
  2. 执行 var myArr=test(); 此时myArr就是数组arr[]
  3. 程序执行for循环中 myArr[j]();,此时myArr[]中的值就是第二步中arr[]的值。执行myArrj就是执行function(){console.log(i)},此时i从AO中查找,在test方法执行完时,i已经变为10,所以控制台会输出10个10。
文章分类
前端
文章标签