闭包

117 阅读2分钟

闭包

JavaScript 中闭包无处不在,你只需要能够识别并拥抱它。

什么是闭包

function foo() {
    var a = 2;
    function bar() {
        console.log( a ); // 2
    }
    bar();
}
foo();
// 上面的并不算是闭包,只是闭包的一部分

function foo() {
    var a = 2;
    function bar() { 
        console.log( a );
    }
    return bar;
}
var baz = foo();
baz(); // 2 —— 朋友,这就是闭包的效果
  1. 上面的例子
    1. 执行完后,foo 的内部作用域被销毁
  2. 下面的例子
    1. 闭包的“神奇”之处,foo 的内部作用域都没有被销。

所以什么是闭包

函数在定义时的词法作用域以外的地方被调用。闭包使的函数可以继续访问定义时的作用域。

关键字

  • 定义时的词法作用域之外
  • 继续访问定义时的作用域

闭包的应用

  • 定时器
  • 事件监听器
  • Ajax 请求
  • 跨窗口通信
  • Web Workers

看个面试题

for (var i=1; i<=5; i++) {
    setTimeout( function timer() {
        console.log(i);
    }, i*1000)
}

答案是:5个6

为什么

首先是 6 是从哪里来的,这个循环的终止条件是 i 不再 <=5,条件成立时,i 等于 6。

第二个,延迟函数会等循环结束之后再执行,所以当延迟函数执行完时,i 已经等于 6。

解决方法:

  1. let,const
  2. 给每个 setTimeout 弄个单独的作用域,IIFE
for (var i=1; i<=5; i++) {
    (function(j) {
        setTimeout( function timer() {
            console.log(j);
        }, j*1000)
    })(i)
}

闭包的作用

模块化

function CoolModule() {
    var something = "cool";
    var another = [1, 2, 3];
    function doSomething() { 
        console.log( something );
    }
    function doAnother() {
        console.log( another.join( " ! " ) );
    }
    return {
        doSomething: doSomething, 
        doAnother: doAnother
    };
}
var foo = CoolModule(); 
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

小结

闭包是什么

函数在定义时的词法作用域以外的地方被调用。闭包使的函数可以继续访问定义时的作用域。

  1. 定义时的作用域之外调用
  2. 能够访问定义时的内部作用域