淺談ES6中迭代器和生成器
本文已經默認你已經知道generator
是什麼以及for...of
和數據類型map
怎麼用了。
前ES6的時代怎麼遍歷
先來一道思考題:
通過下面的變量1. 尋找xiaohong(假設名稱唯一)是否喜歡basketball 2. 所有同學的名字
conststudents={xiaohong:{age:'22',fav:['sleep','basketball'],teachers:{english:'daming',chinense:'helios',math:['helios2','helios3']}},xiaoming:{age:'22',fav:['sleep','basketball','football'],teachers:{english:'daming',chinense:'helios',math:['helios2','helios3']}},}
對於第一個問題來說,我們可以使用各種循環語句: for/while
for(leti=0;i<students[xiaoming].fav.length;i++){if(students[xiaoming].fav[i]==='basketball')console.log(true)}leti=0;while(i++<students[xiaoming].fav.length){if(students[xiaoming].fav[i]==='basketball')console.log(true)}
for...of
for(letitemofstudents[xiaoming].fav){if(item==='basketball')console.log(true)}
那麼對於第二個問題來說,因為for/while
是不能遍歷對象的,所以行不通,但是對像有一個專屬的遍歷方法for...in
我們來看一下怎麼通過for...in來遍歷:
for(letstuinstudents){console.log(stu)}
你可能會想了,通過for...in
去遍歷數組會怎樣呢?我們看一下通過for...in
去遍歷:
for(letiteminstudents[xiaoming].fav){console.log(item)// 或者去判断
}
哎呀,通過for...in
不也照樣能實現數組的遍歷麼,那為什麼不歸結到數組的遍歷裡面去呢!這裡面還有一些細節需要去了解(這也是上面的“對像有一個專屬的遍歷方法”為什麼加粗),我們通過一段代碼去解釋:
const num = [ 5 , 6 , 7 ] for ( let i in num ) { console . log ( i + 1 )} // 01 // 11 // 21
這是因為for-in
是為普通對象({key: value})設計的,所以只能遍歷到字符串類型的鍵。
還有下面這個雖然不常用,但是也是不得不說的:
const arr = [ 5 , 6 , 7 ] arr . foo = function () {} for ( let i in arr ) { console . log ( i ) } // 5 // 6 // 7 // foo !!!
foo
屬於arr上面的方法,被遍歷出來是說的過去的。
那麼用for...of
我們來看看會怎麼樣
for(letstuofstudents){}// Uncaught TypeError: students is not iterable
is not iterable ,這個iterable
是神馬東西,我們接下來下面一步步的看。
先從可迭代(iterable)和迭代器(iterator)說起
iterable是ES6對iteration(迭代/遍歷)引入的接口。
如果一個對像被視為iterable(可迭代)那麼它一定有一個Symbol.iterator
屬性,這個屬性返回一個iterator(迭代器)方法,這個方法返回一個規定的對象(這個後面會說)。也就是說iterable
是iterator
的工廠, iterable
能夠創建iterator
。 iterator
是用於遍歷數據結構中元素的指針。
兩者之間的關係
Axel Rauschmaye大神的圖簡直不能再清晰了。
數據消費者: javascript本身提供的消費語句結構,例如for...of循環和spread operator (...)數據源:數據消費者能夠通過不同的源(Array,string)得到供數據消費者消費的值;
讓數據消費者支持所有的數據源這是不可以行的,因為還可能增加新的數據消費者和數據源。因此ES6引入了Iterable
接口數據源去實現,數據消費者去使用
可迭代協議(iterable protocol)和迭代器協議(iterator protocol)
可迭代協議(iterable protocol)
可迭代協議(iterable protocol)是允許js對象能夠自定義自己的迭代行為。
簡單的說只要對像有Symbol.iterator
這個屬性就是可迭代的,我們可以通過重寫(一些對象實現了iterable,比如Array,string)/添加(對於沒有實現iterable的對像比如object,可以添加這個屬性,使之變為可迭代的)該熟悉使之變為可迭代的。
當一個對象需要被迭代(for...of或者spread operator )的時候,他的Symbol.iterator
函數被調用並且無參數,然後返回一個迭代器。
迭代器協議(iterator protocol)
迭代器協議定義了一種標準的方式來產生一個有限或無限序列的值。
當一個對像被認為是一個迭代器的時候,它會至少實現next()
方法, next()
方法返回兩個屬性value
(d迭代器返回的值)和done
(迭代時候已經結束)。
還有幾個可選的方法可以被實現,具體請看: sec-iterator-interface
iterable協議, iterator協議還有next之間的關係
來源於網絡
然後談談ES6中的for...of說起
再文章的最開始我們已經說了再前ES6的時候,如何去遍歷。現在我們說說ES6新增的for...of
的作用。
for...in
在前面也已經說了,在ES6之前遍歷object的時候用for...in
循環, for...in
會遍歷對像上所有可枚舉的值(包括原型(prototype)上的屬性),比如下面這樣:
function foo () { this . name = 'helios' } foo . prototype . sayName = function () { return this . name ; } var o = new foo (); for ( var i in o ) { console . log ( i ) } // name // sayName
如果我們只想遍歷對象自身的屬性,可以使用hasOwnProperty
,如下:
function foo() { this.name = 'helios' } foo.prototype.sayName = function() { return this.name; } var o = new foo(); for (var i in o) { if (!o.hasOwnProperty(i)) continue; console.log(i) }
如果我們不想讓一個對象的屬性,在for...in
中不被遍歷出來,可是使用Object.defineProperty
來定義對像上的屬性是否可別枚舉(更多的屬性請看: Object.defineProperty() ),具體如下面代碼:
varobj={name:'helios'}Object.defineProperty(obj,'age',{enumerable:false})for(variinobj){console.log(i)}
在這一小節的最後我們來說說for...in
中的in操作符的含義:
prop in obj
: -含義:判斷prop是否在obj中- prop:對象的key屬性的類型(string / Symbol) -返回值: boolean
我們來看一組例子:
var o = { name : 'heliso' } console . log ( 'name' in o ) // true console . log ( Symbol . iterator in o ) // false console . log ( 'toString' in o ) // true
這個操作符雖然也適用於數組,但是盡量還是不要用在數組中,因為會比較奇怪,如下代碼:
var arr = [ 6 , 7 , 8 ] console . log ( 7 in arr ) // false console . log ( 1 in arr ) // true console . log ( 'length' in arr ) // true
主要是前兩個比較奇怪對不對,因為對於數組prop
代表的是數組的索引而為其存在的值。按照這樣的思路,正在看的讀者你能思考一下in
操作符在字符串中是怎麼的模式麼?
for...of能遍歷的集合
只要是實現了Interable
接口的數據類型都能被遍歷。
javascript內部實現的有: - Array - String - Map - Set - arguments - DOM data structures
並不是所有的iterable內容都來源於數據結構,也能通過在運行中計算出來,例如所有ES6的主要數據結構有三個方法能夠返回iterable對象。
- entries() 返回一個可遍歷的entries
- keys() 返回一個可遍歷的entries 的keys。
- values() 返回一個可遍歷的entries 的values。
如果for...of不能遍歷怎麼辦
那就數據結構(數據源)去實現iterable就可以了。
用通俗的話說就是,你如果要遍歷一個對象的話,有一下幾個步驟:
- 對像如果沒實現
Symbol.iterator
那就去實現 - 對象的
Symbol.iterator
函數要返回一個iterator
-
iterator
返回一個對象,對像中至少要包含一個next方法來獲取 - next方法返回兩個值
value
和done
現在說說怎麼使object變為可迭代的
上面我們已經鋪墊了這麼多了,我們說了javascript中object是不能被迭代了,因為沒有實現iterable
,現在讓我們來實踐一下讓object變的可迭代。
第一步: 先嘗試著使用for...of遍歷object
下面這樣寫肯定是不行的
constobj={name:'helios',age:23}for(letitofobj){console.log(it)}// TypeError: obj is not iterable
第二步: 讓object實現iterable接口
constobj={name:'helios',age:23,[Symbol.iterator]:function(){letage=23;constiterator={next(){return{value:age,done:age++>24}}}returniterator}}
如果iterable
和iterable
是一個對象的話,上面的代碼可以簡化為:
functioniterOver(){letage=23;constiterable={[Symbol.iterator](){returnthis},next(){return{value:age,done:age++>24}}}returniterable}for(letiofiterOver()){console.log(i)}
現在生成器(generator)可以出場了
我們如果每次想把一個不能迭代的對像變為可迭代的對象,在實現Symbol.iterator
的時候,每次都要寫返回一個對象,對象裡面有對應的next方法,next方法必須返回valua和done兩個值。
這樣寫的話每次都會很繁,好在ES6提供了generator(生成器)能生成迭代器,我們來看簡化後的代碼:
constobj={name:'helios',age:23,[Symbol.iterator]:function*(){while(this.age<=24)yieldthis.age++}}for(letitofobj){console.log(it)}
讓object可迭代真的有意義麼
知乎的這個回答是很有水平的了:為什麼es6裡的object不可迭代?
在stackoverflow中也有很高質量的回答: Why are Objects not Iterable in JavaScript?
在上面的回答中從技術方面說了為什麼Object不能迭代(沒有實現iterable),還說了以什麼樣的方式去遍歷Object是個難題,所以把如何迭代的方式去留給了開發者。
但是還是要思考的一個問題就是:我們真有必要去迭代對象字面量麼?
想一下我們要迭代對象字面量的什麼呢?是keys
還是values
亦或者是entries
,這三種方式在ES6提供的新的數據類型map裡面都有呀,完全是可以代替object的。在這裡不說object
和map
的區別,只是說說在ES6以後我們想把兩個事物關聯起來的時候,不一定要非得是用对象字面量
了, map
支持的更好一下。
對於什麼時候用對象字面量(object)什麼時候使用map我們可以做一下總結:
- 對象字面量(object)應該是靜態的,也就是說我們應該已經知道了裡面有多少個,和對象的屬性有什麼
- 使用對象字面量(object)的一般場景有:
- 不需要去遍歷對象字面量(object)的所有屬性的時候
- 我們知道了裡面有多少個屬性和對象的屬性是什麼的時候
- 需要去
JSON.stringify
和JSON.parse
時候 - 其他的情況用map,其他的情況包括:
- key不是字符串或者symbol的時候
- 需要去遍歷的時候
- 要得到長度的時候
- 遍歷的時候對順序有要求的(對象字面量(object)可能不是按照你寫的順序)
也並不說是map
就肯定比對象字面量(object)好, map
也有如下的缺點:
- 不能使用對象解構
- 不能
JSON.stringify
/JSON.parse
參考
- 25.1.1.1 The Iterable Interface
- Maps vs Objects in ES6, When to use?
- Iterables and iterators in ECMAScript 6
- 【翻譯】Iterables and iterators in ECMAScript 6
- Maps vs Objects in ES6, When to use?
- 迭代協議
- ES6你可能不知道的事-進階篇
- 深入淺出ES6(二):迭代器和for-of循環
- 深入淺出ES6(三):生成器Generators
- Javascript ES6 Iterators建議指南(含實例)
本文已經默認你已經知道generator
是什麼以及for...of
和數據類型map
怎麼用了。
前ES6的時代怎麼遍歷
先來一道思考題:
通過下面的變量1. 尋找xiaohong(假設名稱唯一)是否喜歡basketball 2. 所有同學的名字
conststudents={xiaohong:{age:'22',fav:['sleep','basketball'],teachers:{english:'daming',chinense:'helios',math:['helios2','helios3']}},xiaoming:{age:'22',fav:['sleep','basketball','football'],teachers:{english:'daming',chinense:'helios',math:['helios2','helios3']}},}
對於第一個問題來說,我們可以使用各種循環語句: for/while
for(leti=0;i<students[xiaoming].fav.length;i++){if(students[xiaoming].fav[i]==='basketball')console.log(true)}leti=0;while(i++<students[xiaoming].fav.length){if(students[xiaoming].fav[i]==='basketball')console.log(true)}
for...of
for(letitemofstudents[xiaoming].fav){if(item==='basketball')console.log(true)}
那麼對於第二個問題來說,因為for/while
是不能遍歷對象的,所以行不通,但是對像有一個專屬的遍歷方法for...in
我們來看一下怎麼通過for...in來遍歷:
for(letstuinstudents){console.log(stu)}
你可能會想了,通過for...in
去遍歷數組會怎樣呢?我們看一下通過for...in
去遍歷:
for(letiteminstudents[xiaoming].fav){console.log(item)// 或者去判断
}
哎呀,通過for...in
不也照樣能實現數組的遍歷麼,那為什麼不歸結到數組的遍歷裡面去呢!這裡面還有一些細節需要去了解(這也是上面的“對像有一個專屬的遍歷方法”為什麼加粗),我們通過一段代碼去解釋:
const num = [ 5 , 6 , 7 ] for ( let i in num ) { console . log ( i + 1 )} // 01 // 11 // 21
這是因為for-in
是為普通對象({key: value})設計的,所以只能遍歷到字符串類型的鍵。
還有下面這個雖然不常用,但是也是不得不說的:
const arr = [ 5 , 6 , 7 ] arr . foo = function () {} for ( let i in arr ) { console . log ( i ) } // 5 // 6 // 7 // foo !!!
foo
屬於arr上面的方法,被遍歷出來是說的過去的。
那麼用for...of
我們來看看會怎麼樣
for(letstuofstudents){}// Uncaught TypeError: students is not iterable
is not iterable ,這個iterable
是神馬東西,我們接下來下面一步步的看。
先從可迭代(iterable)和迭代器(iterator)說起
iterable是ES6對iteration(迭代/遍歷)引入的接口。
如果一個對像被視為iterable(可迭代)那麼它一定有一個Symbol.iterator
屬性,這個屬性返回一個iterator(迭代器)方法,這個方法返回一個規定的對象(這個後面會說)。也就是說iterable
是iterator
的工廠, iterable
能夠創建iterator
。 iterator
是用於遍歷數據結構中元素的指針。
兩者之間的關係
Axel Rauschmaye大神的圖簡直不能再清晰了。
數據消費者: javascript本身提供的消費語句結構,例如for...of循環和spread operator (...)數據源:數據消費者能夠通過不同的源(Array,string)得到供數據消費者消費的值;
讓數據消費者支持所有的數據源這是不可以行的,因為還可能增加新的數據消費者和數據源。因此ES6引入了Iterable
接口數據源去實現,數據消費者去使用
可迭代協議(iterable protocol)和迭代器協議(iterator protocol)
可迭代協議(iterable protocol)
可迭代協議(iterable protocol)是允許js對象能夠自定義自己的迭代行為。
簡單的說只要對像有Symbol.iterator
這個屬性就是可迭代的,我們可以通過重寫(一些對象實現了iterable,比如Array,string)/添加(對於沒有實現iterable的對像比如object,可以添加這個屬性,使之變為可迭代的)該熟悉使之變為可迭代的。
當一個對象需要被迭代(for...of或者spread operator )的時候,他的Symbol.iterator
函數被調用並且無參數,然後返回一個迭代器。
迭代器協議(iterator protocol)
迭代器協議定義了一種標準的方式來產生一個有限或無限序列的值。
當一個對像被認為是一個迭代器的時候,它會至少實現next()
方法, next()
方法返回兩個屬性value
(d迭代器返回的值)和done
(迭代時候已經結束)。
還有幾個可選的方法可以被實現,具體請看: sec-iterator-interface
iterable協議, iterator協議還有next之間的關係

