ES6學(xué)習(xí)筆記(17)之 Iterator 和 for...of 循環(huán)

參考:ECMAScript 6 入門

簡單介紹:

我的理解:所謂可遍歷杭朱,就是可以取到下一個(gè)纽疟。重點(diǎn)在于實(shí)現(xiàn)一個(gè)可以取到下一個(gè)對象的next方法

首先巧颈,Iterator適合于容器對象辜纲,既可以存儲其它對象的對象,比如:Array, Map, Set等绞绒。
其內(nèi)部通過next方法可以不斷獲取存儲的里面的下一個(gè)對象婶希,實(shí)現(xiàn)了這樣的next方法的對象便是可遍歷的。
嚴(yán)謹(jǐn)一點(diǎn)蓬衡,一種數(shù)據(jù)結(jié)構(gòu)只要部署了 Iterator 接口喻杈,我們就稱這種數(shù)據(jù)結(jié)構(gòu)是“可遍歷的”(iterable)。

Iterator接口應(yīng)該長什么樣兒的狰晚?使用TypeScript的寫法如下:

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

interface Iterator {
  // next方法是重點(diǎn)筒饰,通過next方法來獲取下一個(gè)對象
  next(value?: any) : IterationResult,
}

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

ES6 創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費(fèi)家肯。當(dāng)使用for...of循環(huán)遍歷某種數(shù)據(jù)結(jié)構(gòu)時(shí)龄砰,該循環(huán)會自動去尋找 Iterator 接口盟猖。一個(gè)數(shù)據(jù)結(jié)構(gòu)只要部署了Symbol.iterator屬性讨衣,就被視為具有 iterator 接口换棚,就可以用for...of循環(huán)遍歷它的成員。也就是說反镇,for...of循環(huán)內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator方法固蚤。

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

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

這些數(shù)據(jù)結(jié)構(gòu)原生部署了Symbol.iterator屬性

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator](); //調(diào)用Array對象的Symbol.iterato屬性方法生成遍歷器

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
Screen Shot 2019-09-09 at 3.45.01 PM.png

除了for...of,還有哪些是使用iterator的變種(請注意歹茶,Object對象默認(rèn)沒有部署 Iterator 接口)

  • 解構(gòu)賦值:對數(shù)組和 Set 結(jié)構(gòu)進(jìn)行解構(gòu)賦值時(shí)夕玩,會默認(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)算符:擴(kuò)展運(yùn)算符(...)也會調(diào)用默認(rèn)的 Iterator 接口惊豺。
// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
  • yield:yield后面跟的是一個(gè)可遍歷的結(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í)現(xiàn)了 Iterator 接口(我的理解尸昧,字符串本質(zhì)上就是一個(gè)char數(shù)組揩页,所以它可以被遍歷也是情理之中的了)
var someString = "hi";
var iterator = someString[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }
iterator.next()  // { value: "i", done: false }
iterator.next()  // { value: undefined, done: true }

使用 Generator 函數(shù)實(shí)現(xiàn)遍歷接口

let myIterable = {
  [Symbol.iterator]: function* () {
    yield 1; // yield 是產(chǎn)出的意思
    yield 2;
    yield 3;
  }
}
[...myIterable] // [1, 2, 3]

// 或者采用下面的簡潔寫法

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

for (let x of obj) {
  console.log(x);
}
// "hello"
// "world"

其它遍歷器對象:

let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
  console.log(pair);
}
// ['a', 1]
// ['b', 2]

像上面的例子,map應(yīng)該調(diào)用了它默認(rèn)的遍歷生成器烹俗。如果我只想遍歷map的key或者value怎么辦呢爆侣?
答案是通過:entries(), keys(), values()等。

ES6 的數(shù)組幢妄、Set兔仰、Map 都部署了以下三個(gè)方法,調(diào)用后都返回遍歷器對象蕉鸳。

  • entries() 返回一個(gè)遍歷器對象乎赴,用來遍歷并返回[鍵名, 鍵值]組成的數(shù)組。對于數(shù)組潮尝,鍵名就是索引值无虚;對于 Set,鍵名與鍵值相同衍锚。Map 結(jié)構(gòu)的 Iterator 接口友题,默認(rèn)就是調(diào)用entries方法。
  • keys() 返回一個(gè)遍歷器對象戴质,用來遍歷所有的鍵名度宦。
  • values() 返回一個(gè)遍歷器對象,用來遍歷所有的鍵值告匠。

類數(shù)組對象舉例:

let arrayLike = { length: 2, 0: 'a', 1: 'b' };//類數(shù)組對象

