Iterator 和 for…of 循环

默认 Iterator 接口

1、默认的 Iterator 接口为部署在数据结构 的 Symbol.iterator 属性,如数组的 Iterator 接口:

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iter])();

iter.next(); // { value: 'a', done: false }
iter.next(); // { value: 'b', done: false }
iter.next(); // { value: 'c', done: true }
复制代码

原生具备 Iterator 接口的数据结构有:Array,Map,Set,String,TypeArray、函数的 arguments 对象、NodeList 对象。其他数据结构需要部署 Iterator 接口才能被 for…of 循环遍历。

对于类数组对象(存在数值键名和 length 属性),可以引用数组的 Iterator 接口部署 Symbol.Iterator方法,或者使用 Array.from() 方法转为数组:

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
// 或者
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
for (let x of Array.from(arrayLike)) {
    console.log(x); // 'a', 'b'
}

let iterable = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3,
    [Symbol.iterator]: [][Symbol.iterator]
};
for (let item of iterable) {
    console.log(item); // 'a', 'b', 'c'
}

let iterable1 = {
    a: 'a',
    b: 'b',
    c: 'c',
    length: 3,
    [Symbol.iterator]: [][Symbol.iterator]
}
for (let item of iterable1) {
    console.log(item); // undefined, undefined, undefined
}
复制代码

普通对象部署数组的 Symbol.iterator 方法并无效果。

如果 Symbol.Iterator 方法对应的不是遍历器生成函数(返回一个遍历器对象),解释引擎将报错。

var obj = {};

obj[Symbol.iterator] = () => 1;

[...obj]; // Uncaught TypeError: Result of the Symbol.iterator method is not an object
复制代码

遍历器对象的 next()、return()、throw()

部署遍历器对象时,next() 方法是必须部署的,return() 方法和 throw() 方法不是必须的。

  • next():遍历器执行的下一步结果
  • return():for…of 循环提前退出(出错或者 break 语句或者 continue 语句),调用 return 方法,该方法必须返回一个对象。
  • throw():主要配合 Generator 函数使用

for…in 和 for…of 区别

for…in 和 for…of 与 forEach 的区别:forEach不能中途跳出循环,for…in 和 for…of 可以和 break、continue、return 配合使用。

  1. for..in 只能获得对象的键名(数组键名为字符串的”0″, “1”, “2”…),for…of 获得键值(可以通过 keys()、entries() 方法获得键名)。
  2. for…in 不能使用数组的keys()、values()、entries() 三个方法(entries() 方法对于数组,键名就是索引值;对于 Set, 键名与键值相同; Map 结构的 Iterator 接口默认调用的就是 entries() 方法)
  3. for…of 循环调用遍历器接口,数组的遍历器接口只返回键名为数字的属性值,for…in 返回所有属性的键名。
let arr = ['a', 'b', 'c'];

for (let a in arr) {
    console.log(a); // 0, 1, 2
}

for (let a of arr) {
    console.log(a); // 'a', 'b', 'c'
}

for (let a in arr.entries()) {
    console.log(a); // undefined
}

for (let a of arr.entries()) {
    console.log(a); // [ 0, 'a' ], [ 1, 'b' ], [ 2, 'c' ]
}

arr.foo = "bar";
for (let a in arr) {
    console.log(a); // 0, 1, 2, foo
}
for (let a of arr) {
    console.log(a); // 'a', 'b', 'c'
}
复制代码

Set 和 Map 结构的遍历

遍历 Set 和 Map 的注意点:

  • 遍历顺序是按照各个成员被添加的顺序;
  • Set 结构返回的是一个值,Map 结构返回的是当前键名和键值组成的数组
let set = new Set(['a', 'b', 'c', 'a']);
for (let e of set) {
    console.log(e); // 'a', 'b', 'c'
}

let map = new Map().set("a", 1).set("b", 2);
for (let arr of map) {
    console.log(arr); // ['a', 1], ['b', 2]
}
复制代码

对象的遍历

对于普通的对象,for…of 不能使用,必须部署了 Iterator 接口才能使用,但是 for…in 可能用于遍历键名。

可以使用 Object.keys 方法将对象的键名生成一个数组,然后遍历这个数组。

let a = {
    a: 1,
    b: 2,
    c: 3
};
for (let item of a) {
    console.log(a); // Uncaught TypeError: a is not iterable
}
for (let item in a) {
    console.log(a); // a, b, c
}
for (let key of Object.keys(a)) {
    console.log(key + ':' a[key]); // a:1, b:2, c:3
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享