深入理解ES6的迭代器與生成器
本文介紹了深入理解ES6的迭代器與生成器,分享給大家,具體如下:
循環(huán)語句的問題
var colors = ["red", "green", "blue"]; for(var i=0; i<colors.length; i++){ console.log(colors[i]); }
在ES6之前,這種標(biāo)準(zhǔn)的for循環(huán),通過變量來跟蹤數(shù)組的索引。如果多個(gè)循環(huán)嵌套就需要追蹤多個(gè)變量,代碼復(fù)雜度會(huì)大大增加,也容易產(chǎn)生錯(cuò)用循環(huán)變量的bug。
迭代器的出現(xiàn)旨在消除這種復(fù)雜性并減少循環(huán)中的錯(cuò)誤。
什么是迭代器
我們先感受一下用ES5語法模擬創(chuàng)建一個(gè)迭代器:
function createIterator(items) { var i = 0; return { // 返回一個(gè)迭代器對(duì)象 next: function() { // 迭代器對(duì)象一定有個(gè)next()方法 var done = (i >= items.length); var value = !done ? items[i++] : undefined; return { // next()方法返回結(jié)果對(duì)象 value: value, done: done }; } }; } var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false}" console.log(iterator.next()); // "{ value: 2, done: false}" console.log(iterator.next()); // "{ value: 3, done: false}" console.log(iterator.next()); // "{ value: undefiend, done: true}" // 之后所有的調(diào)用都會(huì)返回相同內(nèi)容 console.log(iterator.next()); // "{ value: undefiend, done: true}"
以上,我們通過調(diào)用createIterator()函數(shù),返回一個(gè)對(duì)象,這個(gè)對(duì)象存在一個(gè)next()方法,當(dāng)next()方法被調(diào)用時(shí),返回格式{ value: 1, done: false}的結(jié)果對(duì)象。
因此,我們可以這么定義:迭代器是一個(gè)擁有next()方法的特殊對(duì)象,每次調(diào)用next()都返回一個(gè)結(jié)果對(duì)象。
借助這個(gè)迭代器對(duì)象,我們來改造剛開始那個(gè)標(biāo)準(zhǔn)的for循環(huán)【暫時(shí)先忘記ES6的for-of循環(huán)新特性】:
var colors = ["red", "green", "blue"]; var iterator = createIterator(colors); while(!iterator.next().done){ console.log(iterator.next().value); }
what?,消除循環(huán)變量而已,需要搞這么麻煩,代碼上不是得不償失了嗎?
并非如此,畢竟createIterator()只需寫一次,就可以一直復(fù)用。不過ES6引入了生成器對(duì)象,可以讓創(chuàng)建迭代器的過程變得更加簡(jiǎn)單。
什么是生成器
生成器是一種返回迭代器的函數(shù),通過function關(guān)鍵字后的星號(hào)(*)來表示,函數(shù)中會(huì)用到新的關(guān)鍵字yield。
function *createIterator(items) { for(let i=0; i<items.length; i++) { yield items[i]; } } let iterator = createIterator([1, 2, 3]); // 既然生成器返回的是迭代器,自然就可以調(diào)用迭代器的next()方法 console.log(iterator.next()); // "{ value: 1, done: false}" console.log(iterator.next()); // "{ value: 2, done: false}" console.log(iterator.next()); // "{ value: 3, done: false}" console.log(iterator.next()); // "{ value: undefiend, done: true}" // 之后所有的調(diào)用都會(huì)返回相同內(nèi)容 console.log(iterator.next()); // "{ value: undefiend, done: true}"
上面,我們用ES6的生成,大大簡(jiǎn)化了迭代器的創(chuàng)建過程。我們給生成器函數(shù)createIterator()傳入一個(gè)items數(shù)組,函數(shù)內(nèi)部,for循環(huán)不斷從數(shù)組中生成新的元素放入迭代器中,每遇到一個(gè)yield語句循環(huán)都會(huì)停止;每次調(diào)用迭代器的next()方法,循環(huán)便繼續(xù)運(yùn)行并停止在下一條yield語句處。
生成器的創(chuàng)建方式
生成器是個(gè)函數(shù):
function *createIterator(items) { ... }
可以用函數(shù)表達(dá)式方式書寫:
let createIterator = function *(item) { ... }
也可以添加到對(duì)象中,ES5風(fēng)格對(duì)象字面量:
let o = { createIterator: function *(items) { ... } }; let iterator = o.createIterator([1, 2, 3]);
ES6風(fēng)格的對(duì)象方法簡(jiǎn)寫方式:
let o = { *createIterator(items) { ... } }; let iterator = o.createIterator([1, 2, 3]);
可迭代對(duì)象
在ES6中,所有的集合對(duì)象(數(shù)組、Set集合及Map集合)和字符串都是可迭代對(duì)象,可迭代對(duì)象都綁定了默認(rèn)的迭代器。
來了來了,姍姍來遲的ES6循環(huán)新特性for-of:
var colors = ["red", "green", "blue"]; for(let color of colors){ console.log(color); }
for-of循環(huán),可作用在可迭代對(duì)象上,正是利用了可迭代對(duì)象上的默認(rèn)迭代器。大致過程是:for-of循環(huán)每執(zhí)行一次都會(huì)調(diào)用可迭代對(duì)象的next()方法,并將迭代器返回的結(jié)果對(duì)象的value屬性存儲(chǔ)在變量中,循環(huán)將繼續(xù)執(zhí)行這一過程直到返回對(duì)象的done屬性的值為true。
如果只需要迭代數(shù)組或集合中的值,用for-of循環(huán)代替for循環(huán)是個(gè)不錯(cuò)的選擇。
訪問默認(rèn)迭代器
可迭代對(duì)象,都有一個(gè)Symbol.iterator方法,for-of循環(huán)時(shí),通過調(diào)用colors數(shù)組的Symbol.iterator方法來獲取默認(rèn)迭代器的,這一過程是在JavaScript引擎背后完成的。
我們可以主動(dòng)獲取一下這個(gè)默認(rèn)迭代器來感受一下:
let values = [1, 2, 3]; let iterator = values[Symbol.iterator](); console.log(iterator.next()); // "{ value: 1, done: false}" console.log(iterator.next()); // "{ value: 2, done: false}" console.log(iterator.next()); // "{ value: 3, done: false}" console.log(iterator.next()); // "{ value: undefined, done: true}"
在這段代碼中,通過Symbol.iterator獲取了數(shù)組values的默認(rèn)迭代器,并用它遍歷數(shù)組中的元素。在JavaScript引擎中執(zhí)行for-of循環(huán)語句也是類似的處理過程。
用Symbol.iterator屬性來檢測(cè)對(duì)象是否為可迭代對(duì)象:
function isIterator(object) { return typeof object[Symbol.iterator] === "function"; } console.log(isIterable([1, 2, 3])); // true console.log(isIterable(new Set())); // true console.log(isIterable(new Map())); // true console.log(isIterable("Hello")); // true
創(chuàng)建可迭代對(duì)象
當(dāng)我們?cè)趧?chuàng)建對(duì)象時(shí),給Symbol.iterator屬性添加一個(gè)生成器,則可以將其變成可迭代對(duì)象:
let collection = { items: [], *[Symbol.iterator]() { // 將生成器賦值給對(duì)象的Symbol.iterator屬性來創(chuàng)建默認(rèn)的迭代器 for(let item of this.items) { yield item; } } }; collection.items.push(1); collection.items.push(2); collection.items.push(3); for(let x of collection) { console.log(x); }
內(nèi)建迭代器
ES6中的集合對(duì)象,數(shù)組、Set集合和Map集合,都內(nèi)建了三種迭代器:
- entries() 返回一個(gè)迭代器,其值為多個(gè)鍵值對(duì)。如果是數(shù)組,第一個(gè)元素是索引位置;如果是Set集合,第一個(gè)元素與第二個(gè)元素一樣,都是值。
- values() 返回一個(gè)迭代器,其值為集合的值。
- keys() 返回一個(gè)迭代器,其值為集合中的所有鍵名。如果是數(shù)組,返回的是索引;如果是Set集合,返回的是值(Set的值被同時(shí)用作鍵和值)。
不同集合的默認(rèn)迭代器
每個(gè)集合類型都有一個(gè)默認(rèn)的迭代器,在for-of循環(huán)中,如果沒有顯式指定則使用默認(rèn)的迭代器。按常規(guī)使用習(xí)慣,我們很容易猜到,數(shù)組和Set集合的默認(rèn)迭代器是values(),Map集合的默認(rèn)迭代器是entries()。
請(qǐng)看以下示例:
let colors = [ "red", "green", "blue"]; let tracking = new Set([1234, 5678, 9012]); let data = new Map(); data.set("title", "Understanding ECMAScript 6"); data.set("format", "print"); // 與調(diào)用colors.values()方法相同 for(let value of colors) { console.log(value); } // 與調(diào)用tracking.values()方法相同 for(let num of tracking) { console.log(num); } // 與調(diào)用data.entries()方法相同 for(let entry of data) { console.log(entry); }
這段代碼會(huì)輸入以下內(nèi)容:
"red"
"green"
"blue"
1234
5678
9012
["title", "Understanding ECMAScript 6"]
["format", "print"]
for-of循環(huán)配合解構(gòu)特性,操縱數(shù)據(jù)會(huì)更方便:
for(let [key, value] of data) { console.log(key + "=" + value); }
展開運(yùn)算符操縱可迭代對(duì)象
let set = new Set([1, 2, 3, 4, 5]), array = [...set]; console.log(array); // [1,2,3,4,5]
展開運(yùn)算符可以操作所有的可迭代對(duì)象,并根據(jù)默認(rèn)迭代器來選取要引用的值,從迭代器讀取所有值。然后按返回順序?qū)⑺鼈円来尾迦氲綌?shù)組中。因此如果想將可迭代對(duì)象轉(zhuǎn)換為數(shù)組,用展開運(yùn)算符是最簡(jiǎn)單的方法。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 不得不知的ES6小技巧
- 你點(diǎn)的 ES6一些小技巧,請(qǐng)查收
- 淺談讓你的代碼更簡(jiǎn)短,更整潔,更易讀的ES6小技巧
- ES6小技巧之代替lodash
- JS實(shí)現(xiàn)集合的交集、補(bǔ)集、差集、去重運(yùn)算示例【ES5與ES6寫法】
- ES6 迭代器(Iterator)和 for.of循環(huán)使用方法學(xué)習(xí)(總結(jié))
- ES6 迭代器與可迭代對(duì)象的實(shí)現(xiàn)
- 詳談ES6中的迭代器(Iterator)和生成器(Generator)
- 詳解ES6語法之可迭代協(xié)議和迭代器協(xié)議
- ES6中的迭代器、Generator函數(shù)及Generator函數(shù)的異步操作方法
- ES6常用小技巧總結(jié)【去重、交換、合并、反轉(zhuǎn)、迭代、計(jì)算等】
相關(guān)文章
基于Bootstrap實(shí)現(xiàn)Material Design風(fēng)格表單插件 附源碼下載
Jquery Material Form Plugin是一款基于Bootstrap的Material Design風(fēng)格的jQuery表單插件。這篇文章主要介紹了基于Bootstrap的Material Design風(fēng)格表單插件附源碼下載,感興趣的朋友參考下2016-04-04詳解JavaScript中的強(qiáng)制類型轉(zhuǎn)換和自動(dòng)類型轉(zhuǎn)換
這篇文章中我們將深入探索一下 JavaScript 中的類型轉(zhuǎn)換,揭示強(qiáng)制類型轉(zhuǎn)換、自動(dòng)類型轉(zhuǎn)換和轉(zhuǎn)換函數(shù)的奧秘,快跟隨小編一起學(xué)習(xí)一下吧2023-12-12微信小程序滑動(dòng)選擇器的實(shí)現(xiàn)代碼
這篇文章主要介紹了微信小程序滑動(dòng)選擇器的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08對(duì)存在JavaScript隱式類型轉(zhuǎn)換的四種情況的總結(jié)(必看篇)
下面小編就為大家?guī)硪黄獙?duì)存在JavaScript隱式類型轉(zhuǎn)換的四種情況的總結(jié)(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08