闭包深入理解
闭包指的是:能够访问另一个函数作用域中变量的函数
。
清晰的讲:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量
function f(x=0){
var count=x;
function getCount(){
return count++;
}
return getCount
}
var func = f(10);
console.log(func()); // 10
代码如下
let f = ()=>{
let func = []
for(var i = 0; i < 3; i++){
func.push(()=>{
return i*i;
})
}
return func
}
这样,f1应该输出0, f2输出1,f3输出4 ……,本应该这样才对。但是结果却是三个9。
let [f1,f2,f3]=f();
console.log(f1()); // 9
console.log(f2()); // 9
console.log(f3()); // 9
原因分析
道理很简单,因为使用了var声明的变量。var声明的变量是没有块级作用域的,上面的代码在逻辑上,等价于:
let f = ()=>{
let func = []
var i
for(i = 0; i < 3; i++){
func.push(()=>{
return i*i;
})
}
return func
}
所以解析顺序就是:
1,因为 func[0] / func[1] / func[2]三个闭包函数的外部词法环境都是循环体的词法环境和函数f的词法环境:
2,在查询标识符i的过程中, 在循环体的词法环境中未找到标识符i,所以将使用函数f的词法环境中的标识符i。
3,调用f1 / f2 / f3时,循环体已经结束, 所以 标识符 i最终值为3。所以最终皆返回 9
我们知道,导致最终结果都相同的原因是f1/f2/f3在解析标识符i的过程中使用的都是 函数词法环境 的标识符i
解决方法
方法一 在返回的函数外部创建一个立即执行函数表达式,在这个函数中定义一个新的标识符x
let f = ()=>{
let func = []
for(var i = 0; i < 3; i++){
func.push(
(function(){
var x=i;
return ()=>x*x;
})()
)
}
return func
}
let [f1,f2,f3]=f();
console.log(f1()); // 0
console.log(f2()); //1
console.log(f3()); //4
方法二 在循环体内创建let变量来接收值,使用块作用域
let f = ()=>{
let func = []
for(var i = 0; i < 3; i++){
let x = i; //let 定义变量,接收即将要返回的值
func.push(()=>{
return x*x;
})
}
return func
}
let [f1,f2,f3]=f();
console.log(f1()); // 0
console.log(f2()); //1
console.log(f3()); //4
方法三 循环体定义i时,用let定义,使用块作用域(推荐!推荐!推荐!)
let f = ()=>{
let func = []
for(let i = 0; i < 3; i++){
func.push(()=>{
return i*i;
})
}
return func
}
let [f1,f2,f3]=f();
console.log(f1()); // 0
console.log(f2()); //1
console.log(f3()); //4
有个经典面试题,是这样的:
for(var i =0;i<6;i++){
setTimeout(()=>{console.log(i)},0) //输出666666
}
方法一个 利用setTimeout第三个参数这样改下
for(var i =0;i<6;i++){
setTimeout((j)=>{console.log(j)},0,i); //正常输出 0 1 2 3 4 5
}
它是正常输出的, 因为setTimeout接收了当前i的值作为参数后,在函数内部将i参数传递给延时执行的函数
参考:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
方法二 使用let ,块作用域
for(let i =0;i<6;i++){
setTimeout(()=>{console.log(i)},0);
}
方法三 在循环体内创建let变量来接收值,使用块作用域
for(var i =0;i<6;i++){
let j = i;
setTimeout(()=>{console.log(j)},0);
}
版权声明:本文为CSDN博主「krfwill」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/krfwill/article/details/106290329