從Iterator到map、set和Generator

在ES6的教程里先說了Map和Set再說Iteratator裸违,盡管我們理解遍歷的意思是什么扔嵌,但是學(xué)習(xí)Iterator的原理和結(jié)構(gòu)有助于我們進(jìn)一步理解有應(yīng)用到遍歷器的語法限府。

Iterator原理

Iterator 的遍歷過程是這樣的。
(1)創(chuàng)建一個指針對象痢缎,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置胁勺。也就是說,遍歷器對象本質(zhì)上独旷,就是一個指針對象署穗。
(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é)束位置褐啡。

默認(rèn)的Iterator

存在這樣的一個默認(rèn)接口:如果一個數(shù)據(jù)結(jié)構(gòu)擁有屬性Symbol.iterator,則這個數(shù)據(jù)結(jié)構(gòu)是可遍歷的鳖昌。屬性Symbol.iterator對應(yīng)一個函數(shù)备畦,該函數(shù)規(guī)定了遍歷的規(guī)則。執(zhí)行這個函數(shù)许昨,就會返回一個遍歷器懂盐。

const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};

該例子中常量obj是可遍歷的,因?yàn)樗鼡碛蠸ymbol.iterator屬性糕档,該屬性對應(yīng)一個函數(shù)莉恼,函數(shù)返回一個擁有next屬性(對應(yīng)一個返回value和done的函數(shù))的對象。所以當(dāng)我們調(diào)用obj.next()的時候,就會返回對應(yīng)的value和done的值类垫。

原生Iterator

以下是原生擁有Iterator接口的數(shù)據(jù)結(jié)構(gòu):
Array
Map
Set
String
TypedArray
函數(shù)的 arguments 對象
NodeList 對象
對于原生部署 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)司光,不用自己寫遍歷器生成函數(shù),for...of循環(huán)會自動遍歷它們悉患。除此之外残家,其他數(shù)據(jù)結(jié)構(gòu)(主要是對象)的 Iterator 接口,都需要自己在Symbol.iterator屬性上面部署(部署Symbol.iterator的函數(shù)售躁,還有next函數(shù))坞淮,這樣才會被for...of循環(huán)遍歷。部署還有一種取巧的方法陪捷,就是類似數(shù)組的對象可以直接借用Array.prototype[Symbol.iterator]回窘。

return方法和throw方法

return方法和throw方法在部署中是可選的。
return方法針對以下情況執(zhí)行:
在for...of循環(huán)中市袖,
1.出錯 2.break語句 3.continue語句

for...of和for...in

for...in循環(huán)啡直,只能獲得對象的鍵名,不能直接獲取鍵值苍碟。ES6 提供for...of循環(huán)酒觅,允許遍歷獲得鍵值。

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

for...of循環(huán)調(diào)用遍歷器接口微峰,數(shù)組的遍歷器接口只返回具有數(shù)字索引的屬性舷丹。這一點(diǎn)跟for...in循環(huán)也不一樣。

let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); //  "3", "5", "7"
}

iterator的使用

iterator主要應(yīng)用于Generator蜓肆,但在此之前還要溫習(xí)一下具備原生接口的Set和Map颜凯。

Set和weakset

Set

創(chuàng)建Set
const set = new Set()
也可以傳入數(shù)組初始化set
const set = new Set([1,2,3,4])
set里面沒有重復(fù)的值,傳入重復(fù)的值會被排除掉仗扬。

Set 實(shí)例的屬性和方法

Set 結(jié)構(gòu)的實(shí)例有以下屬性症概。
Set.prototype.constructor:構(gòu)造函數(shù),默認(rèn)就是Set函數(shù)厉颤。
Set.prototype.size:返回Set實(shí)例的成員總數(shù)穴豫。
操作方法:
add(value):添加某個值,返回 Set 結(jié)構(gòu)本身逼友。
delete(value):刪除某個值精肃,返回一個布爾值,表示刪除是否成功帜乞。
has(value):返回一個布爾值司抱,表示該值是否為Set的成員。
clear():清除所有成員黎烈,沒有返回值习柠。
遍歷方法:
keys():返回鍵名的遍歷器
values():返回鍵值的遍歷器
entries():返回鍵值對的遍歷器
forEach():使用回調(diào)函數(shù)遍歷每個成員

Weakset

const ws = new WeakSet();
WeakSet 可以接受一個數(shù)組或類似數(shù)組的對象作為參數(shù)匀谣。(實(shí)際上,任何具有 Iterable 接口的對象资溃,都可以作為 WeakSet 的參數(shù)武翎。)該數(shù)組的所有成員,都會自動成為 WeakSet 實(shí)例對象的成員溶锭。

WeakSet.prototype.add(value):向 WeakSet 實(shí)例添加一個新成員宝恶。
WeakSet.prototype.delete(value):清除 WeakSet 實(shí)例的指定成員。
WeakSet.prototype.has(value):返回一個布爾值趴捅,表示某個值是否在

WeakSet 沒有size屬性垫毙,沒有辦法遍歷它的成員。

Map和weakmap

