迭代器
在上篇,我们说for...in遍历的是对象的属性名或者说数组的下标,还简单罗列了几个辅助迭代器。那是否有可以直接遍历属性值而非对象属性(或数组下标)的方式呢?
ES6新增了for...of,可以实现:
var myArray = [1,2,3]
for(var v of myArray) {
// 1 2 3
console.log(v)
}
for...of循环首先会像被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有返回值。
可迭代对象有Array、Map、Set、String、TypeArray、arguments等,可以通过for...of来遍历,而普通对象没有默认的@@iteration属性,不能直接通过for...of遍历。
我们以数组为例,来模拟下for...of是如何在数组中工作的。
var myArray = [1,2,3]
var it = myArray[Symbol.iterator]()
console.log(it.next()) // {value: 1,done: false}
console.log(it.next()) // {value: 2,done: false}
console.log(it.next()) // {value: 3,done: false}
console.log(it.next()) // {value: undefined,done: true}
这里手动执行next()方法就是模拟循环遍历的过程。当done为true的时候,则循环结束。这里稍微提下Symbol.iterator用来获取数组的@@iterator内部属性,返回的是一个迭代器对象的函数,而不是直接返回的对象。
刚才也说了,常用的一些数据结构都是可迭代对象,而普通对象却不是,有点匪夷所思。我们先看看:
var myObject = {
a: 1,
b: 2
}
for (let val of myObject) {
// Uncaught TypeError: myObject is not iterable
console.log(val)
}
我们能否给对象自定义@@iterator属性呢?
var myObject = {
a: 1,
b: 2
}
// 给myObject新增一个迭代器属性,是一个函数
Object.defineProperty(myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function() {
var _this = this
var idx = 0
var attrs = Object.keys(_this)
return {
next: function() {
return {
value: _this[attrs[idx++]],
done: idx > attrs.length
}
}
}
}
})
// 手动遍历myObject
var it = myObject[Symbol.iterator]()
it.next() // { value:2, done:false }
it.next() // { value:3, done:false }
it.next() // { value:undefined, done:true }
// 用for..of遍历myObject
for (var v of myObject) {
console.log(v) // 1 2
}
这里我们给myObject对象手动添加了个迭代器对象,这样的话,也就可以正常通过for...of来循环遍历属性值了。不过,你有没有想过,这么多类型都有默认的迭代器对象,而应用最广泛的普通对象却没有,我想这应该是有意为之,未来不好预测,应该是避免影响未来功能的扩展。