javascript中的闭包会在执行(求值)的时候从当前的运行上下文解析变量吗?

2013-07-13 15:32:58 +08:00
 pythonee
我是在这里看到这段代码的
http://msdn.microsoft.com/zh-cn/library/hh968324.aspx

var dofun = [];

for (var i = 0; i < 10; i++) {
dofun[i] = function() {
return i;
}

}

for (var i = 0; i < 10; i++) {
alert(dofun[i]());
}

会输出0到9,因为第二个for循环同样用了 i。但是按正常的理解,第一个for循环,变量 i 经过var的修饰,应该算局部变量了呀,不至于从第二个for循环的 i 中每次赋值啊,所以按理应该是全部输出10才对吧,所以我想问,因为闭包是延迟求值,返回闭包后,对它的求值,外界的同名变量会干扰吗?按我的理解是不会的,我觉得这里应该是有什么地方我遗漏了,所以请各位指点一下。
3100 次点击
所在节点    程序员
19 条回复
zztczcx
2013-07-13 15:56:51 +08:00
因为这里只有一个作用域就是 全局作用域。在第一个for循环里,闭包的作用域是全局的域(也就是运行这个函数的域),第二个for仍然是在同一个域中。
我看你上一个帖子,那个闭包的创建你用的就挺对的啊。
bakac
2013-07-13 15:57:57 +08:00
var i = 0;
for (i = 0; i < 10; i ++){
dofun[i] = function(){
return i;
}
}
console.log(i); // i = 10;
for (i = 0; i < 10; i++){
//i 0 -> 9
console.log(dofun[i]());
}
因为公用了 都引用了i ,而是第2个for又把i的值覆盖
pythonee
2013-07-13 17:47:18 +08:00
@zztczcx
@bakac

我认真读完那个帖子,我大概明白了,这个东西不仅涉及作用域,还有就是闭包的激活时机,也就是延迟求值的发生时间,而作用域的话,还是那些,先从最近的找,不过这里的重点是闭包的求值时机
Golevka
2013-07-14 01:23:59 +08:00
@pythonee 所以说javascript中for从句的scoping是一个和C++/C#/Java程序员直觉很不一样的设计————你在for(var i..中定义的i出了for之后还是可用的, 也就是说你在for里面定义的那个i是全局的.
chemzqm
2013-07-14 06:18:01 +08:00
pythonee
2013-07-14 13:25:31 +08:00
@Golevka

嗯,但是这里更多的是激活的时机,也就是说第一个for不会记忆当时的 i ,因为还没有被激活,所以在它调用的时候,也就是激活(求值)的时候,从第二个 for 循环中找到了 i
pythonee
2013-07-14 13:26:04 +08:00
@chemzqm
嗯,我不懂得时候,还是会先去那里看看,实在迷惑了,才会发帖求助
Golevka
2013-07-14 14:16:18 +08:00
@pythonee 人家ECMA-262 13.2明确规定的lexical scoping的东西怎么一下子变成求值时解析了? 你在global scope创建的function object, F.[[Scope]]自然引用的是全局作用域. 并且你也知道所有的inner function都共享同一个parent scope, 那么parent scope里变量的值变了自然会影响closure的求值结果.
zztczcx
2013-07-14 14:23:40 +08:00
@Golevka 这是因为js 没有块作用域
heroicYang
2013-07-14 14:38:46 +08:00
@Golevka 点赞,非常赞同!
clowwindy
2013-07-14 14:41:43 +08:00
JavaScript 是 function scoping + static scoping,变量名绑定哪个变量是编译时就确定的。
undozen
2013-07-14 14:56:45 +08:00
楼主不如看看我之前写过的文章?
http://zh.undozen.com/2013/10
switch
2013-07-14 16:22:05 +08:00
@Golevka +1
学 javascript 还是应该去看下 ECMAScript。
Golevka
2013-07-14 17:27:56 +08:00
我感觉LZ是被误导了. 刚才点开LZ贴出的链接, 于是我感觉微软的那个MVP碉堡了.

"函数是不是值得来锁定一个变量,是看该变量在调用这个函数的时候,是不是能在上下文作用域中找到这个变量,如果无法在调用时找到这个变量,内部函数就会锁住它,否则就不会锁住,至少表面上是这样的。"

我擦咧? 难道我计算一个函数的free variable时还要关心它所有的call site么? 还有和"创建"函数的时机相关的说明我都不知该从哪吐嘈了.
pythonee
2013-07-14 22:14:58 +08:00
@Golevka
那我不是深深被误导了,我靠,我也觉得有点毁三观的感觉

@clowwindy
你说的块作用域是指什么?我内部函数确实有作用域啊,我用var声明的变量也有啊


@clowwindy
什么叫编译期绑定,就拿这个例子来讲,第一个for循环 创建函数的时候,并没有绑定到每个 i,是运行期求出来的啊
pythonee
2013-07-14 22:31:07 +08:00
@Golevka
其实我觉得 那个mvp说的也没有问题的样子,它并不是说关心call site,是说在 ()或 return的时候会锁定变量,假如不是像他说的那样的话,按理来说,第一个for就会求值的话,那么这时候 闭包里的 i 都是10了,也就没有第二个for什么事了
Golevka
2013-07-14 23:42:24 +08:00
@pythonee .................................

ECMAScript中的closure"锁定"的不是变量的值, 而是environment; 并且"锁定"的时机也不是在()/return/求值时, 而是在"定义"时. 不信你翻一下262看看在遇到"FunctionDeclaration"这条产生式时编译器需要做什么动作? Entering Function Code时又要做什么动作? 顺便SICP的3.2 The Environment Model of Evaluation也可拿来作为参考, 因为那里描述的也是带upward的模型, 也即js-er所谓的scope chaining.
pythonee
2013-07-15 08:45:50 +08:00
@Golevka
哦,这下总算找到正统了,我之前还看了一篇 javascript执行上下文的文章,这里的环境应该就是指的上下文吧,上下文的建立应该是在定义时,赋值是在执行时。哈哈,非常感谢你的回答,我突然觉得javascript很多概念挺统一的啊,之前被它的变化多端弄得神魂颠倒
Mutoo
2013-07-15 09:45:26 +08:00
var dofun = [];
var i = 0; // global i
for (i = 0; i < 10; i++) {
(function() {
var that_i = i; // encloure_i
dofun[i] = function() {
return that_i;
}
})();
}
console.log(i); // i = 10;
for (i = 0; i < 10; i++) {
//i 0 -> 9
console.log(dofun[i]());
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/75650

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX