前端小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 迭代器协议的工作原理,而非推荐的实践。

相关推荐
i听风逝夜35 分钟前
Web 3D地球实时统计访问来源
前端·后端
iMonster39 分钟前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢1 小时前
antd渐变色边框按钮
前端
元直数字电路验证1 小时前
Jakarta EE Web 聊天室技术梳理
前端
wadesir1 小时前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛1 小时前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端
灵犀坠1 小时前
前端面试八股复习心得
开发语言·前端·javascript
9***Y481 小时前
前端动画性能优化
前端
网络点点滴1 小时前
Vue3嵌套路由
前端·javascript·vue.js
牧码岛1 小时前
Web前端之Vue+Element打印时输入值没有及时更新dom的问题
前端·javascript·html·web·web前端