JavaScript基础之闭包

157 阅读2分钟

在面试时经常会被问到闭包。那么什么是闭包呢?以及闭包有什么用处呢?

闭包是什么

首先我们要从JavaScript的作用域说起。在es5中,JavaScript中只有函数级作用域和全局作用域。多个作用域嵌套形成作用域链接。

请看示例:

function init() {
    // init函数作用域
    var name = "hi"; 
    function displayName() { 
        // displayName函数作用域
        var person = 'you'
        alert(name+person); 
    }
    displayName();
}
// 全局作用域
init();

在displayName函数内部查找变量时,会按照最近的displayName作用域到init作用域直至全局作用域的顺序向上查找。

编程语言的作用域工作模型一般有两种,1,词法作用域2,动态作用域。

词法作用域是无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。

而动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调 用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。

JavaScript使用词法作用域进行变量的查找。

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

闭包的应用示例

如何可以依次打印出1-5?

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

失败。以上代码执行过后并不能依次打印出1-5。因为timer里访问的是全局变量i,在循环执行完毕后,全局变量i已经变成了6.

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

成功!每个匿名梓执行函数都有一个内部变量i,保存了对当时函数执行时i的引用,从而不会打印循环执行完毕后的i的值。

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

失败。在每次循环开始时用let声明的变量j的值都会被下一轮循环的新的i值覆盖。

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

成功!i被锁定在for循环的局部作用域中,timer每次访问的都是当时的作用域中的i。

参考资料

《你不知道的JavaScript》
es6.ruanyifeng.com/#docs/let#l…