迭代器

80 阅读2分钟

迭代器

在上篇,我们说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来循环遍历属性值了。不过,你有没有想过,这么多类型都有默认的迭代器对象,而应用最广泛的普通对象却没有,我想这应该是有意为之,未来不好预测,应该是避免影响未来功能的扩展。