來源於網絡
然後談談ES6中的for...of說起
再文章的最開始我們已經說了再前ES6的時候,如何去遍歷。現在我們說說ES6新增的for...of
的作用。
for...of能遍歷的集合
只要是實現了Interable
接口的數據類型都能被遍歷。
javascript內部實現的有: - Array - String - Map - Set - arguments - DOM data structures
並不是所有的iterable內容都來源於數據結構,也能通過在運行中計算出來,例如所有ES6的主要數據結構有三個方法能夠返回iterable對象。
- entries() 返回一個可遍歷的entries
- keys() 返回一個可遍歷的entries 的keys。
- values() 返回一個可遍歷的entries 的values。
如果for...of不能遍歷怎麼辦
那就數據結構(數據源)去實現iterable就可以了。
用通俗的話說就是,你如果要遍歷一個對象的話,有一下幾個步驟:
- 對像如果沒實現
Symbol.iterator
那就去實現 - 對象的
Symbol.iterator
函數要返回一個iterator
-
iterator
返回一個對象,對像中至少要包含一個next方法來獲取 - next方法返回兩個值
value
和done
現在說說怎麼使object變為可迭代的
上面我們已經鋪墊了這麼多了,我們說了javascript中object是不能被迭代了,因為沒有實現iterable
,現在讓我們來實踐一下讓object變的可迭代。
第一步: 先嘗試著使用for...of遍歷object
下面這樣寫肯定是不行的
constobj={name:'helios',age:23}for(letitofobj){console.log(it)}// TypeError: obj is not iterable
第二步: 讓object實現iterable接口
constobj={name:'helios',age:23,[Symbol.iterator]:function(){letage=23;constiterator={next(){return{value:age,done:age++>24}}}returniterator}}
如果iterable
和iterable
是一個對象的話,上面的代碼可以簡化為:
functioniterOver(){letage=23;constiterable={[Symbol.iterator](){returnthis},next(){return{value:age,done:age++>24}}}returniterable}for(letiofiterOver()){console.log(i)}
現在生成器(generator)可以出場了
我們如果每次想把一個不能迭代的對像變為可迭代的對象,在實現Symbol.iterator
的時候,每次都要寫返回一個對象,對象裡面有對應的next方法,next方法必須返回valua和done兩個值。
這樣寫的話每次都會很繁,好在ES6提供了generator(生成器)能生成迭代器,我們來看簡化後的代碼:
constobj={name:'helios',age:23,[Symbol.iterator]:function*(){while(this.age<=24)yieldthis.age++}}for(letitofobj){console.log(it)}
讓object可迭代真的有意義麼
知乎的這個回答是很有水平的了:為什麼es6裡的object不可迭代?
在stackoverflow中也有很高質量的回答: Why are Objects not Iterable in JavaScript?
在上面的回答中從技術方面說了為什麼Object不能迭代(沒有實現iterable),還說了以什麼樣的方式去遍歷Object是個難題,所以把如何迭代的方式去留給了開發者。
但是還是要思考的一個問題就是:我們真有必要去迭代對象字面量麼?
想一下我們要迭代對象字面量的什麼呢?是keys
還是values
亦或者是entries
,這三種方式在ES6提供的新的數據類型map裡面都有呀,完全是可以代替object的。在這裡不說object
和map
的區別,只是說說在ES6以後我們想把兩個事物關聯起來的時候,不一定要非得是用对象字面量
了, map
支持的更好一下。
對於什麼時候用對象字面量(object)什麼時候使用map我們可以做一下總結:
- 對象字面量(object)應該是靜態的,也就是說我們應該已經知道了裡面有多少個,和對象的屬性有什麼
- 使用對象字面量(object)的一般場景有:
- 不需要去遍歷對象字面量(object)的所有屬性的時候
- 我們知道了裡面有多少個屬性和對象的屬性是什麼的時候
- 需要去
JSON.stringify
和JSON.parse
時候 - 其他的情況用map,其他的情況包括:
- key不是字符串或者symbol的時候
- 需要去遍歷的時候
- 要得到長度的時候
- 遍歷的時候對順序有要求的(對象字面量(object)可能不是按照你寫的順序)
也並不說是map
就肯定比對象字面量(object)好, map
也有如下的缺點:
- 不能使用對象解構
- 不能
JSON.stringify
/JSON.parse
參考
- 25.1.1.1 The Iterable Interface
- Maps vs Objects in ES6, When to use?
- Iterables and iterators in ECMAScript 6
- 【翻譯】Iterables and iterators in ECMAScript 6
- Maps vs Objects in ES6, When to use?
- 迭代協議
- ES6你可能不知道的事-進階篇
- 深入淺出ES6(二):迭代器和for-of循環
- 深入淺出ES6(三):生成器Generators
- Javascript ES6 Iterators建議指南(含實例)
本文已經默認你已經知道generator
是什麼以及for...of
和數據類型map
怎麼用了。
前ES6的時代怎麼遍歷
先來一道思考題:
通過下面的變量1. 尋找xiaohong(假設名稱唯一)是否喜歡basketball 2. 所有同學的名字
conststudents={xiaohong:{age:'22',fav:['sleep','basketball'],teachers:{english:'daming',chinense:'helios',math:['helios2','helios3']}},xiaoming:{age:'22',fav:['sleep','basketball','football'],teachers:{english:'daming',chinense:'helios',math:['helios2','helios3']}},}
對於第一個問題來說,我們可以使用各種循環語句: for/while
for(leti=0;i<students[xiaoming].fav.length;i++){if(students[xiaoming].fav[i]==='basketball')console.log(true)}leti=0;while(i++<students[xiaoming].fav.length){if(students[xiaoming].fav[i]==='basketball')console.log(true)}
for...of
for(letitemofstudents[xiaoming].fav){if(item==='basketball')console.log(true)}
那麼對於第二個問題來說,因為for/while
是不能遍歷對象的,所以行不通,但是對像有一個專屬的遍歷方法for...in
我們來看一下怎麼通過for...in來遍歷:
for(letstuinstudents){console.log(stu)}
你可能會想了,通過for...in
去遍歷數組會怎樣呢?我們看一下通過for...in
去遍歷:
for(letiteminstudents[xiaoming].fav){console.log(item)// 或者去判断
}
哎呀,通過for...in
不也照樣能實現數組的遍歷麼,那為什麼不歸結到數組的遍歷裡面去呢!這裡面還有一些細節需要去了解(這也是上面的“對像有一個專屬的遍歷方法”為什麼加粗),我們通過一段代碼去解釋:
const num = [ 5 , 6 , 7 ] for ( let i in num ) { console . log ( i + 1 )} // 51 // 61 // 71
這是因為for-in
是為普通對象({key: value})設計的,所以只能遍歷到字符串類型的鍵。
還有下面這個雖然不常用,但是也是不得不說的:
const arr = [ 5 , 6 , 7 ] arr . foo = function () {} for ( let i in arr ) { console . log ( i ) } // 5 // 6 // 7 // foo !!!
foo
屬於arr上面的方法,被遍歷出來是說的過去的。
那麼用for...of
我們來看看會怎麼樣
for(letstuofstudents){}// Uncaught TypeError: students is not iterable
is not iterable ,這個iterable
是神馬東西,我們接下來下面一步步的看。
先從可迭代(iterable)和迭代器(iterator)說起
iterable是ES6對iteration(迭代/遍歷)引入的接口。
如果一個對像被視為iterable(可迭代)那麼它一定有一個Symbol.iterator
屬性,這個屬性返回一個iterator(迭代器)方法,這個方法返回一個規定的對象(這個後面會說)。也就是說iterable
是iterator
的工廠, iterable
能夠創建iterator
。 iterator
是用於遍歷數據結構中元素的指針。
兩者之間的關係
Axel Rauschmaye大神的圖簡直不能再清晰了。
數據消費者: javascript本身提供的消費語句結構,例如for...of循環和spread operator (...)數據源:數據消費者能夠通過不同的源(Array,string)得到供數據消費者消費的值;
讓數據消費者支持所有的數據源這是不可以行的,因為還可能增加新的數據消費者和數據源。因此ES6引入了Iterable
接口數據源去實現,數據消費者去使用
可迭代協議(iterable protocol)和迭代器協議(iterator protocol)
可迭代協議(iterable protocol)
可迭代協議(iterable protocol)是允許js對象能夠自定義自己的迭代行為。
簡單的說只要對像有Symbol.iterator
這個屬性就是可迭代的,我們可以通過重寫(一些對象實現了iterable,比如Array,string)/添加(對於沒有實現iterable的對像比如object,可以添加這個屬性,使之變為可迭代的)該熟悉使之變為可迭代的。
當一個對象需要被迭代(for...of或者spread operator )的時候,他的Symbol.iterator
函數被調用並且無參數,然後返回一個迭代器。
迭代器協議(iterator protocol)
迭代器協議定義了一種標準的方式來產生一個有限或無限序列的值。
當一個對像被認為是一個迭代器的時候,它會至少實現next()
方法, next()
方法返回兩個屬性value
(d迭代器返回的值)和done
(迭代時候已經結束)。
還有幾個可選的方法可以被實現,具體請看: sec-iterator-interface
iterable協議, iterator協議還有next之間的關係

