跳到主要內容
黯羽輕揚每天積累一點點

for...of 循環_ES6 筆記 1

免費2016-03-13#JS#ES6 for-of#js for-of#ES6 forof#ES6 forof循环

for...of 用來遍歷數組,類似於用 for...in 遍歷對象。

一.作用

用來遍歷數組,類似於用 for...in 遍歷對象

for (var i = 0; i < arr.length; i++) 用著挺好,嫌麻煩還可以用 arr.forEach(),那 for...in 存在的意義是什麼?

  1. for (var i = 0; i < arr.length; i++) 不夠簡潔

太長,長就容易出錯,比如筆者經常犯的錯誤:for (var j = 0; j < arr[i].length; i++)

  1. forEach 不夠靈活

arr.forEach 不好用,因為無法通過 break 或者 return 跳出

  1. 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...offorEach 再見

參考資料

  • 《ES6 in Depth》:InfoQ 中文站提供的免費電子書

評論

暫無評論,快來發表你的看法吧

提交評論