摘要:由于中引入了許多數(shù)據(jù)結(jié)構(gòu)算上原有的包括等等數(shù)組需要一個(gè)東西來(lái)管理他們這就是遍歷器。數(shù)組默認(rèn)遍歷器遍歷值相當(dāng)于依次輸出依次輸出依次輸出依次輸出不難看出默認(rèn)得到值而只能得到索引。即遍歷器的本質(zhì)就是一個(gè)指針。
由于 ES6 中引入了許多數(shù)據(jù)結(jié)構(gòu), 算上原有的包括Object, Array, TypedArray, DataView, buffer, Map, WeakMap, Set, WeakSet等等, 數(shù)組需要一個(gè)東西來(lái)管理他們, 這就是遍歷器(iterator)。
for...of遍歷器調(diào)用通常使用 for...of 循環(huán), for...of 可以遍歷具有 iterator 的對(duì)象, ES6中默認(rèn)只有數(shù)組, Set, Map, String, Generator和一些類數(shù)組對(duì)象(arguments, DOM NodeList)帶有遍歷器, 其他的數(shù)據(jù)結(jié)構(gòu)需要自己定義遍歷器。
數(shù)組
默認(rèn) for...of 遍歷器遍歷值
var arr = ["red", "green", "blue"]; for(let v of arr){ //相當(dāng)于 for(let i in arr.values()) console.log(v); //依次輸出 "red", "green", "blue" } for(let i in arr){ console.log(i); //依次輸出 0, 1, 2 } for(let [key, value] of arr.entries()){ console.log(key + ": " + value); //依次輸出 0: "red", 1: "green", 2: blue" } for(let key of arr.keys()){ console.log(key); //依次輸出 0, 1, 2 }
不難看出 for...of 默認(rèn)得到值, 而 for...in 只能得到索引。當(dāng)然數(shù)組的 for...of 只返回?cái)?shù)字索引的屬性, 而 for...in 沒(méi)有限制:
var arr = ["red", "green", "blue"]; arr.name = "color"; for(let v of arr){ console.log(v); //依次輸出 "red", "green", "blue" } for(let i in arr){ console.log(arr[i]); //依次輸出 "red", "green", "blue", "color" }
Set
默認(rèn) for...of 遍歷器遍歷值
var set = new Set(["red", "green", "blue"]); for(let v of set){ //相當(dāng)于 for(let i in arr.values()) console.log(v); //依次輸出 "red", "green", "blue" } for(let [key, value] of set.entries()){ console.log(key + ": " + value); //依次輸出 "red: red", "green: green", "blue: blue" } for(let key of set.keys()){ console.log(key); //依次輸出 "red", "green", "blue" }
map
默認(rèn) for...of 遍歷器遍歷鍵值對(duì)
var map = new Map(); map.set("red", "#ff0000"); map.set("green", "#00ff00"); map.set("blue", "#0000ff"); for(let [key, value] of map){ //相當(dāng)于 for(let i in arr.entries()) console.log(key + ": " + value); //依次輸出 "red: #ff0000", "green: #00ff00", "blue: #0000ff" } for(let value of map.values()){ console.log(value); //次輸出 "#ff0000", "#00ff00", "#0000ff" } for(let key of map.keys()){ console.log(key); //次輸出 "red", "green", "blue" }
字符串
for...of可以很好的處理區(qū)分32位 Unicode 字符串
var str = "Hello"; for(let v of str){ console.log(v); //依次輸出 "H", "e", "l", "l", "o" }
類數(shù)組對(duì)象
// DOM NodeList var lis = document.getElementById("li"); for(let li of lis){ console.log(li.innerHTML); //遍歷每個(gè)節(jié)點(diǎn) } //arguments function fun(){ for(let arg of arguments){ console.log(arg); //遍歷每個(gè)參數(shù) } }
不是所有類數(shù)組對(duì)象都有 iterator, 如果沒(méi)有, 可以先用Array.from()進(jìn)行轉(zhuǎn)換:
var o = {0: "red", 1: "green", 2: "blue", length: 3}; var o_arr = Array.from(o); for(let v of o_arr){ console.log(v); //依次輸出 "red", "green", "blue" }
技巧1: 添加以下代碼, 使 for...of 可以遍歷 jquery 對(duì)象:
$.fn[Symbol.iterator] = [][Symbol.iterator];
技巧2: 利用 Generator 重新包裝對(duì)象:
function* entries(obj){ for(let key of Object.keys(obj)){ yield [key, obj[key]]; } } var obj = { red: "#ff0000", green: "#00ff00", blue: "#0000ff" }; for(let [key, value] of entries(obj)){ console.log(`${key}: ${value}`); //依次輸出 "red: #ff0000", "green: #00ff00", "blue: #0000ff" }幾種遍歷方法的比較
for 循環(huán): 書寫比較麻煩
forEach方法: 無(wú)法終止遍歷
for...in: 僅遍歷索引, 使用不便捷; 會(huì)遍歷原型鏈上的屬性, 不安全; 會(huì)遍歷非數(shù)字索引的數(shù)組屬性;
for...of:
iterator 與 [Symbol.iterator]iterator 遍歷過(guò)程是這樣的:
創(chuàng)建一個(gè)指針對(duì)象, 指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。即遍歷器的本質(zhì)就是一個(gè)指針。
調(diào)用一次指針的 next 方法, 指針指向第一數(shù)據(jù)成員。之后每次調(diào)用 next 方法都會(huì)將之后向后移動(dòng)一個(gè)數(shù)據(jù)。
知道遍歷結(jié)束。
我們實(shí)現(xiàn)一個(gè)數(shù)組的遍歷器試試:
var arr = [1, 3, 6, 5, 2]; var it = makeIterator(arr); console.log(it.next()); //Object {value: 1, done: false} console.log(it.next()); //Object {value: 3, done: false} console.log(it.next()); //Object {value: 6, done: false} console.log(it.next()); //Object {value: 5, done: false} console.log(it.next()); //Object {value: 2, done: false} console.log(it.next()); //Object {value: undefined, done: true} function makeIterator(arr){ var nextIndex = 0; return { next: function(){ return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true} } }; }
由這個(gè)例子我們可以看出以下幾點(diǎn):
迭代器具有 next() 方法, 用來(lái)獲取下一元素
next() 方法具有返回值, 返回一個(gè)對(duì)象, 對(duì)象 value 屬性代表下一個(gè)值, done 屬性表示是否遍歷是否結(jié)束
如果一個(gè)數(shù)據(jù)結(jié)構(gòu)本身不具備遍歷器, 或者自帶的遍歷器不符合使用要求, 請(qǐng)按此例格式自定義一個(gè)遍歷器。
其實(shí)一個(gè) id 生成器就很類似一個(gè)遍歷器:
function idGen(){ var id = 0; return { next: function(){ return id++; } }; } var id = idGen(); console.log(id.next()); //0 console.log(id.next()); //1 console.log(id.next()); //2 //...
對(duì)于大多數(shù)數(shù)據(jù)結(jié)構(gòu), 我們不需要再像這樣寫遍歷器函數(shù)了。因?yàn)樗麄円呀?jīng)有遍歷器函數(shù)[Symbol.iterator], 比如Array.prototype[Symbol.iterator] 是數(shù)組結(jié)構(gòu)的默認(rèn)遍歷器。
下面定義一個(gè)不完整(僅包含add()方法)的鏈表結(jié)構(gòu)的實(shí)例:
function Node(value){ this.value = value; this.next = null; } function LinkedList(LLName){ this.head = new Node(LLName); this.tail = this.head; } var proto = { add: function(value){ var newNode = new Node(value); this.tail = this.tail.next = newNode; return this; } } LinkedList.prototype = proto; LinkedList.prototype.constructor = LinkedList; LinkedList.prototype[Symbol.iterator] = function(){ var cur = this.head; var curValue; return { next: function(){ if(cur !== null){ curValue = cur.value; cur = cur.next; return {value: curValue, done: false} } else { return {value: undefined, done: true} } } }; } var ll = new LinkedList("prime"); ll.add(1).add(2).add(3).add(5).add(7).add(11); for(let val of ll){ console.log(val); //依次輸出 1, 2, 3, 5, 7, 11 }
注意, 如果遍歷器函數(shù)[Symbol.iterator]返回的不是如上例所示結(jié)構(gòu)的對(duì)象, 會(huì)報(bào)錯(cuò)。
當(dāng)然, 如果不不喜歡用for...of(應(yīng)該鮮有這樣的人吧), 可以用 while 遍歷:
var arr = [1, 2, 3, 5, 7]; var it = arr[Symbol.iterator]; var cur = it.next(); while(!cur.done){ console.log(cur.value); cur = it.next(); }
以下操作會(huì)在內(nèi)部調(diào)用相應(yīng)的 iterator:
數(shù)組的解構(gòu)賦值
展開(kāi)運(yùn)算符
yield* 后面帶有一個(gè)可遍歷結(jié)構(gòu)
for...of
Array.from() 將類數(shù)組對(duì)象轉(zhuǎn)換為數(shù)組
Map(), Set(), WeakMap(), WeakSet() 等構(gòu)造函數(shù)傳輸初始參數(shù)時(shí)
Promise.all()
Promise.race()
Generator 與遍歷器iterator 使用 Generator 實(shí)現(xiàn)會(huì)更簡(jiǎn)單:
var it = {}; it[Symbol.iterator] = function* (){ var a = 1, b = 1; var n = 10; while(n){ yield a; [a, b] = [b, a + b]; n--; } } console.log([...it]); //1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
當(dāng)然, 以上代碼還可以這樣寫:
var it = { *[Symbol.iterator](){ var a = 1, b = 1; var n = 10; while(n){ yield a; [a, b] = [b, a + b]; n--; } } } console.log([...it]); //[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]遍歷器對(duì)象的其他方法
以上的遍歷器對(duì)象只提到了 next() 方法, 其實(shí)遍歷器還有 throw() 方法和 return() 方法:
如果遍歷終止(break, continue, return或者出錯(cuò)), 會(huì)調(diào)用 return() 方法
Generator 返回的遍歷器對(duì)象具throw() 方法, 一般的遍歷器用不到這個(gè)方法。具體在 Generator 中解釋。
function readlineSync(file){ return { next(){ if(file.isAtEndOfFile()){ file.close(); return {done: true}; } }, return(){ file.close(); return {done: true}; } } }
上面實(shí)現(xiàn)了一個(gè)讀取文件內(nèi)數(shù)據(jù)的函數(shù), 當(dāng)讀取到文件結(jié)尾跳出循環(huán), 但是當(dāng)循環(huán)跳出后, 需要做一些事情(關(guān)閉文件), 以防內(nèi)存泄露。這個(gè)和 C++ 中的析構(gòu)函數(shù)十分類似, 后者是在對(duì)象刪除后做一些釋放內(nèi)存的工作, 防止內(nèi)存泄露。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97443.html
摘要:函數(shù)可以沒(méi)有返回值,此時(shí)它依然返回一個(gè)并且在調(diào)用方法時(shí)一次行執(zhí)行完函數(shù)內(nèi)全部代碼,返回。將一個(gè)可遍歷結(jié)構(gòu)解構(gòu),并逐一返回其中的數(shù)據(jù)。 Generator Generator 函數(shù)是 es6 中的新的異步編程解決方案,本節(jié)僅討論 Generator 函數(shù)本身,異步編程放在后面的部分。Generator 函數(shù)之前也提到過(guò),描述內(nèi)部封裝的多個(gè)狀態(tài),類似一個(gè)狀態(tài)機(jī),當(dāng)然也是很好的 iterat...
摘要:筆記和和是塊作用域的,是聲明常量用的。一個(gè)對(duì)象如果要有可被循環(huán)調(diào)用的接口,就必須在的屬性上部署遍歷器生成方法原型鏈上的對(duì)象具有該方法也可。這種方式會(huì)訪問(wèn)注冊(cè)表,其中存儲(chǔ)了已經(jīng)存在的一系列。這種方式與通過(guò)定義的獨(dú)立不同,注冊(cè)表中的是共享的。 ECMAScript6 筆記 let 和 const let和const是塊作用域的 ,const是聲明常量用的。 {let a = 10;} a ...
摘要:迭代器要說(shuō)生成器,必須首先說(shuō)迭代器區(qū)分與講到迭代器,就需要區(qū)別幾個(gè)概念看著都差不多,其實(shí)不然。比如常見(jiàn)就是與分離實(shí)現(xiàn)的本身是可迭代對(duì)象,但不是迭代器,類似與但是又不同。 2016.3.10關(guān)于例子解釋的補(bǔ)充更新 源自我的博客 例子 老規(guī)矩,先上一個(gè)代碼: def add(s, x): return s + x def gen(): for i in range(...
摘要:來(lái)說(shuō)說(shuō)迭代器和生成器,還有可迭代對(duì)象和生成器表達(dá)式。有點(diǎn)繞是不是,其實(shí),一般只要知道可迭代對(duì)象以及它是如何實(shí)現(xiàn)的就行了,中常常用生成器來(lái)代替迭代器,可以說(shuō),生成器就是迭代器。 來(lái)說(shuō)說(shuō)迭代器和生成器,還有可迭代對(duì)象和生成器表達(dá)式。 之前簡(jiǎn)單的提到過(guò),一個(gè)對(duì)象是可迭代的可以理解為能夠使用for循環(huán)。這樣說(shuō)其實(shí)不太準(zhǔn)確,某個(gè)對(duì)象可迭代是因?yàn)樗鼉?nèi)部實(shí)現(xiàn)了$__iter__$這個(gè)特殊方法。比如在...
摘要:異步編程程序執(zhí)行分為同步和異步,如果程序每執(zhí)行一步都需要等待上一步完成才能開(kāi)始,此所謂同步。因此異步編程十分重要。 異步編程 程序執(zhí)行分為同步和異步,如果程序每執(zhí)行一步都需要等待上一步完成才能開(kāi)始,此所謂同步。如果程序在執(zhí)行一段代碼的同時(shí)可以去執(zhí)行另一段代碼,等到這段代碼執(zhí)行完畢再吧結(jié)果交給另一段代碼,此所謂異步。比如我們需要請(qǐng)求一個(gè)網(wǎng)絡(luò)資源,由于網(wǎng)速比較慢,同步編程就意味著用戶必須等...
閱讀 3219·2021-11-23 09:51
閱讀 3681·2021-09-22 15:35
閱讀 3658·2021-09-22 10:02
閱讀 2969·2021-08-30 09:49
閱讀 526·2021-08-05 10:01
閱讀 3392·2019-08-30 15:54
閱讀 1641·2019-08-30 15:53
閱讀 3569·2019-08-29 16:27