let-var 作用域链问题
发布于 9 年前 作者 magicdawn 7314 次预览 最后一次回复是 9 年前 来自 问答
之前看过汤姆大叔的博客翻译的js the core
- 变量对象 http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html
- 闭包 http://www.cnblogs.com/TomXu/archive/2012/01/31/2330252.html
- 原文 http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
对for循环中出现的问题可以做出解释
var data = [];
for (var k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
}
为什么直接data[k] = function(){ alert(k) }这样做k总是3, 因为闭包 data[0] data[1] data[2]的 [[scope]]属性是一样的, 他们是在同一个上下文中创建出的闭包, 他们
- data[0].scope = [ data0Context.VO, globalContextVO ]
- data[1].scope = [ data1Context.VO, globalContextVO ]
- data[2].scope = [ data2Context.VO, globalContext.VO ] 可以看到除了第一个是它们自己的VO外, 后面都是一样的, 他们引用的k不存在于他们自己的VO对象中, 由于外面k修改, 他们都跟着变了.
为什么加一层自执行函数可以.
for (var k = 0; k < 3; k++) {
(function temp(k){
data[k] = function () {
alert(k);
};
})(k);
}
此时他们的scope属性如下
- data[0].scope = [ data0Context.VO, tempContext.VO={ k:0}, globalContext.VO ]
- data[1].scope = [ data1Context.VO, tempContext.VO={ k:1}, globalContext.VO ]
- data[2].scope = [ data2Context.VO, tempContext.VO={ k:2}, globalContext.VO ] 可以看到他们的scope chain中引用到k的地方是没有重合的, 后修改for循环中的k是不会影响到他们scope chain中的k值的.
问题来了 http://dmitrysoshnikov.com/ecmascript/javascript-the-core/comment-page-3/#comment-173907
var data = [];
for (let k = 0; k < 3; k++) {
data[k] = function () {
alert(k);
};
}
data[0](); // 0
data[1](); // 1
data[2](); // 2
阮一峰对ES6 let类似的问题有个解释, 块级作用域, 这个我也知道, 但解决不了我的问题 http://es6.ruanyifeng.com/#docs/let
我的想法是:
- data[0].scope = [ data0Context.VO, blockContext.VO={ k }, globalContext.VO ]
- data[1].scope = [ data1Context.VO, blockContext.VO={ k }, globalContext.VO ]
- data[2].scope = [ data2Context.VO, blockContext.VO={ k }, globalContext.VO ]
k存在于 let-for的块级作用域中, 而且是公共的, 就是引用的同一个k, 结果应该也是与 var 相同, 但是测试结果不是这样的, 遂求解答?
15 回复
若有所思… 此时的blockContext 应该与 前面的 tempContext 一样, 不重合~
看不懂
看了下原文说的Evaluation context部分, 应该是for循环每次都会创建, blockContext中的k 值每次都不一样, 就起到了跟var+自执行函数一样的效果
简单地说, 如果
for括号里面有let或者const, 那么for的body每次执行的时候都会新建一个词法环境, 并且把let和const的声明的变量名和值绑定到这个新的词法环境, 所以有上面的结果.不是很明白,for let应该给每个循环一个单独的作用域,虽然没看过标准,但感觉标准应该做符合直觉的事情。来填坑了无数人的坑。
另外,现在各种环境的es6支持称不上完美。for of基本在任何平台对异步异常处理都不太对劲。最好babel转下……
然而最近好像碰到了个babel的错误。。。
@William17
yeah, 类似用var加一个自执行函数每次都创建一个作用域…
@reverland
怎么说?
我问JavaScript the core的作者这个问题有回复了 http://dmitrysoshnikov.com/ecmascript/javascript-the-core/comment-page-3/#comment-173985
@magicdawn 建一个函数的话,会进入一个新的执行上下文。 而含有词法声明的for只会新建一个词法环境对象。
@William17
一个是 Lexical Environment http://www.ecma-international.org/ecma-262/6.0/index.html#sec-lexical-environments 一个是 Execution Context http://www.ecma-international.org/ecma-262/6.0/index.html#sec-execution-contexts
Soga~
@magicdawn 指这个例子,不知道表述有什么问题没。只是想说珍爱生命,慎用原生es6。
参见:https://hacks.mozilla.org/2015/07/es6-in-depth-generators-continued/
这个例子,我这里node v5.0.0
firefox for ubuntu 43.0.4
babel转换后的
@reverland
这个不就是在用这个 iteration protocol么, 为啥没有调用 return? 发现node v4的generator没有return… 怎么回事
@magicdawn 不知道怎么回事。。。
哈哈,不明觉厉…
@wangchi …