JS 引擎如何實現 for (let i;...) { } 寫法中每次循環綁定不同的循環變數 i?

在阮一峰老師的《ECMAScript 6 入門》中有這樣一段:

地址:http://es6.ruanyifeng.com/#docs/let#基本用法

而在《You Don"t Know JS》中也有相關內容的解釋:

地址:getify/You-Dont-Know-JS

地址:getify/You-Dont-Know-JS

這裡都說,對於 for (let i;...) {} 這種情況在每一次循環,i 都會 re-bind 到當次循環的值,我的問題是,這種 re-bind 是如何實現的

可能的解釋:

1. 根據作用域鏈和閉包的原理,對於每次循環都創建獨立的執行上下文,這樣有多少次循環就有多少個獨立的執行上下文,而每個獨立的執行上下文綁定了不同的 i,再根據作用域鏈查找到不同的 i 即可。
但另外的疑問是:每循環一次都創建獨立的執行上下文在效率和內存使用率上是不是很浪費,而且循環次數多的時候可能會爆?

2. 所有的循環還是一個共用的執行上下文,但是對於 for (let i;...) {} 這之中的 i 則特殊處理,使得每次循環內的閉包都綁定到不同的 i 值(即當次循環的 i 的值)。

想問一問哪種解釋是正確的。

其他可能的參考:

ECMA 標準的解釋:http://www.ecma-international.org/ecma-262/7.0/#sec-for-statement-runtime-semantics-labelledevaluation


蟹妖。

這個不是ES6如何實現的問題,ES6隻是個規範,它要求是這樣,不是它實現成這樣。

這個是V8(以及其他JS引擎)如何實現的問題,對於V8我是不了解的,不瞎答了。希望其他巨巨能補全。

babel是怎麼實現呢?其實這個非常簡單,我們先看結果,再看原因

for (let i = 0; i &< 5; ++i) { setTimeout(function() { console.log(i) }, 500) }

babel的結果是

var _loop = function _loop(i) {
setTimeout(function () {
console.log(i);
}, 500);
};

for (var i = 0; i &< 5; ++i) { _loop(i); }

我們知道在ES6以前只有全局作用域和函數作用於,ES6里才有局部作用域。

所以babel要「實現」局部作用域的辦法自然是用一個函數來產生一個作用域,這個和我們以為的經驗是一致的。


哎,有規範就看規範啊……

13.7.4.7Runtime Semantics: LabelledEvaluation

13.7.4.8Runtime Semantics: ForBodyEvaluation( test, increment, stmt, perIterationBindings, labelSet )

13.7.4.9Runtime Semantics: CreatePerIterationEnvironment( perIterationBindings )

for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement 規則的時候

每次迭代會新建運行環境記錄值為拷貝最後迭代內容

(大白話 = 每次循環體都是個獨立的新scope)

所以當次循環體里的定義的func往外爬變數就是當次循環體內的值了唄……


不是寫的很明確了嗎,js引擎內部有個你訪問不到的變數存著每次i的值。


let i 中的 i 是一個 "區塊變數",每一個i只能活到當次 for 大括弧結束。

var i 中的i 是一個"局部變數",這個i的作用於和生命期不受 for 大括弧限制。

我寧願老老實實寫

for ... {

a[i] = (function(i) { return function() { console.log(i); } }(i));

}

難看點,但是概念暴露出來了。


推薦閱讀:

gulp是一個前端工作流管理工具,具體做什麼的?
如何看待『真阿當』關於前端核心壁壘的描述?
CSS 動畫會不會被 JS 阻塞?
前端面試時總讓寫原生Ajax真的很有意義嗎?
一個普通三本學院的一個普通學生,對未來的迷茫,我應該怎樣規劃我的人生?

TAG:JavaScript | 閉包 | ECMAScript2015 | 阮一峰人物 |