之前看过汤姆大叔的博客翻译的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 相同, 但是测试结果不是这样的, 遂求解答?
简单地说, 如果for
括号里面有let
或者const
, 那么for
的body
每次执行的时候都会新建一个词法环境, 并且把let
和const
的声明的变量名和值绑定到这个新的词法环境, 所以有上面的结果.
不是很明白,for let应该给每个循环一个单独的作用域,虽然没看过标准,但感觉标准应该做符合直觉的事情。来填坑了无数人的坑。
另外,现在各种环境的es6支持称不上完美。for of基本在任何平台对异步异常处理都不太对劲。最好babel转下……
然而最近好像碰到了个babel的错误。。。
我问JavaScript the core的作者这个问题有回复了 http://dmitrysoshnikov.com/ecmascript/javascript-the-core/comment-page-3/#comment-173985
Per ES6 let is a block-scoped. So every time we enter the block of the for loop, a new environment is created, and the function created inside the block, > is created relatively to this environment.
Roughly it’s similar to (although at engine level much more optimized than this):
var data = [];
for (var k = 0; k < 3; k++) {
(function(x) {
data[x] = function () {
alert(x);
};
})(k);
}
一个是 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/
function setup() {
console.log("setup")
}
function cleanup() {
console.log("cleanup")
}
function work(value) {
console.log(value)
}
function* produceValues() {
setup();
try {
yield "work1";
yield "work2";
// ... yield some values ...
} finally {
cleanup();
}
}
for (var value of produceValues()) {
work(value);
break;
}
这个例子,我这里node v5.0.0
➜ test node forof.js
setup
work1
firefox for ubuntu 43.0.4
setup
work1
babel转换后的
➜ test cat forof-es5.js
"use strict";
require("babel-polyfill");
var _marked = [produceValues].map(regeneratorRuntime.mark);
function setup() {
console.log("setup");
}
function cleanup() {
console.log("cleanup");
}
function work(value) {
console.log(value);
}
function produceValues() {
return regeneratorRuntime.wrap(function produceValues$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
setup();
_context.prev = 1;
_context.next = 4;
return "work1";
case 4:
_context.next = 6;
return "work2";
case 6:
_context.prev = 6;
cleanup();
return _context.finish(6);
case 9:
case "end":
return _context.stop();
}
}
}, _marked[0], this, [[1,, 6, 9]]);
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = produceValues()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var value = _step.value;
work(value);
break;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
➜ test node forof-es5.js
setup
work1
cleanup
Note that the .return() is not called automatically by the language in all contexts, only in cases where the language uses the iteration protocol. So it is possible for a generator to be garbage collected without ever running its finally block.
这个不就是在用这个 iteration protocol么, 为啥没有调用 return? 发现node v4的generator没有return… 怎么回事