闭包是基于词法作用域书写代码时所产生的自然结果。即使你还不熟悉闭包的概念,在你之前的编程经历中你也可能已经创建了很多的闭包,所以我们现在要做的,是理解并掌握它。
在解释闭包之前,我们需要了解一些关于作用域链的细节。当某个函数被调用时,会创建一个执行环境及相应的作用域链,作用域链本质是一个指向变量对象的指针列表,可以通过作用域链来引用外层的变量。
function foo() {
var a = 2;
function bar() {
console.log(a)
}
return bar;
}
var baz = foo();
baz(); // 2
上述的代码中,函数bar()具有一个涵盖foo()作用域的闭包。
一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。而闭包的神奇之处正是可以阻止这件事情的发生,foo()的作用域依然存在,因为bar()的作用域链依然在引用着foo()的变量。
bar()依然持有对该作用域的引用,而这个引用就叫做闭包。
无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都可以观察到闭包。
回调函数
function wait(message) {
setTimeout( function timer() {
console.log(message);
}, 1000);
}
wait("Hello, closure!");
将一个内部函数(timer)传递给setTimeout()。timer具有涵盖wait()作用域的闭包,因此还保有对变量message的引用。
在定时器、事件监听、Ajax请求、跨窗口通信、Web Workers或者任何其他的任务中,只要使用了回调函数,实际上就是在使用闭包。