调整字号

ES6 快速上手:for…of 和迭代器

es6-small

for ... of 是 ES6 新增的循环语句,可以遍历一个集合(比如数组),用来代替普通的 for 循环或 forEach。一个例子:

var myArray = ["a", "b", "c"];
for (var value of myArray) {
  console.log(value);
}

那用 for ... of 有什么好处呢?显然用 forEach 也能遍历数组元素,但是 forEach 的缺陷是无法响应 break, continue, return 语句。所以,不能自由跳出,只能循环到底。另外有一个小提示,在大多数浏览器中 forEach 遍历都比 for 循环要慢。虽然 forEach 更简洁,但是从执行效率考虑应当使用 for

然后你会想到用 for...in,但 for...in 也存在一个问题,就是 for...in 给出的索引都是字符串,比如:

var myArray = ["a", "b", "c"];
for (var value in myArray) {
  console.log(value); //"0", "1", "2"
}

如果你直接拿这些索引来做运算,就会出错,比如 value + 10,会得到 "010" 而不是 10。而且,for...in 不光遍历数组内的元素,如果你往数组上添加了自定义的属性,也会被遍历出来。还有一点,最糟糕的情况下,for...in 还可能会不按 0、1、2……的顺序进行遍历。

for...of 可以克服以上这些缺点。它既支持 break, continue, return,同时,索引的类型和原来类型保持一致,对于数组,索引就是 Number 类型。

for...of 也可以遍历类数组对象,比如 NodeList 类型。

还能用来遍历字符串,会把它当作是字符序列:

for (var char of "(⊙ˍ⊙)") {
  alert(char);
}

还支持遍历 MapSet 对象。因为 Map 对象都是键值对,所以,需要使用解构赋值:

for (var [name, age] of persons) {
  console.log(name + "'s age is: " + age);
}

但是 for...of 并不支持遍历普通对象。你可以用 for...in 代替,或者借助 Object.keys

for ( var key of Object.keys(obj) ) {
  console.log(key + ": " + obj[key]);
}

除了上面提到的类型,在其他对象上调用 for...of 会抛出错误,包括nullundefined。为什么?因为 for...of 需要有迭代器的配合。

迭代器(iterators)

“迭代器”:简单来说,迭代器的核心就是一个 .next() 方法,每次调用 .next(),应该返回一个类似下面的对象:

{
  value: 100,
  done: false
}

假设 a[1, 2, 3] 的迭代器,那么每次调用迭代器的 .next(),应该是这样的结果:

a.next(); // { value: 1, done: false }
a.next(); // { value: 2, done: false }
a.next(); // { value: 3, done: false }
a.next(); // { value: undefined, done: true }

可以看出,每次返回的 value 就是从数组中抽出的一个元素,一直遍历到最后,done 表示状态,当它变为 true 时,遍历就结束了。

迭代器的作用就是用来遍历集合,再对里面的数据进行处理,就像我们经常用的 forEach 一样。迭代器的好搭档是 for...of

可遍历对象(iterables)

前面说了,只有在数组、NodeListSetMap、字符串上可以使用for...of,因为它们都是可遍历对象,称为“iterables”。在普通对象上使用 for...of 会抛出错误。

什么是可遍历对象?简单来说,只要对象上拥有一个 [Symbol.iterator]() 方法,该方法返回一个迭代器。那这个对象就是可遍历的,可以用 for...of 进行遍历。

[Symbol.iterator]() 所返回的迭代器又叫默认迭代器(default iterator)。上面所讲的那些类型,都内置了默认迭代器,所以能在 for...of 中使用。

创建可遍历对象

根据上面所讲的条件,只要你给任意对象添加一个方法:obj[Symbol.iterator],然后,它就能就支持 for...of 遍历了:

obj[Symbol.iterator] = function() {
  // 在这里返回一个迭代器
};
for(let key of obj) {
  // 就可以使用for...of了
}

多数时候我们都可以借用内置对象的迭代器,举个例子,让 jQuery 对象也支持 for...of

  jQuery.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

因为 jQuery 对象与数组类似,所以就直接借用了 Array 的迭代器。不管存在于实例上还是原型上,只要能调用到 [Symbol.iterator]() 就可以。

如果自己编写 [Symbol.iterator],一般都会使用生成器(generator)来作为迭代器。

[Symbol.iterator]() 也可以访问一个对象的迭代器,比如,获取一下数组的迭代器:

let arr = [1, 2, 3];
let iterator = arr[Symbol.iterator]();

iterator.next(); // {value: 1, done: false}
iterator.next(); // {value: 2, done: false}
...

所以要检测一个对象是否可遍历,检查它的 [Symbol.iterator] 属性就行了:

let isIterable = typeof obj[Symbol.iterator] === "function";

现在再回过头来看一下 for...of 的原理:其实 for...of 就是先调用对象的 [Symbol.iterator]() 方法,获取到迭代器,然后就在迭代器上不断调用 next(),当 done 变为 true,表示遍历到最后一个元素了,结束。

还没有评论,沙发空缺中……
flight