army8735 / jsdc

Javascript Downcast (es6 to es5)
http://army8735.me/jsdc
93 stars 12 forks source link

for of简化为for in,无需迭代器 #26

Open army8735 opened 9 years ago

army8735 commented 9 years ago

@hax 有可能有陷阱吗

hax commented 9 years ago

我没懂意思。你是要改哪里的代码?

先可大概说下差异。 for-of走迭代器,因为每次要产生object形如{value:x, done:false},对于当前浏览器来说略有性能开销。 for-in是遍历key,要上溯prototype,且要把所有key按照特定规则排序(先数字排序后其他按原始顺序),其实也慢得很,说不定比for-of还慢。 要快,目前还是用for/while循环。另外数组上的forEach之类的方法似乎现在浏览器优化得不错,跟直接循环的性能相当。

army8735 commented 9 years ago

目前for of是翻译成了迭代器循环来实现:

for(a of b) {
}
for(a =b.next();!a.done;a=b.next()) {a=a.value;
}

但其实翻译成for in循环更简单:

for(a in b) {a=b[a]
}

可省略迭代器的shim实现。

但怕有哪里不支持什么的。

hax commented 9 years ago

这个翻译有问题。必须是

_it = b[Symbol.iterator]()
while(!(_x = _it.next()).done) { a = _x.value }

翻译成 for-in 应该是不对的。

army8735 commented 9 years ago

Symbol.iterator?

hax commented 9 years ago

见这里:https://people.mozilla.org/~jorendorff/es6-draft.html#sec-well-known-symbols

Specification Name [[Description]] Value and Purpose
@@iterator "Symbol.iterator" A method that returns the default Iterator for an object. Called by the semantics of the for-of statement.
army8735 commented 9 years ago

thx

hax commented 9 years ago

当然,如果是老浏览器就需要runtime的Symbol shim/sham。 就算没有Symbol,兼容起来也不是很难,见:https://github.com/hax/my-promise/blob/master/src/Promise.js#L421-L436 可以看到在没有提供iterator时直接退化为数组遍历,当然做得更好一点,可以提供其他iteratable类型的默认实现。

army8735 commented 9 years ago

for in的话,不能遍历迭代器吗?

hax commented 9 years ago

应该不行。见这里:https://people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind-labelset

按照我读spec的理解: iterationKind对于for-in来说总是enumerate,对于for-of来说总是iterate。前者总是直接调用确定的[[Enumerate]](),除了Proxy可以改这个行为,其他所有对象的enumerate行为都是spec指定的(也就是和以前一样的行为)。也就是说for-in不会调用自定义迭代器。

hax commented 9 years ago

只是spec规定的默认迭代器的行为大多就是基于[[Enumerate]]。所以如果用户没有customize过,则for-of的行为和for-in是一致的。因此理论上应该可以做优化。我前面贴的例子里的这几行https://github.com/hax/my-promise/blob/master/src/Promise.js#L425-L429 就是:iterable如果没有自定义iterator,我就认为它是数组,然后就按数组遍历来优化。

所以有一种优化可能就是,如果是一个局部变量我们明确知道它的类型(比如是数组),并且Array.prototype[Symbol.iterator]没有被改写。则我们可以直接编译为for(;;)循环。

hax commented 9 years ago

补充:babel有可选的loose mode,其行为略有不同(tracer似乎也有)。 另外iterator的行为严格来说还有捕捉异常和触发iterator的return方法。参见这两个issue: https://github.com/google/traceur-compiler/issues/1773 https://github.com/babel/babel/issues/838