前面的代码段有些学术化,而且是人工构建来说明 闭包的使用 的。但我保证过给你的东西不止是一个新的酷玩具。我保证过闭包是在你的现存代码中无处不在的东西。现在让我们 看看 真相。

    1. function wait(message) {
    2. setTimeout( function timer(){
    3. console.log( message );
    4. }, 1000 );
    5. }
    6. wait( "Hello, closure!" );

    我们拿来一个内部函数(名为 timer)将它传递给 setTimeout(..)。但是 timer 拥有覆盖 wait(..) 的作用域的闭包,实际上保持并使用着对变量 message 的引用。

    在我们执行 wait(..) 一千毫秒之后,要不是内部函数 timer 依然拥有覆盖着 wait() 内部作用域的闭包,它早就会消失了。

    引擎 的内脏深处,内建的工具 setTimeout(..) 拥有一些参数的引用,可能称为 fn 或者 func 或者其他诸如此类的东西。引擎 去调用这个函数,它调用我们的内部 timer 函数,而词法作用域依然完好无损。

    闭包。

    或者,如果你信仰jQuery(或者就此而言,其他的任何JS框架):

    1. function setupBot(name,selector) {
    2. $( selector ).click( function activator(){
    3. console.log( "Activating: " + name );
    4. } );
    5. }
    6. setupBot( "Closure Bot 1", "#bot_1" );
    7. setupBot( "Closure Bot 2", "#bot_2" );

    我不确定你写的是什么代码,但我通常写一些代码来负责控制全球的闭包无人机军团,所以这完全是真实的!

    把玩笑放在一边,实质上 无论何时何地 只要你将函数作为头等的值看待并将它们传来传去的话,你就可能看到这些函数行使闭包。计时器、事件处理器、Ajax请求、跨窗口消息、web worker、或者任何其他的异步(或同步!)任务,当你传入一个 回调函数,你就在它周围悬挂了一些闭包!

    注意: 第三章介绍了 IIFE 模式。虽然人们常说 IIFE(独自)是一个可以观察到闭包的例子,但是根据我们上面的定义,我有些不同意。

    1. var a = 2;
    2. (function IIFE(){
    3. console.log( a );
    4. })();

    这段代码“好用”,但严格来说它不是在观察闭包。为什么?因为这个函数(就是我们这里命名为“IIFE”的那个)没有在它的词法作用域之外执行。它仍然在它被声明的相同作用域中(那个同时持有 a 的外围/全局作用域)被调用。a 是通过普通的词法作用域查询找到的,不是通过真正的闭包。

    虽说技术上闭包可能发生在声明时,但它 不是 严格地可以观察到的,因此,就像人们说的,它是一颗在森林中倒掉的树,但周围没人去听到它

    虽然 IIFE 本身 不是一个闭包的例子,但是它绝对创建了作用域,而且它是我们用来创建可以被闭包的作用域的最常见工具之一。所以 IIFE 确实与闭包有强烈的关联,即便它们本身不行使闭包。

    亲爱的读者,现在把这本书放下。我有一个任务给你。去打开一些你最近的 JavaScript 代码。寻找那些被你作为值的函数,并识别你已经在那里使用了闭包,而你以前甚至可能不知道它。

    我会等你。

    现在……你看到了!