迭代器

迭代器的概念

迭代器就是一種接口音比,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機(jī)制隧哮。任何的數(shù)據(jù)結(jié)構(gòu)只要部署了Inerator接口座舍,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)沮翔,所有的迭代器都有一個next方法,該方法會返回一個對象鉴竭,表示當(dāng)前數(shù)據(jù)成員的信息岸浑。這個對象具有valuedone兩個屬性矢洲,value屬性返回當(dāng)前位置的成員缩焦,done屬性是一個布爾值,表示遍歷是否結(jié)束盖桥,即是否還有必要再一次調(diào)用next方法揩徊。

迭代器的主要作用有三個:

  • 為各種數(shù)據(jù)結(jié)構(gòu)提供一個統(tǒng)一的嵌赠、簡便的訪問接口;
  • 使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列齿税;
  • ES6創(chuàng)造了一種新的遍歷命令for...of循環(huán)凌箕,Inerator接口主要供for...of消費(fèi)

迭代器的遍歷過程是這樣的:

  1. 創(chuàng)建一個指針對象词渤,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置掖肋。
  2. 第一次調(diào)用指針對象的next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員
  3. 第二次調(diào)用指針對象的next方法沿盅,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第二個成員
  4. 不斷調(diào)用指針對象的next方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置

每一次調(diào)用next方法韧掩,都會返回?cái)?shù)據(jù)結(jié)構(gòu)的當(dāng)前成員信息疗锐,具體來說费彼,就是返回一個包含valuedone兩個屬性的對象,其中value是當(dāng)前成員的值雇卷,而done是一個布爾值关划,表示遍歷是否結(jié)束翘瓮。

var it = makeIterator(['1', '2','3']);

it.next() // { value: "1", done: false }
it.next() // { value: "2", done: false }
it.next() // { value: '3', done: false }
it.next() // { value: undefined, done: true}
it.next() // { value: undefined, done: true}

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true};
    }
  };
}

如果是使用TypeScript的寫法,迭代器接口(Iterable)调榄、指針對象(Iterator)和next方法返回值的規(guī)格如下:

interface Iterable {
  [Symbol.iterator]() : Iterator,
}

interface Iterator {
  next(value?: any) : IterationResult,
}

interface IterationResult {
  value: any,
  done: boolean,
}

默認(rèn)Iterator接口

一種數(shù)據(jù)結(jié)構(gòu)只要部署了Iterator接口振峻,我們就稱這種數(shù)據(jù)結(jié)構(gòu)是“可遍歷的”择份,默認(rèn)的Iterator接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性荣赶,或者說一個數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性,就可以認(rèn)為是可遍歷的利诺。

原生具備Iterator接口的數(shù)據(jù)結(jié)構(gòu)如下:

Array/Map/Set/String/TypedArray/函數(shù)的arguments對象/NodeList對象

實(shí)例(數(shù)組的Symbol.iterator屬性)

let arr = ['1','2','3'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: "1", done: false }
iter.next() // { value: "2", done: false }
iter.next() // { value: '3', done: false }
iter.next() // { value: undefined, done: true}
iter.next() // { value: undefined, done: true}

上面的代碼中慢逾,變量arr是一個數(shù)組,原生就具有遍歷器接口口注,部署在arrSymbol.iterator屬性上面寝志,所以調(diào)用這個屬性策添,便可以得到迭代器對象。

調(diào)用Iterator接口的場合

  • 解構(gòu)賦值

    對數(shù)組和Set結(jié)構(gòu)進(jìn)行解構(gòu)賦值時乐导,會默認(rèn)調(diào)用Symbol.iterator方法

    let set = new Set().add('a').add('b').add('c');
    
    let [x,y] = set;
    // x='a'; y='b'
    
    let [first, ...rest] = set;
    // first='a'; rest=['b','c'];
    
  • 擴(kuò)展運(yùn)算符

    let arr = ['b', 'c'];
    ['a', ...arr, 'd']
    // ['a', 'b', 'c', 'd']
    
  • yield*

    yield*后面跟的是一個可遍歷的結(jié)構(gòu)兽叮,它會調(diào)用該結(jié)構(gòu)的遍歷器接口

    let generator = function* () {
      yield 1;
      yield* [2,3,4];
      yield 5;
    };
    
    var iterator = generator();
    
    iterator.next() // { value: 1, done: false }
    iterator.next() // { value: 2, done: false }
    iterator.next() // { value: 3, done: false }
    iterator.next() // { value: 4, done: false }
    iterator.next() // { value: 5, done: false }
    iterator.next() // { value: undefined, done: true }
    
  • 其他場合

    由于數(shù)組的遍歷會調(diào)用遍歷器接口猾愿,所以任何接受數(shù)組為參數(shù)的場合账阻,其實(shí)都調(diào)用了遍歷器接口淘太,如:

    • for...of
    • Array.from()
    • Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]])
    • Promise.all()
    • Promise.race()

