前端小tips

研究个问题 下面前端代码为啥成立 Object.prototype[Symbol.iterator] = function() {

return Object.values(this)Symbol.iterator

}

let [a, b] = {a: 1, b: 2}

console.log(a, b); // 1 2

这段前端代码之所以能让 let [a, b] = {a: 1, b: 2} 正常工作,并输出 1 2,是因为它巧妙地利用了 JavaScript 中的迭代器协议(Iterator Protocol)原型链(Prototype Chain)

我们来一步步拆解:

1. 正常情况下,为什么 let [a, b] = {a: 1, b: 2} 会报错?

在 JavaScript 中,let [a, b] = ... 这种语法叫做数组解构赋值(Array Destructuring Assignment) 。它要求等号右边的值必须是可迭代的(Iterable)

什么是可迭代的?一个对象如果实现了迭代器协议,那么它就是可迭代的。迭代器协议的核心是对象必须有一个属性名为 Symbol.iterator 的方法。这个方法返回一个迭代器(Iterator) 。迭代器是一个对象,它有一个 next() 方法,每次调用 next() 都会返回一个 { value: any, done: boolean } 形式的对象,直到 donetrue

常见的可迭代对象包括:数组(Array)、字符串(String)、Map、Set、arguments 对象、NodeList 等。

而普通的 JavaScript 对象(如 {a: 1, b: 2})默认不是可迭代的 。所以,如果你直接运行 let [a, b] = {a: 1, b: 2},通常会抛出 TypeError: {a: 1, b: 2} is not iterable 这样的错误。

2. 代码 Object.prototype[Symbol.iterator] = function() { return Object.values(this)[Symbol.iterator]() } 的作用

这行代码是关键!它做了以下几件事:

  • Object.prototype 这是所有普通 JavaScript 对象的原型。当你创建一个对象字面量 {} 时,它的原型就是 Object.prototype。这意味着,任何继承自 Object.prototype 的对象(几乎所有你创建的普通对象都如此)都会拥有你在这里添加的属性和方法。

  • [Symbol.iterator] 这是 JavaScript 中的一个知名 Symbol 。当 JavaScript 引擎需要对一个对象进行迭代时(例如在 for...of 循环中,或者进行数组解构时),它就会查找并调用这个 Symbol.iterator 方法。

  • function() { ... } 这就是我们为 Object.prototype 添加的 Symbol.iterator 方法的实现。

    • this 在这个方法内部,this 指向当前正在被迭代的对象。例如,在 let [a, b] = {a: 1, b: 2} 的上下文中,this 就是 {a: 1, b: 2} 这个对象。
    • Object.values(this) Object.values() 方法会返回一个给定对象自身的所有可枚举属性值的数组。对于 {a: 1, b: 2}Object.values(this) 将返回数组 ``。
    • [Symbol.iterator]() 数组是天生可迭代的。[Symbol.iterator]() 会返回数组 `` 的默认迭代器。这个迭代器会按顺序(先 1,再 2)产生值。

所以,这整行代码的含义是:

我们修改了 Object 的原型,给所有普通对象添加了一个 Symbol.iterator 方法。当任何普通对象被要求迭代时,它会先把自己所有属性的值提取出来,形成一个数组,然后返回这个数组的默认迭代器

3. let [a, b] = {a: 1, b: 2} 如何工作?

当 JavaScript 引擎执行 let [a, b] = {a: 1, b: 2} 这行代码时:

  1. 它发现等号右边是一个普通对象 {a: 1, b: 2}

  2. 由于是数组解构赋值,引擎会尝试获取这个对象的迭代器。

  3. 它沿着 {a: 1, b: 2} 的原型链向上查找 Symbol.iterator 方法,最终在 Object.prototype 上找到了我们添加的那个方法。

  4. 引擎调用这个方法,此时 this 就是 {a: 1, b: 2}

  5. 方法内部执行 Object.values(this),得到数组 ``。

  6. 然后执行 [Symbol.iterator](),返回了数组 `` 的迭代器。

  7. 数组解构 [a, b] 开始使用这个迭代器:

    • 第一次调用迭代器的 next(),得到 { value: 1, done: false },于是 a 被赋值为 1
    • 第二次调用迭代器的 next(),得到 { value: 2, done: false },于是 b 被赋值为 2
    • 后续再调用 next()done 将为 true,表示迭代结束。

总结

通过修改 Object.prototype,我们强制让所有普通对象变得"可迭代",并且它们的迭代行为被定义为"按顺序迭代其所有属性的值"。这就是为什么 let [a, b] = {a: 1, b: 2} 能够成功将 1 赋值给 a,将 2 赋值给 b 的原因。

重要提示:

尽管这段代码能实现你看到的效果,但在实际生产环境中,强烈不建议 直接修改 Object.prototype。因为 Object.prototype 是所有对象的基石,对其进行修改会影响到程序中所有的对象,可能导致:

  • 命名冲突: 与其他库或框架添加的同名方法冲突。
  • 意外行为: 改变了 JavaScript 核心对象的默认行为,可能导致难以调试的问题。
  • 性能问题: 某些优化可能因为原型链的改变而失效。

这种做法更多是为了演示 JavaScript 迭代器协议的工作原理,而非推荐的实践。

相关推荐
晚风予星2 小时前
Ant Design Token Lens 迎来了全面升级!支持在 .tsx 或 .ts 文件中直接使用 Design Token
前端·react.js·visual studio code
sunny_2 小时前
⚡️ vite-plugin-oxc:从 Babel 到 Oxc,我为 Vite 写了一个高性能编译插件
前端·webpack·架构
GIS之路2 小时前
ArcPy 开发环境搭建
前端
林小帅4 小时前
【笔记】OpenClaw 架构浅析
前端·agent
林小帅4 小时前
【笔记】OpenClaw 生态系统的多语言实现对比分析
前端·agent
程序猿的程4 小时前
开源一个 React 股票 K 线图组件,传个股票代码就能画图
前端·javascript
不爱说话郭德纲5 小时前
告别漫长的HbuilderX云打包排队!uni-app x 安卓本地打包保姆级教程(附白屏、包体积过大排坑指南)
android·前端·uni-app
唐叔在学习5 小时前
[前端特效] 左滑显示按钮的实现介绍
前端·javascript
用户5282290301806 小时前
【学习笔记】ECMAScript 词法环境全解析
前端
青青家的小灰灰6 小时前
React 架构进阶:自定义 Hooks 的高级设计模式与最佳实践
前端·react.js·前端框架