这是一道字节跳动的面试题,乍一看是不是觉得熟悉又陌生?很多人第一反应可能会懵住。
在 JavaScript 中,解构赋值语法的左侧是一个数组,而右侧则应该是一个具有迭代器接口的对象(如数组、Map、Set等)。因此,将对象 {a: 1, b: 2} 解构赋值给 [a, b] 会导致语法错误。但面试官偏偏要求让这个等式成立,这背后考察的正是对 ES6+ 核心特性的深入理解。
解题思路
错误思路
既然将一个对象解构赋值给数组会报错,那是不是直接把语法改成对象的解构就行了?比如写成 var { a, b } = { a: 1, b: 2 };。
如果真这么做了,那恭喜你,面试可能就到此为止了。这道题的考点远不止于此。
正确思路:从报错信息入手
我们先看看直接执行会报什么错:
var [a, b] = {a: 1, b: 2}
// TypeError: {(intermediate value)(intermediate value)} is not iterable
错误明确指出,右侧的对象 (intermediate value) 是不可迭代的(not iterable)。这直接点明了问题的关键:对象默认不具备迭代器属性。
为了加深理解,我们看一个 for...of 循环的例子:
let arr = [1, 2, 3]
let obj = {
a: 1,
b: 2,
c: 3
}
for(let item of arr){
console.log(item) // 正常输出 1, 2, 3
}
for(let item of obj){
console.log(item) // TypeError: obj is not iterable
}
我们知道 for...of 只能遍历具有迭代器接口的数据结构。那么,数组身上的迭代器属性到底是什么样的呢?我们可以在控制台查看数组的原型。

从上图可以看到,数组原型(Array.prototype)上有一个 Symbol.iterator 属性,它的值是一个函数。如果我们调用这个函数会得到什么?
console.log(arr.__proto__[Symbol.iterator]());
// Object [Array Iterator] {}
关键点来了:它返回的是一个迭代器对象!
所以,一个可迭代对象的基本结构是这样的:
interable
{
[Symbol.iterator]: function () {
return 迭代器 // 一个具有next()方法的对象
}
}
由此我们可以得出结论:一个数据结构只要具备 [Symbol.iterator] 属性,且其值是一个能返回迭代器对象的函数,那它就是可迭代的。
实现:让对象变得可迭代
回到面试题,目标是让 var [a, b] = {a: 1, b: 2} 成立。思路很明确了:为对象手动添加一个迭代器,也就是让对象能够通过某种方式“继承”或拥有迭代器属性。
我们可以直接在 Object.prototype 上添加这个方法,但这会影响到所有对象,需要谨慎考虑其副作用。不过,为了解题,我们可以先这样做:
Object.prototype[Symbol.iterator] = function(){
// 暂时为空
}
var [a, b] = {a: 1, b: 2}
console.log(a, b);
此时,错误信息变了:
TypeError: Result of the Symbol.iterator method is not an object
这说明我们的方向是对的,但 [Symbol.iterator] 方法没有返回一个正确的迭代器对象。
我们知道 var [a, b] = [1, 2] 是完全可以的。所以,最直接的思路是让对象的迭代器行为模仿数组。我们可以让对象的迭代器返回其属性值的迭代器。使用 Object.values(this) 可以获取对象的所有值,形成一个数组,而这个数组天然就是可迭代的。
Object.prototype[Symbol.iterator] = function(){
// 使用 Object.values(this) 获取对象的所有值,并返回这些值的迭代器对象
return Object.values(this)[Symbol.iterator]()
}
var [a, b] = {a: 1, b: 2}
console.log(a, b); // 输出:1 2
成功! 这段代码的核心是重写了 Object.prototype 上的 [Symbol.iterator] 方法。新的方法通过 Object.values(this) 获取调用它的对象的所有值([1, 2]),然后调用这个值数组自身的迭代器方法并返回其结果。这样,任何对象在被数组解构或用于 for...of 循环时,都会遍历其属性值。
总结与思考
这道题巧妙地考察了对 JavaScript 迭代协议(Iteration Protocols) 的理解。解构赋值、for...of、扩展运算符(...)等语法都依赖于可迭代对象。理解 Symbol.iterator 这个内部符号是如何工作的,是掌握现代 JavaScript 异步编程和高级数据操作的基础。
在实际项目中,我们通常不会直接修改 Object.prototype,因为这会造成全局污染,影响所有对象。更安全的做法是为特定对象单独添加迭代器,或者使用 Map、Set 这类内置的可迭代数据结构。
解决这类“熟悉又陌生”的面试题,关键在于将知识点串联起来,从错误信息中定位核心概念(本例中是“not iterable”),并利用对语言底层机制(如迭代器)的理解来构建解决方案。希望这个解析能帮助你在未来的技术面试中更加从容。如果你对这类 JavaScript 深入原理的讨论感兴趣,欢迎到 云栈社区 的 HTML/CSS/JS 板块与其他开发者交流探讨。