// 報(bào)錯
for (let x of arrayLike) {
  console.log(x);
}

// 正確戈抄,先使用Array.from將其轉(zhuǎn)為帶 Iterator 接口的數(shù)組
for (let x of Array.from(arrayLike)) {
  console.log(x);
}

無法中途跳出forEach循環(huán),break命令或return命令都不能奏效后专。


非重點(diǎn):

遍歷器對象的 return()划鸽,throw()
遍歷器對象除了具有next方法,還可以具有return方法和throw方法。如果你自己寫遍歷器對象生成函數(shù)裸诽,那么next方法是必須部署的嫂用,return方法和throw方法是否部署是可選的。
return方法的使用場合是丈冬,如果for...of循環(huán)提前退出(通常是因?yàn)槌鲥e嘱函,或者有break語句),就會調(diào)用return方法埂蕊。如果一個(gè)對象在完成遍歷前往弓,需要清理或釋放資源,就可以部署return方法蓄氧。

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

上面代碼中函似,函數(shù)readLinesSync接受一個(gè)文件對象作為參數(shù),返回一個(gè)遍歷器對象喉童,其中除了next方法缴淋,還部署了return方法。下面的兩種情況泄朴,都會觸發(fā)執(zhí)行return方法重抖。

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

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

上面代碼中,情況一輸出文件的第一行以后祖灰,就會執(zhí)行return方法钟沛,關(guān)閉這個(gè)文件;情況二會在執(zhí)行return方法關(guān)閉文件之后局扶,再拋出錯誤恨统。
注意,return方法必須返回一個(gè)對象三妈,這是 Generator 規(guī)格決定的畜埋。
throw方法主要是配合 Generator 函數(shù)使用,一般的遍歷器對象用不到這個(gè)方法畴蒲。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悠鞍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子模燥,更是在濱河造成了極大的恐慌咖祭,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔫骂,死亡現(xiàn)場離奇詭異么翰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辽旋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門浩嫌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檐迟,“玉大人,你說我怎么就攤上這事码耐∽烦伲” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵伐坏,是天一觀的道長。 經(jīng)常有香客問我握联,道長桦沉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任金闽,我火速辦了婚禮纯露,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘代芜。我一直安慰自己埠褪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布挤庇。 她就那樣靜靜地躺著钞速,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嫡秕。 梳的紋絲不亂的頭發(fā)上渴语,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機(jī)與錄音昆咽,去河邊找鬼驾凶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掷酗,可吹牛的內(nèi)容都是我干的调违。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼泻轰,長吁一口氣:“原來是場噩夢啊……” “哼技肩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浮声,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤亩鬼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后阿蝶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雳锋,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年羡洁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玷过。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辛蚊,靈堂內(nèi)的尸體忽然破棺而出粤蝎,到底是詐尸還是另有隱情,我是刑警寧澤袋马,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布初澎,位于F島的核電站,受9級特大地震影響虑凛,放射性物質(zhì)發(fā)生泄漏碑宴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一桑谍、第九天 我趴在偏房一處隱蔽的房頂上張望延柠。 院中可真熱鬧,春花似錦锣披、人聲如沸贞间。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽增热。三九已至,卻和暖如春胧辽,著一層夾襖步出監(jiān)牢的瞬間钓葫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工票顾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留础浮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓奠骄,卻偏偏與公主長得像豆同,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子含鳞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

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

  • Iterator(遍歷器)的概念 JavaScript原有的表示“集合”的數(shù)據(jù)結(jié)構(gòu)影锈,主要是數(shù)組和對象,ES6又添加...
    oWSQo閱讀 615評論 0 1
  • 一蝉绷、概念 遍歷器(Iterator)主要就是為了向JavaScript中的數(shù)組(Array)和對象(Object)...
    HalShaw閱讀 388評論 0 0
  • Iterator(遍歷器)的概念 默認(rèn) Iterator 接口 調(diào)用 Iterator 接口的場合 字符串的 It...
    Android_馮星閱讀 297評論 0 0
  • 一鸭廷、Iterator(遍歷器)的概念 Iterator 是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機(jī)制熔吗。 任何...
    magic_pill閱讀 314評論 0 1
  • 電影《閨蜜2》終于上映了桅狠,電影還沒去看讼载,看了預(yù)告片就很燃轿秧,和自己的好閨蜜,一起冒險(xiǎn)咨堤、喝酒菇篡、旅行、大戰(zhàn)壞人一喘,其中會發(fā)...
    旅行祈禱與愛閱讀 21,362評論 5 11