工作中大量的使用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 );
}