js关于for循环中的闭包问题?
程序员文章站
2022-03-30 12:01:50
...
for(var i=0,arr=[];i arr.push(function(){alert(i)});
}
arr[0](); // ?? 结果不是0
arr[1](); // ?? 全是4
改装后
for(var i=0,arr=[];i arr.push(
(function(i){
return function(){
alert(i);
}
})(i)
);
}
使用闭包可以解决了,为什么第一次代码中的i读取的一直是I变量的最后的结果呢?
那个大神能给分析一下第一段代码的执行的具体步骤呢?
至于什么js缺陷、bug之类的论调大可歇歇了(至少这个问题不算是bug)。开惯了自动挡的人,非要嘲笑手动挡的车离合油门配合不当容易熄火,有意思么?人家原理就是这样的,至少作为前端语言还是不错的。非要拉出去和工业语言一较高下,算我没说。
这里的 i 是个引用,你只要明白了这点就没啥难理解的。
每当一个function被call的时候,会有一个object产生(叫activation object),这个object会包括这个被call的function里定义的变量和arguments,以及它们的值。Global scope的变量由global object来保存。当function尝试读取一个变量的时候,它会先从最近的一个这种object来读(就像prototyping,但注意这个近取决于function的定义在哪里,而不是被执行的地方),读到了就不继续了,反之会读上一级context的这个object,直到global context。当某一个function结束执行时,跟它相连的这个object会被取消reference(然后就会被GC清理)。也就是说假如你有以下的code:
抛开有关大家常说的执行环境作用域链变量引用的解释,我们还可以从一些执行步骤乃至语义的角度来分析解决题主提到的问题。
for循环的循环体不是一个作用域
你的第一段代码等效于以下展开
}
arr[0](); // ?? 结果不是0
arr[1](); // ?? 全是4
改装后
for(var i=0,arr=[];i arr.push(
(function(i){
return function(){
alert(i);
}
})(i)
);
}
使用闭包可以解决了,为什么第一次代码中的i读取的一直是I变量的最后的结果呢?
那个大神能给分析一下第一段代码的执行的具体步骤呢?
回复内容:
来答一发,欢迎大神斧正!这个问题用js的 预解释+作用域+闭包 就能够解释得通,为了方便理解,我下面用两个图来说明一下:- 这是修改之前的运行过程:
图例:绿框为函数执行的栈内存,粉色为堆内存,0XAA为内存地址(存储着函数的字符串,并不会执行,当函数被调用时才会拿出来执行),沿着主流程从上到下看来看在修改之前的原始版分中,arr中每一项执行时,都会去上级作用域寻找i,而i在for循环执行结束后就已经变成了4,所以arr中每一项执行的结果都是一样的。函数在预解释阶段,都被当成字符串存入堆内存,在真正执行时,才会被拿出来执行,数组中存储的,其实只是指向这个堆内存的指针,i并没有传进去,执行的时候i才被传进去。
- 这是修改之后的运行过程:
至于什么js缺陷、bug之类的论调大可歇歇了(至少这个问题不算是bug)。开惯了自动挡的人,非要嘲笑手动挡的车离合油门配合不当容易熄火,有意思么?人家原理就是这样的,至少作为前端语言还是不错的。非要拉出去和工业语言一较高下,算我没说。
这里的 i 是个引用,你只要明白了这点就没啥难理解的。
var arr = [], i;
for (i = 0; i 3; i++) {
arr.push(function() {
console.log(i);
});
}
console.log(i) // 3
arr[0](); // 3
i = 5;
arr[0](); // 5
理解这个问题的关键是execution context,简单说就是当一个function被call的时候,它能“看到”哪些地方的变量名。为了简化问题你暂时可以考虑execution context只由以下两种组成:1)Global scope也就是全局的变量,2)每个function被call的时候在这个function里面定义的变量。每当一个function被call的时候,会有一个object产生(叫activation object),这个object会包括这个被call的function里定义的变量和arguments,以及它们的值。Global scope的变量由global object来保存。当function尝试读取一个变量的时候,它会先从最近的一个这种object来读(就像prototyping,但注意这个近取决于function的定义在哪里,而不是被执行的地方),读到了就不继续了,反之会读上一级context的这个object,直到global context。当某一个function结束执行时,跟它相连的这个object会被取消reference(然后就会被GC清理)。也就是说假如你有以下的code:
var globalVar = 0;
var outerVar;
var innerVar;
console.log(globalVar);
console.log(outerVar);
console.log(innerVar);
function outer() {
function inner() {
var innerVar = 2;
console.log(globalVar);
console.log(outerVar);
console.log(innerVar);
}
var outerVar = 1;
console.log(globalVar);
console.log(outerVar);
console.log(innerVar);
inner();
}
outer();
关于本题:抛开有关大家常说的执行环境作用域链变量引用的解释,我们还可以从一些执行步骤乃至语义的角度来分析解决题主提到的问题。
for(var i=0,arr=[];i
for (var i = 0, arr = []; i 3; i++) {
arr.push(function () {
alert(j)
});
}
因为js没有块级作用域for循环的循环体不是一个作用域
你的第一段代码等效于以下展开
var arr=[];
var i=0;
arr.push(function(){console.log(i);});
i++;
arr.push(function(){console.log(i);});
i++;
arr.push(function(){console.log(i);});
i++;
arr.push(function(){console.log(i);});
i++;
arr[0]();
arr[3]();
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
相关文章
相关视频
专题推荐
-
独孤九贱-php全栈开发教程
全栈 170W+
主讲:Peter-Zhu 轻松幽默、简短易学,非常适合PHP学习入门
-
玉女心经-web前端开发教程
入门 80W+
主讲:灭绝师太 由浅入深、明快简洁,非常适合前端学习入门
-
天龙八部-实战开发教程
实战 120W+
主讲:西门大官人 思路清晰、严谨规范,适合有一定web编程基础学习
网友评论
文明上网理性发言,请遵守 新闻评论服务协议
我要评论