轉(zhuǎn):https://juejin.cn/post/7055958299996323854#heading-0
前言
【Iterator】(迭代器)是ES6中JavaScript的一個(gè)新特性,擁有迭代器特性的數(shù)據(jù),js可以為它執(zhí)行如for...of,map(),filter()等迭代方法。掌握這個(gè)技巧之后,在某些開發(fā)業(yè)務(wù)常見中籽孙,我們可以利用迭代器做出更合適的技術(shù)選擇亲善。
迭代協(xié)議
值得注意的是凉袱,在ES6中我們看到圍繞著迭代器傲宜,新增出了許多的API运杭。如for...of,map(),filter()等『洌可是這些方法的實(shí)現(xiàn)辆憔,并不是ES在底層新增了一些內(nèi)置實(shí)現(xiàn)或者語法。ES做的只是定制了一套【迭代協(xié)議】报嵌。
這個(gè)迭代協(xié)議有2個(gè)部分虱咧,【可迭代協(xié)議】和【迭代器協(xié)議】。
迭代器協(xié)議
迭代器協(xié)議規(guī)定了锚国,一個(gè)迭代器應(yīng)該要長(zhǎng)什么樣子腕巡。在官方規(guī)定中,一個(gè)對(duì)象想要被稱為“迭代器”血筑,就必須擁有next方法绘沉。而這個(gè)next方法調(diào)用之后會(huì)返回一個(gè)對(duì)象。這個(gè)對(duì)象有2個(gè)屬性value和done豺总。
- value 是本次迭代的返回值车伞。在done為true之后,value就沒有意義了喻喳,但最好返回undefined另玖。
- done 是迭代結(jié)束的標(biāo)識(shí)。
根據(jù)這個(gè)規(guī)范表伦,我們大致可以寫出一個(gè)迭代器應(yīng)該是以下的樣子:
var myIterator = {
next:function(){
count++谦去;
if(count<3){
return {value: count,done:false};
}else{
return {value: undefined,done:true};
}
},
count:0
}
復(fù)制代碼
可迭代協(xié)議
可是光有迭代器是沒用的,我們需要有一個(gè)載體能讓迭代器跑起來绑榴。這就是【可迭代協(xié)議】的工作了哪轿。可迭代協(xié)議規(guī)范了哪些數(shù)據(jù)可以作為運(yùn)行迭代器的載體翔怎。在JavaScript中窃诉,String、Array赤套、TypedArray飘痛、Map 和 Set 等都是內(nèi)置的符合可迭代協(xié)議的類型。
其中最常見的調(diào)用迭代的方式之一就是for...of容握。
for(var i of [1,2,3]){
console.log(i);
}
for(var i of "123"){
console.log(i);
}
for(var i of new Set([1,2,3])){
console.log(i);
}
// 以上均輸出
// 1
// 2
// 3
復(fù)制代碼
Symbol.iterator
ok宣脉,現(xiàn)在我們知道了平常的一些類型可以調(diào)用迭代方法是因?yàn)樗麄兎狭丝傻鷧f(xié)議√奘希可是這個(gè)協(xié)議具體是什么塑猖?我們可以手動(dòng)的讓其他類型也符合這個(gè)協(xié)議嗎竹祷?
答案其實(shí)是 @@iterator 方法,只有一個(gè)數(shù)據(jù)身上有 @@iterator 方法就符合可迭代器協(xié)議了羊苟。而這個(gè)方法可以通過被【Symbol.iterator】屬性引用塑陵。
為此我們可以在瀏覽器控制臺(tái)輸出array,map蜡励,set等類型令花,可以看到他們都內(nèi)置了一個(gè)Symbol.iterator屬性。
[圖片上傳失敗...(image-e345ea-1658818947596)]
因此我們可以簡(jiǎn)單地理解為只有一個(gè)數(shù)據(jù)(不管是什么內(nèi)類)凉倚,需要它身上有【Symbol.iterator】屬性兼都,且這個(gè)屬性指向一個(gè)符合迭代器協(xié)議的迭代器之后。這個(gè)數(shù)據(jù)就符合了可迭代協(xié)議稽寒。
手動(dòng)實(shí)現(xiàn)Iterator
讀到這里扮碧,其實(shí)我們已經(jīng)做好了所有前置準(zhǔn)備了。現(xiàn)在我們開始來手動(dòng)為一個(gè)對(duì)象(非內(nèi)置符合可迭代器協(xié)議的數(shù)據(jù)類型)實(shí)現(xiàn)for of 方法瓦胎。
// 在js中對(duì)象類型芬萍,默認(rèn)并不符合可迭代協(xié)議
var obj = {a:'a',b:'b'};
for(var i of obj){
console.log(i);
}
// 執(zhí)行會(huì)報(bào)錯(cuò) Uncaught TypeError: obj is not iterable
復(fù)制代碼
我們先寫一個(gè)迭起器。
// 根據(jù)迭代器協(xié)議搔啊,寫一個(gè)迭代器
var myIterator = function(){
var done = false;
return {
next: function(){
if(done){
return {value:'hi',done:true};
}else{
done = true;
return {value:'going',done:false};
}
}
}
}
復(fù)制代碼
然后給一個(gè)對(duì)象添加【Symbol.iterator】屬性,并指向上面的寫的迭代器北戏。
var obj={};
obj[Symbol.iterator] = myIterator;
復(fù)制代碼
此時(shí)再調(diào)用for...of
for(var i of obj){
console.log(i);
}
// 正常輸出
// 'going'
復(fù)制代碼
可以看到负芋,obj已經(jīng)可以像內(nèi)置符合迭代器的類型一樣執(zhí)行for...of了。
結(jié)合生成器優(yōu)化寫法
相信細(xì)心的朋友在看到next方法時(shí)嗜愈,就想到了生成器旧蛾。既然迭代是在不停地調(diào)用next方法,生成器函數(shù)又天生自帶next方法蠕嫁,那么是不是可以直接用生成器來定義迭代器呢锨天?答案是可以的,結(jié)合生成器我們可以寫出更加簡(jiǎn)練的代碼剃毒。
var myIterator = function*(){
yield 11;
yield 12;
yield 13;
yield 14;
yield 15;
}
// 定義一個(gè)空的數(shù)組
var arr = [];
arr[Symbol.iterator] = myIterator;
for(var i of arr){
console.log(i);
}
// 輸出
// 11
// 12
// 13
// 14
// 15
復(fù)制代碼
所以生成器究竟是對(duì)象還是函數(shù)病袄?
通過上述的簡(jiǎn)化實(shí)現(xiàn)之后,我們發(fā)現(xiàn)結(jié)合生成器確實(shí)能實(shí)現(xiàn)我們想要的效果赘阀∫娌可是本文前面不是說了,迭代器必須符合迭代器協(xié)議嗎基公?迭代器不是應(yīng)該是一個(gè)對(duì)象嗎幅慌?為什么這里生成器是一個(gè)函數(shù)也符合這個(gè)協(xié)議?
其實(shí)大家不用懷疑轰豆,因?yàn)樯善魉仁且粋€(gè)函數(shù)也是一個(gè)對(duì)象胰伍。
// 定義一個(gè)生成器
let aGeneratorObject = function* (){
yield 1;
yield 2;
yield 3;
}();
typeof aGeneratorObject.next;
// 返回"function", 他有一個(gè)next方法齿诞,說明他符合迭代器協(xié)議
typeof aGeneratorObject[Symbol.iterator];
// 返回"function", 有一個(gè)@@iterator方法,說明生成器本身也是可迭代對(duì)象
aGeneratorObject[Symbol.iterator]() === aGeneratorObject;
// 返回true, 因?yàn)锧@iterator方法返回自身(即迭代器)骂租,說明這是一個(gè)格式良好的可迭代對(duì)象
[...aGeneratorObject];
// 返回[1, 2, 3]
console.log(Symbol.iterator in aGeneratorObject)
// 返回true, 因?yàn)锧@iterator方法是aGeneratorObject的一個(gè)屬性
復(fù)制代碼
總結(jié)
今天我們掌握了JavaScript中的迭代器協(xié)議祷杈,知道了迭代器與可迭代對(duì)象的關(guān)系。并手動(dòng)實(shí)現(xiàn)了自定義迭代器的效果菩咨。希望可以幫助大家對(duì)迭代器有深入的了解吠式,并在開發(fā)中運(yùn)用起來。
參考
developer.mozilla.org/zh-CN/docs/…