Map

Map是一種具有鍵值對的數(shù)據(jù)結(jié)構(gòu)拱绑,相對于Object來說综芥,它的優(yōu)點(diǎn)是鍵的范圍不限于字符串,各種類型的值(包括對象)都可以當(dāng)作鍵猎拨。也就是說膀藐,Object 結(jié)構(gòu)提供了“字符串-值”的對應(yīng),Map 結(jié)構(gòu)提供了“值-值”的對應(yīng)迟几。

const m = new Map();
const o = {p: 'Hello World'};
//Map { { p: 'hello world' } => 'content' }
m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

賦值應(yīng)用:任何具有 Iterator 接口消请、且每個成員都是一個雙元素的數(shù)組的數(shù)據(jù)結(jié)構(gòu)(詳見《Iterator》一章)都可以當(dāng)作Map構(gòu)造函數(shù)的參數(shù)。這就是說类腮,Set和Map都可以用來生成新的 Map。

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3

如果對同一個鍵多次賦值蛉加,后面的值將覆蓋前面的值蚜枢。

const map = new Map();

map
.set(1, 'aaa')
.set(1, 'bbb');

map.get(1) // "bbb"

上面代碼對鍵1連續(xù)賦值兩次,后一次的值覆蓋前一次的值针饥。

如果讀取一個未知的鍵厂抽,則返回undefined。

new Map().get('asfddfsasadf')
// undefined

Map 的鍵實(shí)際上是跟內(nèi)存地址綁定的丁眼,只要內(nèi)存地址不一樣筷凤,就視為兩個鍵。如果 Map 的鍵是一個簡單類型的值(數(shù)字苞七、字符串藐守、布爾值),則只要兩個值嚴(yán)格相等蹂风,Map 將其視為一個鍵卢厂,比如0和-0就是一個鍵,布爾值true和字符串true則是兩個不同的鍵惠啄。另外慎恒,undefined和null也是兩個不同的鍵任内。雖然NaN不嚴(yán)格相等于自身,但 Map 將其視為同一個鍵融柬。

實(shí)例屬性和方法

1.size屬性返回 Map 結(jié)構(gòu)的成員總數(shù)死嗦。
2.set(key, value) set方法返回的是當(dāng)前的Map對象,因此可以采用鏈?zhǔn)綄懛ā?br> 3.get(key) 如果找不到key粒氧,返回undefined越除。
4.has(key)
5.delete(key)刪除某個鍵,返回true靠欢。如果刪除失敗廊敌,返回false
6.clear()清除所有成員,沒有返回值门怪。
遍歷方法:
keys():返回鍵名的遍歷器骡澈。
values():返回鍵值的遍歷器。
entries():返回所有成員的遍歷器掷空。
forEach():遍歷 Map 的所有成員肋殴。

Weakmap

首先,WeakMap只接受對象作為鍵名(null除外)坦弟,不接受其他類型的值作為鍵名护锤。
其次,WeakMap的鍵名所指向的對象酿傍,不計(jì)入垃圾回收機(jī)制烙懦。
只要所引用的對象的其他引用都被清除,垃圾回收機(jī)制就會釋放該對象所占用的內(nèi)存赤炒。也就是說氯析,一旦不再需要,WeakMap 里面的鍵名對象和所對應(yīng)的鍵值對會自動消失莺褒,不用手動刪除引用掩缓。
垃圾回收機(jī)制以后再寫一篇筆記說明。
WeakMap只有四個方法可用:get()遵岩、set()你辣、has()、delete()尘执。
沒有遍歷機(jī)制和size屬性舍哄。

Generator

終于講到了Generator。
Generator可以看作是一個封裝了多個內(nèi)部狀態(tài)的函數(shù)正卧,這些狀態(tài)是可遍歷的蠢熄。執(zhí)行Generator返回一個遍歷器。
形式上炉旷,Generator 函數(shù)是一個普通函數(shù)签孔,但是有兩個特征然想。一是绪抛,function關(guān)鍵字與函數(shù)名之間有一個星號肤舞;二是雅潭,函數(shù)體內(nèi)部使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

Generator 函數(shù)是分段執(zhí)行的但绕,yield表達(dá)式是暫停執(zhí)行的標(biāo)記救崔,而next方法可以恢復(fù)執(zhí)行。
Generator 函數(shù)可以不用yield表達(dá)式捏顺,這時就變成了一個單純的暫緩執(zhí)行函數(shù)六孵。

function* f() {
  console.log('執(zhí)行了!')
}

var generator = f();

setTimeout(function () {
  generator.next()
}, 2000);

注意的地方:
1.yield表達(dá)式只能用在 Generator 函數(shù)里面幅骄,用在其他地方都會報(bào)錯劫窒。
2.yield表達(dá)式如果用在另一個表達(dá)式之中,必須放在圓括號里面拆座。
3.yield表達(dá)式用作函數(shù)參數(shù)或放在賦值表達(dá)式的右邊主巍,可以不加括號。

next()帶參數(shù)