Iterator接口與Generator函數(shù)

Symbol.iterator方法的最簡單實(shí)現(xiàn)蒲牧,還是使用生成器函數(shù)(Generator)

var arr = {};

arr[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...arr]   //[1,2,3]

let obj = {
    * [Symbol.iterator](){
    yield 'hello';
    yield 'world';
}
}

for(let x of obj) {
   console.log(x)
}

上述實(shí)例中冰抢,Symbol.iterator方法幾乎不用部署任何代碼,只要用yield命令給出每一步的返回值即可翠订。

迭代器對象的return(), throw()

迭代器除了有next方法尽超,還可以具有returnthrow方法梧躺,如果自定義迭代器對象生成函數(shù),那么next方法是必須部署的巩踏。

return方法的使用場合是蛀缝,如果for...of循環(huán)提前退出(通常是因?yàn)槌鲥e屈梁,或者有break語句或continue語句),就會調(diào)用return方法煞抬。如果一個對象在完成遍歷前革答,需要清理或釋放資源曙强,就可以部署return方法碟嘴。

function readLinesSync(file) {
  return {
    next() {
      return { done: false };
    },
    return() {
      file.close();
      return { done: true };
    },
  };
}

上面代碼中娜扇,函數(shù)readLinesSync接受一個文件對象作為參數(shù),返回一個遍歷器對象枢析,其中除了next方法刃麸,還部署了return方法嫌蚤。下面的三種情況脱吱,都會觸發(fā)執(zhí)行return方法。

// 情況一
for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;
}

// 情況二
for (let line of readLinesSync(fileName)) {
  console.log(line);
  continue;
}

// 情況三
for (let line of readLinesSync(fileName)) {
  console.log(line);
  throw new Error();
}

上面代碼中续捂,情況一輸出文件的第一行以后牙瓢,就會執(zhí)行return方法矾克,關(guān)閉這個文件;情況二輸出所有行以后酒繁,執(zhí)行return方法州袒,關(guān)閉該文件弓候;情況三會在執(zhí)行return方法關(guān)閉文件之后菇存,再拋出錯誤。

注意陈惰,return方法必須返回一個對象,這是 Generator 規(guī)格決定的关筒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒸播,一起剝皮案震驚了整個濱河市萍肆,隨后出現(xiàn)的幾起案子塘揣,更是在濱河造成了極大的恐慌,老刑警劉巖才写,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞草,死亡現(xiàn)場離奇詭異,居然都是意外死亡洲守,警方通過查閱死者的電腦和手機(jī)沾凄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門婴削,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牙肝,“玉大人配椭,你說我怎么就攤上這事『饫悖” “怎么了瘾境?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵迷守,是天一觀的道長旺入。 經(jīng)常有香客問我茵瘾,道長拗秘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任行瑞,我火速辦了婚禮餐禁,結(jié)果婚禮上帮非,老公的妹妹穿的比我還像新娘。我一直安慰自己筑舅,他們只是感情好翠拣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布误墓。 她就那樣靜靜地躺著益缎,像睡著了一般莺奔。 火紅的嫁衣襯著肌膚如雪令哟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天驳癌,我揣著相機(jī)與錄音,去河邊找鬼典予。 笑死乐严,一個胖子當(dāng)著我的面吹牛昂验,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播占婉,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼酌予,長吁一口氣:“原來是場噩夢啊……” “哼奖慌!你這毒婦竟也來了简僧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谅海,沒想到半個月后蹦浦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年枫吧,在試婚紗的時候發(fā)現(xiàn)自己被綠了九杂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片例隆。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡镀层,死狀恐怖皿曲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坞古,我是刑警寧澤绸贡,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布听怕,位于F島的核電站尿瞭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏黑竞。R本人自食惡果不足惜疏旨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一遏匆、第九天 我趴在偏房一處隱蔽的房頂上張望谁榜。 院中可真熱鬧窃植,春花似錦、人聲如沸葛超。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豫缨。三九已至,卻和暖如春燃箭,著一層夾襖步出監(jiān)牢的瞬間舍败,已是汗流浹背邻薯。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工厕诡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壹罚。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓猖凛,卻偏偏與公主長得像辨泳,于是被迫代替她去往敵國和親霞幅。 傳聞我的和親對象是個殘疾皇子司恳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容