声明:以下内容摘自《你不知道的javascript》上卷一书的第5章的片段。
1.现在我懂了
作用域闭包-1中的代码片段有些死板,并且为了解释如何使用闭包而认为地在结构上进行了修饰。
现在让我们来搞懂这个事实。
function wait(message) {
setTimeout( function timer() {
console.log( message );
}, 1000);
}
wait( "Hello, closure!" );
将一个内部函数(名为timer)传递给setTimeout(..)。timer具有涵盖wait(..)作用域的闭包,因此还保有对变量message的引用。
wait(..) 执行1000毫秒后, 它的内部作用域并不会消失,timer函数依然保有wait(..)作用域的闭包。
在引擎内部,内置的工具函数setTimeout(..)持有对一个参数的引用,这个参数也许叫做fn或func,或类似的其他名字。引擎会调用这个函数,在例子中就是内部的timer函数,而词法作用域在这个过程中保持完整。
这就是闭包!
function setupBot(name, selector) {
$( selector ).click( function activator() {
console.log( "Activating: " + name );
});
}
setupBot( " Closure Bot 1", "#bot_1" );
setupBot( " Closure Bot 2", "#bot_2" );
如果将(访问它们各自词法作用域的)函数当做第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。
在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务重,只要使用了回调函数,实际上就在使用闭包!
IIFE模式
通常认为IIFE是典型的闭包例子。
var a = 2;
(function IIFE() {
console.log( a );
})();
虽然这段代码可以正常工作,但严格来讲它并不是闭包。
为什么?因为函数(示例代码中的IIFE)并不是在它本身的词法作用域以外执行的。它在定义时所在的作用域中执行(而外部作用域,即全局作用域也持有a)。a是通过普通的词法作用域查找而非闭包被发现的。
尽管IIFE本身并不是观察闭包的恰当例子,但它的确创建了闭包,并且也是最常用来创建可以被封闭起来的闭包的工具。
因此IIFE的确同作用域息息相关,即使本身并不会真的创建作用域。