一个函数在声明时能够记住当前作用域、父函数作用域及父函数作用域上的变量和参数的引用,直至通过作用域链上全局作用域,基本上闭包是在声明函数时创建的作用域
1、简单例子
// 全局作用域
var globalVar = "abc";
function a(){
console.log(globalVar);
}
a(); // abc
在此示例中,当声明 a 函数时,全局作用域是 a 闭包的一部分
变量 globalVar 在图中没有值的原因是该变量的值可以根据调用函数a的位置和时间而改变。但是在上面的示例中,globalVar 变量的值为 abc
2、复杂例子
var globalVar = "global";
var outerVar = "outer"
function outerFunc(outerParam) {
function innerFunc(innerParam) {
console.log(globalVar, outerParam, innerParam);
}
return innerFunc;
}
const x = outerFunc(outerVar);
outerVar = "outer-2";
globalVar = "guess"
x("inner");//guess outer inner
(1)当调用 outerFunc 函数并将返回值 innerFunc 函数分配给变量 x 时,即使为 outerVar 变量分配了新值 outer-2,outerParam 也继续保留 outer 值,因为重新分配是在调用 outerFunc 之后发生的
(2)当调用 outerFunc 函数时,它会在作用域链中查找 outerVar 的值,此时的 outerVar 的值将为 "outer"
(3)当调用了 innerFunc 的 x 变量时,innerParam 将具有一个 inner 值,因为这是在调用中传递的值,而 globalVar 变量值为 guess,因为在调用 x 变量之前,我们将一个新值分配给 globalVar
3、错误例子
const arrFuncs = [];
for(var i = 0; i < 5; i++){
arrFuncs.push(function (){
return i;
});
}
console.log(i); // 5
for (let i = 0; i < arrFuncs.length; i++) {
console.log(arrFuncs[i]()); // 5个5
}
(1)var 关键字创建一个全局变量,当 push 一个函数时,这里返回的全局变量 i,在循环后,在该数组中调用其中一个函数时,它会打印5
(2)闭包在创建变量时,会保留该变量的引用而不是其值。可以使用 IIFES 或使用 let 来代替 var 的声明