Skip to content
On this page

21. 使对象具有可迭代性

Object.prototype 上添加一个可迭代协议的实现之后,可以使得对象(plain object)具有某些可迭代特性,例如 for (let [k, v] of obj) 遍历:

js
let obj = {
  name: 'Tom',
  age: 20
}

for (let [k, v] of obj) {
  console.log(k, v)
}
1
2
3
4
5
6
7
8

1. 使用迭代器实现

js
Object.prototype[Symbol.iterator] = function () {
  let arr = []
  for (k in this) {
    arr.push([k, this[k]])
  }
  let index = 0
  return {
    next: () => {
      if (index < arr.length) {
        return { done: false, value: arr[index++] }
      }
      return { done: true, value: undefined }
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

2. 使用生成器实现

生成器也是一种迭代器,相当于提供了一种更加简洁的写法。

js
Object.prototype[Symbol.iterator] = function* () {
  let arr = []
  for (k in this) {
    arr.push([k, this[k]])
  }
  yield* arr
}
1
2
3
4
5
6
7

测试:

js
let obj = {
  name: 'Tom',
  age: 20
}

for (let [k, v] of obj) {
  console.log(k, v)
}
/*
 * name Tom
 * age 20
*/
1
2
3
4
5
6
7
8
9
10
11
12

3. 举一反三

举一反三,如何在不修改原代码的情况下,使得第二行代码输出 1 3

js
var [a, b] = { a: 1, b: 3 }
console.log(a, b) // 1 3
1
2

这里如果是用 {a, b} 来接受就能够执行,因为 JS 对象天生具有解构赋值特性,前提接收的方式前后要一致,但这里前面是数组形式。

JS 中的解构赋值分为两种情况:

  • 数组解构赋值(由 Symbol.iteratel 实现)
  • 对象解构赋值(JS 底层做的特殊实现,对象解构必须使用对象形式接受)

那如何做到对象解构,然后使用数组形式 [a, b] 接受呢?很容易想到可以在 Object 上实现 Symbol.iteratel 方法,使得对象具有类似数组的解构特性。

js
Object.prototype[Symbol.iterator] = function* () {
  let arr = []
  for (let k in this) {
    arr.push(this[k])
  }
  yield* arr
}

var [a, b] = { a: 1, b: 3 }

console.log(a, b) // 1 3
1
2
3
4
5
6
7
8
9
10
11

简洁写法:

js
Object.prototype[Symbol.iterator] = function* () {
  yield* Object.values(this).map(item => item)
}
1
2
3