闭包
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”。
var scope = "global scope";
function checkScope(name) {
var scope = "local scope";
var fun = function () {
console.log(`name: ${name}`);
console.log(`scope: ${scope}`);
}
return fun;
}
checkScope('cez')(); // name: cez
// scope: local scope
在这个例子中,我们在外部函数
checkScope
中又定义了函数fun
,并且,内部函数fun
可以引用外部函数checkScope
的参数(name
)和局部变量(scope
),当外部函数checkScope
返回内部函数fun
时,相关参数的变量都保存在内部函数中,这种特性称为闭包。
不同外层函数中的内部函数不共享作用域链(也就是局部变量互不干扰)。
function constfunc(i) { return function() { return i; }; }
var funs = [];
for (var i = 0; i < 10; i++) { // 此循环在外层函数外执行
funcs[i] = constfunc(i); // 每循环一次都执行一次外层函数,导致每次都会创建一个新的作用域链
}
funs[0](); // 0
funs[5](); // 5
funs[9](); // 9
当
for
循环执行完毕之后,数组funs
存放着10个函数function() { return i; }
。因为每次调用函数constfunc()
都会创建一个新的作用域链,所以数组中10个函数的i
是互相独立的,都是指向自己当前作用域链中的i
。也就是说,循环变量会绑定到对应的函数,无论该循环变量后续如何更改,都不会影响到已绑定到函数的i
。
一个外层函数中所有内部函数都共享作用域链(也就是共享局部变量)。
function counter() {
var funs = [];
for (var i = 0; i < 10; i++) { // 此循环是在外层函数内执行
funs[i] = function() { return i; }; // 每循环一次创建一个内部函数
}
return funs;
}
var arr = counter();
arr[0](); // 10
arr[5](); // 10
arr[9](); // 10
首先执行
var arr= counter();
,函数counter()
被调用,依次执行函数体内的代码逻辑,执行到for
循环时,将一个个函数function() { return i; }
添加到数组中,但是这些函数并没有被调用。当for
循环结束时, 此时i = 10
,数组funs
存放着 10 个函数,也就是创建了10个闭包,这些闭包都是在同一个函数调用中定义的,因此它们可以共享变量i
。因为闭包特点,内部函数
function() { return i; }
引用的i
是外部函数counter()
的局部变量,也就是for
循环里面的i
,并且所有内部函数都共享变量i
,所以数组中10个函数function() { return i; }
中的i
都是指向同一个i
。如上段分析,循环结束时i = 10
,所以,不管调用数组arr
中的哪个函数都是返回 10。
再看一个例子:
function counter() {
var n = 0;
return {
count: function() { return n++; },
reset: function() { n = 0; }
};
}
var c = counter(), d = counter(); // 创建两个计数器
c.count(); // 0
d.count(); // 0 => 它们互不干扰
c.reset(); // reset() 和 count() 方法共享局部变量
c.count(); // 0 => 因为上一步重置了 c
d.count(); // 1 => 而没有重置 d
counter()
函数返回一个“计数器”对象,这个对象包含两个方法,这两个方法都可以访问局部变量n
,并且是共享局部变量,当其中一个方法改变n
的值时,就会影响到另一个方法。每次调用
counter()
都会创建一个新的作用域链和一个新的局部变量。因此,如果调用counter()
两次,则会得到两个计数器对象,而且彼此包含不同的作用域和局部变量,调用其中一个计数器对象的count()
或reset()
不会影响到另外一个对象。