一.作用
用來遍歷數組,類似於用 for...in 遍歷對象
for (var i = 0; i < arr.length; i++) 用著挺好,嫌麻煩還可以用 arr.forEach(),那 for...in 存在的意義是什麼?
for (var i = 0; i < arr.length; i++)不夠簡潔
太長,長就容易出錯,比如筆者經常犯的錯誤:for (var j = 0; j < arr[i].length; i++)
- forEach 不夠靈活
arr.forEach 不好用,因為無法通過 break 或者 return 跳出
for...in不適合用來遍歷數組
for...in 會遍歷到自定義屬性甚至原型屬性、index 是字符串而不是數值、某些情況下甚至不按順序遍歷
所以,我們需要一種更方便的遍歷數組的方法,就像用 for...in 遍歷對象一樣簡單易用的方法,那就是 for...of
二.特點
1.可以遍歷其它集合
不僅可以遍歷數組,還可以遍歷其它集合(iterable),包括 NodeList/arguments、字符串、Map、Set 等等
示例代碼如下:
// 數組
var arr = [1, 2, 2, 4];
for (var val of arr) {
console.log(val);
}
// 類數組對象(arguments、NodeList)
(function() {
for (var val of arguments) {
console.log(val);
}
})(1, 2, 4);
// Set
var uniqueArr = new Set(arr); // Set 自動去重
for (var val of uniqueArr) {
console.log(val);
}
// Map
var map = new Map([['a', 1], ['b', 2]]);
map.set('c', 3);
for (var [key, val] of map) {
console.log('map[' + key + '] = ' + val);
}
注意:Chrome47 不支持 var [key, val] of map 語法,FF43 支持,node --harmony v0.12.7 不支持解構表達式。如果想痛快地體驗 ES6,建議 node 安裝 think.js
2.不能遍歷對象
for...of 不支持遍歷對象,但可以通過添加 [Symbol.iterator]() 方法來讓其它對象(比如 jQuery 對象)支持 for...of 遍歷,有 [Symbol.iterator]() 方法的對象是可迭代的(iterable)
怎麼個不支持法?示例代碼如下:
var obj = {
'a': 1,
'b': 2
}
for (var val of obj) {
console.log(val);
}
// Uncaught TypeError: is not a function
// 報錯,不支持遍歷對象,那是 for...in 的職責
如果想讓自定義對象支持 for...of 遍歷,需要添加 [Symbol.iterator]() 方法,如下:
// 一般方法實現迭代器
class MyIterator {
constructor(obj) {
this.obj = obj;
this.keys = Object.keys(obj);
this.index = 0;
}
// 實現 iterator 接口
[Symbol.iterator]() {
return this;
}
next() {
var val = this.obj[this.keys[this.index]];
if (this.index === this.keys.length) {
return {
done: true
}
}
else {
this.index++;
return {
done: false,
value: val
}
}
}
}
// 測試
var _obj = {
a: 2,
b: 1
}
for (var val of new MyIterator(_obj)) {
console.log(val);
}
等等,Symbol 是什麼東西?Symbol 大有來頭:Symbol 是 js 中第 7 種基本類型(本來就有的 6 種是 null, undefined, Number, Boolean, Object, String),不是字符串也不是對象,證據如下:
typeof Symbol() === 'symbol'
上面代碼中出現的 Symbol.iterator 是一個內置的函數名(Symbol 類型的,而一般的函數名是 String),執行 for...of 語句的時候,解釋器會在 of 後面的對象身上找名為 Symbol.iterator 的方法,該方法執行後返回一個迭代器對象 iterator,拿到 iterator 後自然是 .next()、.next() 直到 done,過程類似於:
// 數組
var arr = [1, 2, 2, 4];
// for (var val of arr) {
// console.log(val);
// }
var iter = arr[Symbol.iterator]();
while (true) {
var res = iter.next();
if (res.done) {
break;
}
else {
var val = res.value;
console.log(val);
}
}
當然,我們這個功能太弱了,for...of 的強大之處在於支持 break, continue, return,就像經典 for 循環一樣
3.支持自定義迭代器
其實上面我們已經實現了自定義迭代器,但好像很麻煩的樣子。用這個,毋寧死。制定 ES6 標準的人自然也想到了,配合 generator 語法(function* + yield)很容易實現自定義迭代器,以支持 for...of 遍歷:
var obj = {
'a': 1,
'b': 2
}
// generator 實現迭代器
obj[Symbol.iterator] = function*() {
for (var key in this) {
yield this[key];
}
}
for (var val of obj) {
console.log(val);
}
秒秒鐘實現一個迭代器,這下方便多了
P.S.其實我們的 MyIterator 嚴格來說是 generator,(迭代器的)生成器
三.總結
ES5 引入了 forEach,但實際上沒什麼用,很多時候不得不把寫好的 forEach 換成經典 for 循環。。forEach 在靈活性方面不如 every,但 every 會新建一個數組。。
好了,現在有了 for...of,forEach 再見
參考資料
- 《ES6 in Depth》:InfoQ 中文站提供的免費電子書
暫無評論,快來發表你的看法吧