因为 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++ 中的析构函数十分相似, 后者是在对象删除后作一些释放内存的工做, 防止内存泄露。