关于闭包有很多误解。 OP 是 词法作用域 的一个示例,即可以访问外部函数变量的“内部”函数。
有些人认为,ECMAScript 中的每个函数都与其外部执行上下文形成一个闭包,但是这个定义并不表示闭包更广泛和更强大的方面。
下面演示了“每个函数都创建一个闭包”的概念。
var x = 'global x';
function foo() {
console.log(x);
}
function bar() {
var x = 'local x';
foo();
}
bar(); // 'global x'
当 foo 创建时,它的范围被定义并且它可以在全局执行上下文中访问 x。当 bar 被调用时,它会创建一个新的执行上下文并调用 foo。由于 foo 的作用域链是基于它在代码中定义的位置而不是从它被调用的位置创建的,因此它将 x 解析为全局变量而不是全局变量bar 的本地。所以 foo 对包含 x (以及所有其他全局变量)的全局执行上下文有一个闭包。
这展示了 ECMAScript 的词法范围性质和“平凡”的闭包。它没有被利用或允许持续存在,所以它是否真的存在是推测。
闭包更显着的方面是利用它们在创建它们的执行上下文完成后访问局部变量的能力,例如
var foo = (function() {
var closureVariable = 'foo';
return function() {
console.log(closureVariable);
}
}());
foo(); // 'foo'
在上面,立即调用的函数表达式 (IIFE) 使用名为 closureVariable 的变量创建一个执行上下文,并为其分配一个值。然后它返回一个内部函数对象,该对象在其作用域链上具有此变量。
一旦 IIFE 返回,它的执行上下文仍然可供返回的函数访问(现在由 foo 引用)。正是这种对已完成执行上下文的持续访问允许闭包利用对某些“特权”函数“私有”的共享变量,例如
var bar = (function() {
var a;
return {
setA: function(value) {
a = value;
},
getA: function() {
return a;
}
}
}());
bar.setA(6);
console.log(bar.getA()); // 6
方法 setA 和 getA 对创建它们的执行上下文有一个闭包,因此共享对 a 的特权访问。正是这种对原本无法访问的变量的持续访问,才是闭包更重要的特征。
comp.lang.javascript 常见问题解答notes on Closures 有更详尽的解释。
还有 Douglas Crockford 的 Private Members in JavaScript。
编辑
上面的内容已经过修改,以合并 comp.lang.javasscript 上关于闭包的长时间讨论的特性:Confusion about closures。