循环和闭包

工作中大量的使用javascript,但是却并理解闭包是什么?总感觉这门语言有其隐蔽的一面,如果能够掌握将会公里大增。

闭包是基于词法作用域书写代码时产生的自然结果,其实闭包在平常书写的js代码中随处可见。

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行

PS:我对其的理解是当一段词法作用域的代码执行之后,js引擎会对其进行回收,以便来提高性能,但是闭包阻止了这种回收,使这段词法作用域依旧处在内存当中,当你调用词法作用域中的局部变量时,依旧存在。

下面我们来看一段代码,清晰地展示了闭包:

function foo(){

    var a = 2;

    function bar(){

        console.log(a);

    }

    return bar;

}

var baz = foo();

baz(); //2  ----这就是闭包的效果

在 foo() 执行后,通常会期待 foo() 的整个内部作用域都被销毁,因为我们知道引擎有垃圾回收器用来释放不再使用的内存空间。由于看上去 foo() 的内容不会再被使用,所以很 自然地会考虑对其进行回收。 而闭包的“神奇”之处正是可以阻止这件事情的发生。事实上内部作用域依然存在,因此 没有被回收。

function wait(message) {

    setTimeout( function timer() {

        console.log( message );

    }, 1000 );

}

wait( "Hello, closure!" );

wait(..) 执行 1000 毫秒后,它的内部作用域并不会消失,timer 函数依然保有 wait(..) 作用域的闭包。

要说明闭包,for 循环是最常见的例子。

for (var i=1; i<=5; i++) {

     setTimeout( function timer() { console.log( i );

    }, i*1000 );

}

正常情况下,我们对这段代码行为的预期是分别输出数字 1~5,每秒一次,每次一个。

但实际上,这段代码在运行时会以每秒一次的频率输出五次 6。

事实上, 当定时器运行时即使每个迭代中执行的是 setTimeout(.., 0),所有的回调函数依然是在循 环结束后才会被执行,因此会每次输出一个 6 出来。

正确的做法是,我们要使用闭包,首先按照定义我们要给每次循环一个词法作用域,每个作用域都要有自己的i的值,如下:

for(var i =1; i<=5;i++){

    (function(i){ //给每次循环一个独立的词法作用域

        setTimeout(function(){

            console.log(i);

        },i*1000);

    })(i);

}

当然在ES6中,可以用let声明:

for (let i=1; i<=5; i++) {

     setTimeout( function timer() {

        console.log( i );

    }, i*1000 );

}

猜你喜欢

转载自my.oschina.net/u/3229305/blog/916910