一.作用
用来遍历数组,类似于用for…in遍历对象
for (var i = 0; i < arr.length; i++)
用着挺好,嫌麻烦还可以用arr.forEach()
,那for...in
存在的意义是什么?
for (var i = 0; i < arr.length; i++)
不够简洁太长,长就容易出错,比如笔者经常犯的错误:
for (var j = 0; j < arr[i].length; i++)
forEach不够灵活
arr.forEach不好用,因为无法通过break或者return跳出
for...in
不适合用来遍历数组for…in会遍历到自定义属性甚至原型属性、index是字符串而不是数值、某些情况下甚至不按顺序遍历
所以,我们需要一种更方便的遍历数组的方法,就像用for...in
遍历对象一样简单易用的方法,那就是for...of
二.特点
1.可以遍历其它集合
不仅可以遍历数组,还可以遍历其它集合(iterable),包括NodeList/arguments、字符串、Map、Set等等
示例代码如下:
// 数组
var arr = [1, 2, 2, 4];
for (var val of arr) {
console.log(val);
}
// 类数组对象(arguments、NodeList)
(function() {
for (var val of arguments) {
console.log(val);
}
})(1, 2, 4);
// Set
var uniqueArr = new Set(arr); // Set自动去重
for (var val of uniqueArr) {
console.log(val);
}
// Map
var map = new Map([['a', 1], ['b', 2]]);
map.set('c', 3);
for (var [key, val] of map) {
console.log('map[' + key + '] = ' + val);
}
注意:Chrome47不支持var [key, val] of map
语法,FF43支持,node --harmony
v0.12.7不支持解构表达式。如果想痛快地体验ES6,建议node安装think.js
2.不能遍历对象
for...of
不支持遍历对象,但可以通过添加[Symbol.iterator]()
方法来让其它对象(比如jQuery
对象)支持for...of
遍历,有[Symbol.iterator]()
方法的对象是可迭代的(iterable)
怎么个不支持法?示例代码如下:
var obj = {
'a': 1,
'b': 2
}
for (var val of obj) {
console.log(val);
}
// Uncaught TypeError: is not a function
// 报错,不支持遍历对象,那是for...in的职责
如果想让自定义对象支持for...of
遍历,需要添加[Symbol.iterator]()
方法,如下:
// 一般方法实现迭代器
class MyIterator {
constructor(obj) {
this.obj = obj;
this.keys = Object.keys(obj);
this.index = 0;
}
// 实现iterator接口
[Symbol.iterator]() {
return this;
}
next() {
var val = this.obj[this.keys[this.index]];
if (this.index === this.keys.length) {
return {
done: true
}
}
else {
this.index++;
return {
done: false,
value: val
}
}
}
}
// 测试
var _obj = {
a: 2,
b: 1
}
for (var val of new MyIterator(_obj)) {
console.log(val);
}
等等,Symbol
是什么东西?Symbol大有来头:Symbol是js中第7种基本类型(本来就有的6种是null, undefined, Number, Boolean, Object, String),不是字符串也不是对象,证据如下:
typeof Symbol() === 'symbol'
上面代码中出现的Symbol.iterator
是一个内置的函数名(Symbol类型的,而一般的函数名是String),执行for...of
语句的时候,解释器会在of后面的对象身上找名为Symbol.iterator
的方法,该方法执行后返回一个迭代器对象iterator
,拿到iterator
后自然是.next()
、.next()
直到done,过程类似于:
// 数组
var arr = [1, 2, 2, 4];
// for (var val of arr) {
// console.log(val);
// }
var iter = arr[Symbol.iterator]();
while (true) {
var res = iter.next();
if (res.done) {
break;
}
else {
var val = res.value;
console.log(val);
}
}
当然,我们这个功能太弱了,for...of
的强大之处在于支持break
, continue
, return
,就像经典for
循环一样
3.支持自定义迭代器
其实上面我们已经实现了自定义迭代器,但好像很麻烦的样子。用这个,毋宁死。制定ES6标准的人自然也想到了,配合generator
语法(function* + yield
)很容易实现自定义迭代器,以支持for...of
遍历:
var obj = {
'a': 1,
'b': 2
}
// generator实现迭代器
obj[Symbol.iterator] = function*() {
for (var key in this) {
yield this[key];
}
}
for (var val of obj) {
console.log(val);
}
秒秒钟实现一个迭代器,这下方便多了
P.S.其实我们的MyIterator
严格来说是generator,(迭代器的)生成器
三.总结
ES5引入了forEach
,但实际上没什么用,很多时候不得不把写好的forEach
换成经典for
循环。。forEach
在灵活性方面不如every
,但every
会新建一个数组。。
好了,现在有了for...of
,forEach
再见
参考资料
- 《ES6 in Depth》:InfoQ中文站提供的免费电子书