簡單介紹下 ES6 規(guī)范里面迭代器(Iterator)相關(guān)的概念。最近為啥會看到迭代器,是因為看了 fetch 相關(guān)的 Headers 接口,為了實現(xiàn)下 Headers 接口就涉及到了迭代器。
迭代器(Iterator)
為什么要有迭代器,是為了給不同的數(shù)據(jù)結(jié)構(gòu)一個統(tǒng)一的迭代方法,不管你是一個 Array 還是一個 Queue 還是一個 Dictionary,反正只要你提供了符合規(guī)范的迭代器,程序員就能通過統(tǒng)一的方法來迭代你這個數(shù)據(jù)結(jié)構(gòu)。迭代器是一個實現(xiàn)了 Iterator 接口的對象。接口指定迭代器對象必須實現(xiàn)一個 next 方法,如下示例偽代碼。
var iterator = {
next: function() {
// impl
}
}
next 方法是否接受參數(shù),在規(guī)范中并不嚴(yán)格限定,取決于實現(xiàn)當(dāng)前這個迭代器的對象。但是必須能接受無參數(shù)的情況,因為 ES6 語法使用迭代器的時候是不會傳參數(shù)的,譬如 for-of。next 方法返回的數(shù)據(jù)結(jié)構(gòu)是 {done:boolean, value:ES6value}。每次調(diào)用 next,如果與之關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu)里面有數(shù)據(jù),那么按照當(dāng)前數(shù)據(jù)結(jié)構(gòu)自己的規(guī)則把當(dāng)前被迭代到的元素放到返回的數(shù)據(jù)結(jié)構(gòu)的 value 值中,done 填寫 false。done 標(biāo)識是否迭代到了最后。當(dāng)?shù)阶詈笠粋€元素后,再調(diào)用一次 next 后,返回的 done 就應(yīng)該是 true。用代碼示例下:
function getArrayIterator( array ) {
// 等下再說
}
var iterator = getArrayIterator( [1, 2, 3, 4] );
iterator.next(); // { done: false, value: 1 }
iterator.next(); // { done: false, value: 2 }
iterator.next(); // { done: false, value: 3 }
iterator.next(); // { done: false, value: 4 }
iterator.next(); // { done: true, value: undefined }
iterator.next(); // { done: true, value: undefined }
當(dāng)?shù)饕呀?jīng)迭代完最后一個元素,那么無論調(diào)用幾次 next 返回的 done 都是 true。不過規(guī)范里面說,如果在迭代的過程中,next 返回的數(shù)據(jù)結(jié)構(gòu)中沒有 done 這個屬性的話,我們應(yīng)該視作其值是 false。再來看看 value,其取值可以是規(guī)范中的任意類型。當(dāng)?shù)阶詈笠粋€元素后,即 done:true 的時候,value 可以是 undefined,也可以被填入一個返回值。MDN 中加了一篇文章的鏈接來說明當(dāng) done:true 時候,value 如果被設(shè)成有一個 returnValue 的意義,不過我英語比較挫真心沒有看懂,只能列下原文引用:
Why can iterators (optionally) return a value after the last element? That capability is the reason for elements being wrapped. Otherwise, iterators could simply return a publicly defined sentinel (stop value) after the last element.
可迭代對象(Iterable)
迭代器是通過可迭代對象獲得的,譬如數(shù)組就是一個可迭代對象。那么如何通過可迭代對象獲得迭代器呢?規(guī)范中規(guī)定可迭代對象必須實現(xiàn)一個名為 @@iterator 的方法,調(diào)用這個方法返回和當(dāng)前對象掛鉤的迭代器,譬如:
function getArrayIterator( array ) {
return array[ '@@iterator' ]();
}
var iterator = getArrayIterator( [1, 2, 3, 4] );
但是 @@iterator 方法的名字并不是一個 string,而是 Symbol.iterator。Symbol 是ES6 引入的一個新的類型,表示一個獨一無二的值(這里就不展開了)。所以上例獲取迭代器不是正確的寫法,正確的應(yīng)該是:
function getArrayIterator( array ) {
return array[ Symbol.iterator ]();
}
var iterator = getArrayIterator( [1, 2, 3, 4] );
ES6 中新加了一些針對可迭代對象的語法,譬如 for-of:
var array = [ 1, 2, 3 ];
for ( let i of array ) {
console.log( i );
}
// 輸出
// 1
// 2
// 3
/* 上面的 for-of 和下面的代碼等價 */
var iterator = array[ Symbol.iterator ]();
var iteratorResult = iterator.next();
while( !iteratorResult.done ) {
console.log( iteratorResult.value );
iteratorResult = iterator.next();
}
迭代器(Iterator)可選屬性
其實迭代器除了規(guī)定一定要實現(xiàn)的 next 方法,還有兩個是可選實現(xiàn)的方法 return 和 throw。
return
return 方法如果被調(diào)用,意味著調(diào)用者要終結(jié)此次迭代。return 返回一個 {done:true, value:arg} 對象,value 是調(diào)用 return 方法時傳入的參數(shù),即:
var iterator = {
next: function() {
// impl
},
return: function( arg ) {
return {
done: true,
value: arg
}
}
}
return 方法被調(diào)用后,后續(xù)再調(diào)用當(dāng)前迭代器的 next 方法,返回的對象 done 一律為 true。
throw
throw 方法如果被調(diào)用,表示迭代的過程中檢測到了異常。一般來說 throw 方法傳入的參數(shù)就是錯誤對象(但是這個不是強制規(guī)定的)。throw 方法的通常行為應(yīng)該是以拋出異常的方式拋出傳入的對象,但是這個行為也是建議不強制。如果 throw 方法不拋出異常,那么返回值為 {done:true}。表示迭代終結(jié),后續(xù)再調(diào)用當(dāng)前迭代器的 next 方法,返回的對象 done 一律為 true。
因為 return 和 throw 兩個方法是可選實現(xiàn)的,所以在調(diào)用迭代器這個兩個方法前,都要檢測這兩個方法是否存在。
2017.4.6 補充:
規(guī)范里面指出,迭代器也必須是一個可迭代的對象,即:
iterator = {
next: function() {
},
[Symbol.iterator]: function() {
return this;
}
}