因爲 ES6 中引入了許多數據結構, 算上原有的包括Object, Array, TypedArray, DataView, buffer, Map, WeakMap, Set, WeakSet等等, 數組須要一個東西來管理他們, 這就是遍歷器(iterator)。jquery
遍歷器調用一般使用 for...of 循環, for...of
能夠遍歷具備 iterator 的對象, ES6中默認只有數組, Set, Map, String, Generator和一些類數組對象(arguments, DOM NodeList)帶有遍歷器, 其餘的數據結構須要本身定義遍歷器。數組
默認 for...of 遍歷器遍歷值安全
var arr = ["red", "green", "blue"]; for(let v of arr){ //至關於 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 默認獲得值, 而 for...in 只能獲得索引。固然數組的 for...of 只返回數字索引的屬性, 而 for...in 沒有限制:數據結構
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" }
默認 for...of 遍歷器遍歷值函數
var set = new Set(["red", "green", "blue"]); for(let v of set){ //至關於 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" }
默認 for...of 遍歷器遍歷鍵值對this
var map = new Map(); map.set("red", "#ff0000"); map.set("green", "#00ff00"); map.set("blue", "#0000ff"); for(let [key, value] of map){ //至關於 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能夠很好的處理區分32位 Unicode 字符串prototype
var str = "Hello"; for(let v of str){ console.log(v); //依次輸出 "H", "e", "l", "l", "o" }
// DOM NodeList var lis = document.getElementById("li"); for(let li of lis){ console.log(li.innerHTML); //遍歷每一個節點 } //arguments function fun(){ for(let arg of arguments){ console.log(arg); //遍歷每一個參數 } }
不是全部類數組對象都有 iterator, 若是沒有, 能夠先用Array.from()
進行轉換:指針
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 對象:
$.fn[Symbol.iterator] = [][Symbol.iterator];
技巧2: 利用 Generator 從新包裝對象:
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" }
iterator 遍歷過程是這樣的:code
咱們實現一個數組的遍歷器試試:對象
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} } }; }
由這個例子咱們能夠看出如下幾點:
其實一個 id 生成器就很相似一個遍歷器:
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 //...
對於大多數數據結構, 咱們不須要再像這樣寫遍歷器函數了。由於他們已經有遍歷器函數[Symbol.iterator]
, 好比Array.prototype[Symbol.iterator]
是數組結構的默認遍歷器。
下面定義一個不完整(僅包含add()方法)的鏈表結構的實例:
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 }
注意, 若是遍歷器函數[Symbol.iterator]
返回的不是如上例所示結構的對象, 會報錯。
固然, 若是不不喜歡用for...of(應該鮮有這樣的人吧), 能夠用 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(); }
如下操做會在內部調用相應的 iterator:
yield*
後面帶有一個可遍歷結構iterator 使用 Generator 實現會更簡單:
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]
固然, 以上代碼還能夠這樣寫:
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]
以上的遍歷器對象只提到了 next() 方法, 其實遍歷器還有 throw() 方法和 return() 方法:
function readlineSync(file){ return { next(){ if(file.isAtEndOfFile()){ file.close(); return {done: true}; } }, return(){ file.close(); return {done: true}; } } }
上面實現了一個讀取文件內數據的函數, 當讀取到文件結尾跳出循環, 可是當循環跳出後, 須要作一些事情(關閉文件), 以防內存泄露。這個和 C++ 中的析構函數十分相似, 後者是在對象刪除後作一些釋放內存的工做, 防止內存泄露。