在面试时经常会被问到闭包。那么什么是闭包呢?以及闭包有什么用处呢?
闭包是什么
首先我们要从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…