next方法的參數(shù)表示上一個yield表達(dá)式的返回值(所以在第一次使用next方法時挪凑,傳遞參數(shù)是無效的)

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
for...of
function *foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

從例子可以看出孕索,當(dāng)done為true時,for..of停止遍歷并且不返回當(dāng)前值躏碳。
除了for...of循環(huán)以外搞旭,擴(kuò)展運(yùn)算符(...)、解構(gòu)賦值和Array.from方法內(nèi)部調(diào)用的菇绵,都是遍歷器接口选脊。這意味著,它們都可以將 Generator 函數(shù)返回的 Iterator 對象脸甘,作為參數(shù)。

function* numbers () {
  yield 1
  yield 2
  return 3
  yield 4
}

// 擴(kuò)展運(yùn)算符
[...numbers()] // [1, 2]

// Array.from 方法
Array.from(numbers()) // [1, 2]

// 解構(gòu)賦值
let [x, y] = numbers();
x // 1
y // 2

// for...of 循環(huán)
for (let n of numbers()) {
  console.log(n)
}
// 1
// 2
return()和throw()

throw方法可以接受一個參數(shù)偏灿,該參數(shù)會被catch語句接收丹诀。
在generator內(nèi)如果有try...catch,那么generator的錯誤會被捕捉到一次。當(dāng)內(nèi)部已經(jīng)捕獲到錯誤翁垂,后繼的錯誤就由外部處理或者直接中斷程序铆遭。
throw方法被捕獲以后,會附帶執(zhí)行下一條yield表達(dá)式沿猜。也就是說枚荣,會附帶執(zhí)行一次next方法。

return方法啼肩,可以返回給定的值橄妆,并且終結(jié)遍歷 Generator 函數(shù)衙伶。如果return方法調(diào)用時,不提供參數(shù)害碾,則返回值的value屬性為undefined矢劲。

generator的this指向哪里

如果不作任何處理,在generator實(shí)例內(nèi)定義一個this.a=1慌随,this指向的是全局芬沉。
如果希望generator可以兼?zhèn)鋘ext方法和正常的this指向,有兩個方法
1阁猜,使用call或者apply綁定一個空對象
2丸逸,綁定generator函數(shù)的prototype

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市剃袍,隨后出現(xiàn)的幾起案子黄刚,更是在濱河造成了極大的恐慌,老刑警劉巖笛园,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隘击,死亡現(xiàn)場離奇詭異,居然都是意外死亡研铆,警方通過查閱死者的電腦和手機(jī)埋同,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棵红,“玉大人凶赁,你說我怎么就攤上這事∧嫣穑” “怎么了虱肄?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長交煞。 經(jīng)常有香客問我咏窿,道長,這世上最難降的妖魔是什么素征? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任集嵌,我火速辦了婚禮,結(jié)果婚禮上御毅,老公的妹妹穿的比我還像新娘根欧。我一直安慰自己,他們只是感情好端蛆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布凤粗。 她就那樣靜靜地躺著,像睡著了一般今豆。 火紅的嫁衣襯著肌膚如雪嫌拣。 梳的紋絲不亂的頭發(fā)上柔袁,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音亭罪,去河邊找鬼瘦馍。 笑死,一個胖子當(dāng)著我的面吹牛应役,可吹牛的內(nèi)容都是我干的情组。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼箩祥,長吁一口氣:“原來是場噩夢啊……” “哼院崇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袍祖,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤底瓣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蕉陋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捐凭,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年凳鬓,在試婚紗的時候發(fā)現(xiàn)自己被綠了茁肠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡缩举,死狀恐怖垦梆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仅孩,我是刑警寧澤托猩,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站辽慕,受9級特大地震影響京腥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溅蛉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一绞旅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧温艇,春花似錦、人聲如沸堕汞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讯检。三九已至琐鲁,卻和暖如春卫旱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背围段。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工顾翼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奈泪。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓适贸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涝桅。 傳聞我的和親對象是個殘疾皇子拜姿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

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

  • 1.Set 基本用法 ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set。它類似于數(shù)組冯遂,但是成員的值都是唯一的蕊肥,沒有重復(fù)的值。Set本...
    雨飛飛雨閱讀 1,850評論 0 7
  • 簡介 基本概念 Generator函數(shù)是ES6提供的一種異步編程解決方案蛤肌,語法行為與傳統(tǒng)函數(shù)完全不同壁却。本章詳細(xì)介紹...
    呼呼哥閱讀 1,075評論 0 4
  • 在此處先列下本篇文章的主要內(nèi)容 簡介 next方法的參數(shù) for...of循環(huán) Generator.prototy...
    醉生夢死閱讀 1,440評論 3 8
  • [TOC] 參考阮一峰的ECMAScript 6 入門參考深入淺出ES6 let和const let和const都...
    郭子web閱讀 1,781評論 0 1
  • 很喜歡keep上的一句話,自律給我自由裸准。也是因?yàn)檫@句話展东,我才下載了它。 到目前為止狼速,我看過不少關(guān)于自律的文章琅锻。然,...
    houpanpan926閱讀 296評論 7 7