闭包的定义
参考MDN关于闭包的定义:函数和其周围状态(lexical environment, 词法环境)的引用捆绑在一起构成闭包(closure)。
词法作用域
作用域是一个和语言无关的概念,总的来说就是代码中所用变量都有一个可用的范围,这个范围就可以叫做作用域。词法作用域,是指在词法分析阶段就确定了。
function outer() {
var a = '1';
function inner() {
console.log(a);
}
inner();
}
outer();
outer()函数里面有个局部变量a和一个inner的函数,inner是在内部创建的函数,虽然没有自己的变量a,但是可以访问到outer函数的变量a,因此可以在控制台中打印出1。
闭包
首先看下面的例子:
function outer() {
var a = '1';
function inner() {
console.log(a);
}
return inner;
}
outer();
与上一个例子不同的是,将内部函数inner当作返回值返回(在js中,函数作为一等公民,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回)。 一般来说对于一个函数,进入主线程开始执行,主线程会为该函数在内存中分配空间,一旦函数执行完成以后,函数出栈,其变量也会被回收机制销毁,但是对于闭包,outer函数执行完毕以后,作用域并没有随之销毁,因为inner维持着一个对它的词法作用域的引用。
闭包的用处
用闭包模拟私有方法
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var counter1 = makeCounter();
var counter2 = makeCounter();
console.log(counter1.value()); /* logs 0 */
counter1.increment();
counter1.increment();
console.log(counter1.value()); /* logs 2 */
counter1.decrement();
console.log(counter1.value()); /* logs 1 */
console.log(counter2.value()); /* logs 0 */
首先makeCounter提供了一个词法环境,其中有变量privateCounter和方法changeBy,为三个函数increment, decrement和value共享。 我们建立了两个独立的词法作用域couter1和couter2,每个闭包都是引用自己词法作用域中的变量,不会相互影响。
性能考量
如果不是特定任务需要使用闭包,在函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本有负面影响。比如在创建新的对象或者类时,方法通常关联于对象的原型,而不是对象的构造函数。