对象解构赋值的实现方式

通过实现迭代器协议让对象支持数组解构赋值

问题

如何让 var [a, b] = {a: 1, b: 2} 解构赋值成功?

解答

问题分析

直接对对象使用数组解构会报错:

const obj = {
    a: '1',
    b: '2',
}

const [a, b] = obj
// TypeError: obj is not iterable

错误提示对象不可迭代。要解决这个问题,需要让对象实现迭代器协议。

迭代器协议

根据 MDN 定义,要成为可迭代对象,必须实现 @@iterator 方法,即对象上需要有一个 [Symbol.iterator] 属性,该属性是一个无参函数,返回一个迭代器对象。

数组天生支持迭代,我们可以验证:

const array = [1, 2, 3]
const iterator = array[Symbol.iterator]()

console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 2, done: false }
console.log(iterator.next()) // { value: 3, done: false }
console.log(iterator.next()) // { value: undefined, done: true }

数组解构的本质

数组解构实际上是调用迭代器:

const array = [1, 2, 3]
var [a, b, c] = array

// 等价于
const iterator = array[Symbol.iterator]()
var a = iterator.next().value
var b = iterator.next().value
var c = iterator.next().value

实现方案

为对象添加 [Symbol.iterator] 方法:

const obj = {
    a: '1',
    b: '2',
    [Symbol.iterator]() {
        let index = 0
        const keys = Object.keys(this)
        return {
            next() {
                if (index < keys.length) {
                    return {
                        done: false,
                        value: obj[keys[index++]]
                    }
                }
                return { done: true, value: undefined }
            }
        }
    }
}

const [a, b] = obj
console.log(a, b) // '1' '2'

现在对象也可以使用 for...of 遍历:

for (let i of obj) {
    console.log(i)
}
// '1'
// '2'

关键点

  • 数组解构依赖迭代器协议,对象默认不支持
  • 实现 [Symbol.iterator] 方法返回迭代器对象
  • 迭代器对象需要有 next() 方法,返回 { value, done } 格式
  • 实现后对象也可以使用 for...of 遍历
  • next() 方法通过 done 标识是否迭代完成