還是先從一個題目開始: 寫一個隔1s輸出數組的一項的函數。 如果可以用ES6語法,則可以這麼寫: 但是如果把這裡的 改成 ,則輸出就會變成一連串的 。 有同學很快想到了這是閉包啊,因為 把函數加入到 中,所以等到setTimeout的函數體執行時, 已經走完了 迴圈,變成了 。`arr[arr.le ...
還是先從一個題目開始:
寫一個隔1s輸出數組的一項的函數。
如果可以用ES6語法,則可以這麼寫:
function print (arr) {
for (let i = 0; i < arr.length; i++) {
setTimeout(() => {
console.log(arr[i])
}, 1000 * i);
}
}
但是如果把這裡的let
改成var
,則輸出就會變成一連串的undefined
。
有同學很快想到了這是閉包啊,因為setTimeout
把函數加入到microqueue
中,所以等到setTimeout的函數體執行時,i
已經走完了for
迴圈,變成了arr.length
。arr[arr.length]
顯然是undefined。
簡單修改一下,變成ES5的語法。
function print (arr) {
for (var i = 0; i < arr.length; i++) {
(function (index) {
setTimeout(() => {
console.log(arr[index])
}, 1000 * index);
})(i);
}
}
其實就是利用閉包是向父級作用域尋找值的特性,給i
包裝一層作用域,把i
存起來。
閉包概念還請翻看之前的一篇blog-閉包和類。
到這裡閉包的理解應該差不多了,而今天的關鍵點在於——
let做了什麼?
阮一峰老師的《ECMAScript 6》入門裡給出[定義](http://es6.ruanyifeng.com/#docs/let:
ES6 新增了
let
命令,用來聲明變數。它的用法類似於var
,但是所聲明的變數,只在let
命令所在的代碼塊內有效。
他提到了let的幾個特性:
只存在於塊級作用域中
不存在變數提升
暫時性死區
不允許重覆聲明
這裡我不再贅述,大家可以仔細閱讀一下阮一峰老師的書。
我更感興趣的是,在ES5的語法中,如何模擬let
這種塊級作用域的效果。這個時候,應該讓babel
出場了。
打開這個鏈接:可以看到轉換後的代碼。
"use strict";
function print(arr) {
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000 * i);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
}
其實可以對比發現,babel
轉換後的代碼和我們上面寫的ES5實現其實是一樣的。
大概就是通過對let
綁定的塊級作用域加一個函數,把let
聲明的參數,通過函數傳入,達到塊級作用域的效果。
大家可以在babel
試一下let
的其他特性,轉移出的ES5語法並不能實現有的特性,比如暫時性死區。
完,感謝閱讀。