在 ES9 中,新增了 Iterator.prototype.__proto__
这个属性,它引起了一些对此的讨论,而其中一个问题就是:它是否会影响 JavaScript 运行时的基础行为,导致代码出现 BUG。本篇文章就介绍一下这个问题,并提供一些避免该问题的实践建议。
深入了解 Iterator
ES6 增加了一组新的迭代器协议,例如,若一个对象的 [Symbol.iterator]
属性是一个函数,则它被认为是可迭代的。迭代器协议是指定义对象需要去完成哪些操作才能让该对象变成一个迭代器。
下面是一个迭代器的示例:
-- -------------------- ---- ------- ----- ---------- - ----------------- - ---------- - -- --------- - ----- - ------ - -- ----------- - ----------------- - ------ - ------ ------------------------ ----- ----- -- - ---------- - -- ------ - ----- ----- ------ ---- -- - ------------------- - ------ ----- - - ----- ---------- - --- -------------------- --------- -----------
在上面的代码中,MyIterator
是一个实现了自身迭代器协议的类,myIterable
是一个使用该类生成的迭代器,可以按照如下方式使用:
for (const value of myIterable) { console.log(value); }
通过调用 MyIterator
中的 next
方法,我们可以逐步浏览该迭代器,并访问其中的对象。
Iterator.prototype.proto 的问题
ES9 中新增的 Iterator.prototype.__proto__
属性可能会导致一些问题。从技术角度看,我们知道一个对象的原型(或称隐式类)是在创建时设置的。在 JavaScript 中,原型链是一种用于沿着一个对象链查找属性的方法。
例如,当我们在一个数组上查找 length 时,它会在它的原型对象 Array.prototype 上查找,因为数组是其的实例,并且 Array.prototype 是其隐藏的父级。
如果我们设置了 Iterator.prototype.__proto__
,则它会在原型链上创建一个新的“级别”,这可能会干扰 JavaScript 运行时的基本行为,并导致代码中出现不可预测的 Bug。下面是一个示例:
const testObj = {}; console.log(testObj.__proto__.__proto__); // 输出 primitive type {String, Number, Boolean, Symbol, Object} Iterator.prototype.__proto__ = Object.prototype; console.log(testObj.__proto__.__proto__); // 输出 Object.prototype
在上面的代码中,我们可以看到 testObj
对象的原型链中添加了一个额外的级别,这通常可能导致代码中的奇怪错误。这是一些程序员试图减小代码的理由,所以他们尝试在代码中使用 Iterator.prototype.__proto__
。不过,这是一个不好的做法。
避免 Iterator.prototype.proto 的的实践建议
有些开发人员可能会发现将 Iterator.prototype.__proto__
用于某些情况下非常方便。然而,因为涉及到影响 JavaScript 运行时的基础行为,建议您避免将其用于生产代码中。
- 直接使用原型来解决需求
InteroptJS、Lodash 和其他一些库已经为获取和修改原型实现了方便的工具函数。使用这些库或编写自己的工具函数,可以避免直接访问 __proto__
的代码。
-- -------------------- ---- ------- ------ - ---------- ------- --- - ---- --------- -------- ----------- - ----- -------- - ------------------------------------- ------------------------------------ - ----- ----- --- - ---------- --- --- ---- ----- --- - -------- --- -- - - --- ------------------------------------ - --------- ------ --------- -
在上面的代码中,我们使用 Lodash 库中的 prototype
工具来访问 Symbol.iterator
的原型,并对其进行了操作。
- 创建新的 API
可以使用 JavaScript-class-based 面向对象编程(OOP)或函数式编程(FP)范例源码来创建新的 API,从而避免直接访问 Prototype。例如,您可以编写一个 safeIterProto
函数,其中封装了必要的工作来保护代码安全性。
-- -------------------- ---- ------- ------ - --- - ---- --------- ----- ------------- - ------------- - ------------- - ----- - ------- - ------------- - ----------------------------------------------------------- --------------------------------------------- - ----- - ------ - --------------------------------------------- - -------------- ------------- - ----- - --------------- ----- - ------------- ----- --- - --------------- ------------ ------ ---- - - ----- ------------- - --- ---------------- -------- ----------- - --------------------------- -- --- - -- - - --- --- -- - - --- -
在上面的代码中,SafeIterProto
类是我们使用 JavaScript-class-based 面向对象编程创建的。我们使用的 Lodash 工具来安全地迭代,而不会影响其他代码。
结论
避免使用 Iterator.prototype.__proto__
可以保护代码在复杂路径和链操作时表现良好,因此它是一个值得注意的规则。本文提供了两种避免该问题开发实践,即使用原型和创建新的 API。这些实践可以帮助开发人员保护代码的安全性,并避免代码中的不必要错误。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/674e6b5ee884a3e30f263cf4