ES6 Iterator迭代器和for...of循环
ES5中遍历集合通常都是 for循环,数组还有 forEach 方法,对象就是 for-in,
var mycars = {a:2,b:3,c:4} for(var key in mycars) { key;//a b c mycars[key]; //2 3 4 }
ES6 中又添加了 Map 和 Set,而迭代器可以统一处理所有集合数据的方法。迭代器是一个接口,只要你这个数据结构暴露了一个iterator的接口,那就可以完成迭代。ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
迭代器是带有特殊接口的对象。含有一个next()方法,调用返回一个包含两个属性的对象,分别是value和done,value表示当前位置的值,done表示是否迭代完,当为true的时候,调用next就无效了。
一个数据结构只要具有 Symbol.iterator 数据,就可以认为是“可遍历的”(iterable)。
const items = ["zero", "one", "two"]; const it = items[Symbol.iterator](); it.next(); // >{value: "zero", done: false} it.next(); // >{value: "one", done: false} it.next(); // >{value: "two", done: false} it.next(); // >{value: undefined, done: true}
var map=new Map([["a",1],["b",2]]); var iterator=map.keys(); let k; do { k = iterator.next(); console.log(k.value); } while(!k.done);
可迭代的数据结构
以下是可迭代的值:
- Array
- Map
- Set
- String
- TypedArray(一种通用的固定长度缓冲区类型,允许读取缓冲区中的二进制数据)
- 函数中的 arguments 对象
- NodeList 对象
注:普通对象不能迭代。做如下处理,可使对象可以迭代
// code1 function Obj(value) { this.value = value; this.next = null; } Obj.prototype[Symbol.iterator] = function() { var iterator = { next: next }; var current = this; function next() { if (current) { var value = current.value; current = current.next; return { done: false, value: value }; } else { return { done: true }; } } return iterator; } var one = new Obj(1); var two = new Obj(2); var three = new Obj(3); one.next = two; two.next = three; for (var i of one) { console.log(i); } // 1 // 2 // 3
调用 Iterator 接口的场合
(1) 解构赋值
// code2 let set = new Set().add('a').add('b').add('c'); let [x,y] = set; // x='a'; y='b' let [first, ...rest] = set; // first='a'; rest=['b','c'];
(2) 扩展运算符
// code3 // 例一 var str = 'hello'; [...str] // ['h','e','l','l','o'] // 例二 let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
(3)Generator 函数中的 yield* 表达式
// code4 let generator = function* () { yield 1; yield* [2,3,4]; yield 5; }; var iterator = generator(); iterator.next() // { value: 1, done: false } iterator.next() // { value: 2, done: false } iterator.next() // { value: 3, done: false } iterator.next() // { value: 4, done: false } iterator.next() // { value: 5, done: false } iterator.next() // { value: undefined, done: true }
(4)其它场合
- for..of
- Array.from
- Map()、Set()、WeakMap()、WeakSet()
- Promise.all()
- Promise.race()
for...of 循环的优势
数组 forEach 缺点:无法中途跳出 forEach 循环,break 命令或 return 命令都不能生效
myArray.forEach(function (value) { console.log(value); });
对象 for...in 的循环的缺点:
- for...in 遍历主要是为遍历对象而设计的,不适用于遍历数组
- 数组的键名是数字,但是 for...in 循环是以字符串作为键名,“0”、“1”、“2”等。
- for...in 循环不仅可以遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
- 某些情况下,for...in 循环会议任意顺序遍历键名
for (var index in myArray) console.log(myArray[index]); };
for...of 有哪些显著的优点呢?
- 有着同 for...in 一样的简洁语法,但是没有 for...in 那些缺点
- 不同于 forEach 方法,它可以与 break、continue 和 return 配合使用
- 提供了遍历所有数据结构的统一操作接口
1、数组
for...of 循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的值
let arr = [3, 5, 7]; arr.foo = 'hello'; for (let i in arr) { console.log(i); // "0", "1", "2", "foo"
console.log(arr[i]); // "3", "5", "7", "hello" }
for (let i of arr) { console.log(i); // "3", "5", "7" }
2、Map 和 Set 结构
for...of 循环遍历Map 和 Set 结构时,遍历的顺序是按照各个成员被添加进数据结构的顺序,Set 结构遍历时返回的是一个值,而 Map 结构遍历时返回的是一个数组
3、类数组对象
- 字符串
// 普通的字符串遍历 let str = "yuan"; for (let s of str) { console.log(s); // y u a n } // 遍历含有 32位 utf-16字符的字符串 for (let x of 'a\uD83D\uDC0A') { console.log(x); } // 'a' // '\uD83D\uDC0A'
- DOM NodeList 对象
let paras = document.querySelectorAll("p"); for (let p of paras) { p.classList.add("test"); }
- arguments对象
function printArgs() { for (let x of arguments) { console.log(x); } } printArgs("a", "n"); // "a" // "n"
没有 Iterator 接口类数组对象的遍历处理
借用 Array.from 方法处理
let arrayLike = { length: 2, 0 : 'a', 1 : 'b' }; // 报错 for (let x of arrayLike) { console.log(x); } // 正确 for (let x of Array.from(arrayLike)) { console.log(x); }
迭代器应用实例
1、斐波那契数列
var it = { [Symbol.iterator]() { return this }, n1: 0, n2: 1, next() { let temp1 = this.n1, temp2 = this.n2; [this.n1, this.n2] = [temp2, temp1 + temp2] return { value: temp1, done: false } } } for (var i = 0; i < 20; i++) { console.log(it.next()) }
2、任务队列迭代器
3、延迟执行
var map=new Map([["a",1],["b",2]]);var iterator=map.keys();let k;do {k = iterator.next();console.log(k.value);} while(!k.done);