來源於網絡
然後談談ES6中的for...of說起
再文章的最開始我們已經說了再前ES6的時候,如何去遍歷。現在我們說說ES6新增的for...of
的作用。
for...of能遍歷的集合
只要是實現了Interable
接口的數據類型都能被遍歷。
javascript內部實現的有: - Array - String - Map - Set - arguments - DOM data structures
並不是所有的iterable內容都來源於數據結構,也能通過在運行中計算出來,例如所有ES6的主要數據結構有三個方法能夠返回iterable對象。
- entries() 返回一個可遍歷的entries
- keys() 返回一個可遍歷的entries 的keys。
- values() 返回一個可遍歷的entries 的values。
如果for...of不能遍歷怎麼辦
那就數據結構(數據源)去實現iterable就可以了。
用通俗的話說就是,你如果要遍歷一個對象的話,有一下幾個步驟:
- 對像如果沒實現
Symbol.iterator
那就去實現 - 對象的
Symbol.iterator
函數要返回一個iterator
-
iterator
返回一個對象,對像中至少要包含一個next方法來獲取 - next方法返回兩個值
value
和done
現在說說怎麼使object變為可迭代的
上面我們已經鋪墊了這麼多了,我們說了javascript中object是不能被迭代了,因為沒有實現iterable
,現在讓我們來實踐一下讓object變的可迭代。
第一步: 先嘗試著使用for...of遍歷object
下面這樣寫肯定是不行的
constobj={name:'helios',age:23}for(letitofobj){console.log(it)}// TypeError: obj is not iterable
第二步: 讓object實現iterable接口
constobj={name:'helios',age:23,[Symbol.iterator]:function(){letage=23;constiterator={next(){return{value:age,done:age++>24}}}returniterator}}
如果iterable
和iterable
是一個對象的話,上面的代碼可以簡化為:
functioniterOver(){letage=23;constiterable={[Symbol.iterator](){returnthis},next(){return{value:age,done:age++>24}}}returniterable}for(letiofiterOver()){console.log(i)}
現在生成器(generator)可以出場了
我們如果每次想把一個不能迭代的對像變為可迭代的對象,在實現Symbol.iterator
的時候,每次都要寫返回一個對象,對象裡面有對應的next方法,next方法必須返回valua和done兩個值。
這樣寫的話每次都會很繁,好在ES6提供了generator(生成器)能生成迭代器,我們來看簡化後的代碼:
constobj={name:'helios',age:23,[Symbol.iterator]:function*(){while(this.age<=24)yieldthis.age++}}for(letitofobj){console.log(it)}
讓object可迭代真的有意義麼
知乎的這個回答是很有水平的了:為什麼es6裡的object不可迭代?
在stackoverflow中也有很高質量的回答: Why are Objects not Iterable in JavaScript?
在上面的回答中從技術方面說了為什麼Object不能迭代(沒有實現iterable),還說了以什麼樣的方式去遍歷Object是個難題,所以把如何迭代的方式去留給了開發者。
但是還是要思考的一個問題就是:我們真有必要去迭代對象字面量麼?
想一下我們要迭代對象字面量的什麼呢?是keys
還是values
亦或者是entries
,這三種方式在ES6提供的新的數據類型map裡面都有呀,完全是可以代替object的。在這裡不說object
和map
的區別,只是說說在ES6以後我們想把兩個事物關聯起來的時候,不一定要非得是用对象字面量
了, map
支持的更好一下。
對於什麼時候用對象字面量(object)什麼時候使用map我們可以做一下總結:
- 對象字面量(object)應該是靜態的,也就是說我們應該已經知道了裡面有多少個,和對象的屬性有什麼
- 使用對象字面量(object)的一般場景有:
- 不需要去遍歷對象字面量(object)的所有屬性的時候
- 我們知道了裡面有多少個屬性和對象的屬性是什麼的時候
- 需要去
JSON.stringify
和JSON.parse
時候 - 其他的情況用map,其他的情況包括:
- key不是字符串或者symbol的時候
- 需要去遍歷的時候
- 要得到長度的時候
- 遍歷的時候對順序有要求的(對象字面量(object)可能不是按照你寫的順序)
也並不說是map
就肯定比對象字面量(object)好, map
也有如下的缺點:
- 不能通過...去解構
- 不能
JSON.stringify
/JSON.parse