为什么不赞成修改 JavaScript 对象的原型?

IT技术 javascript monkeypatching
2021-01-29 11:27:18

我在这里和那里遇到了一些关于如何不赞成修改 JavaScript 对象的原型的评论?我个人不明白这怎么会是一个问题。例如,扩展 Array 对象以具有 map 和 include 方法或创建更强大的 Date 方法?

5个回答

问题是原型可以在几个地方修改。例如,一个库将向 Array 的原型添加 map 方法,而您自己的代码将添加相同但具有其他目的的方法。所以一个实现将被破坏。

您是否希望开发人员了解大型库(如 JQuery 或 DoJo)的所有内部结构和实现?我怀疑他们是否会在框架的下一个版本之前实现任何东西,他们不得不重新开始。库和框架的全部意义在于您只需要知道如何使用它们。
2021-03-14 11:27:18
几乎不可能了解某些甚至一个库的所有方法和特性,尤其是那些将在未来版本中引入的方法和特性。因此,要确保代码一致性,这是避免污染任何全局范围的最佳方法,其中之一是标准对象的原型。
2021-03-18 11:27:18
在这方面,我觉得如果开发人员决定使用多个库并且不知道他们的代码中发生了什么,那是开发人员的错……但这确实回答了我的问题,谢谢。
2021-03-25 11:27:18
并不是说您必须知道所有方法和功能……而是知道是否操作了本机对象。对于像 jQuery 或 Dojo 这样的大型库,这些信息很容易访问。
2021-04-01 11:27:18
jQuery 插件(它们是 jQuery 本身的猴子补丁)也会发生类似的命名空间冲突:stackoverflow.com/q/5740974/479863
2021-04-10 11:27:18

主要是因为命名空间冲突。我知道 Prototype 框架在保持它们的名称与本地包含的名称不同方面存在很多问题。

有两种向人们提供公用事业的主要方法。

原型制作

将函数添加到对象的原型。MooTools 和 Prototype 就是这样做的。

好处:

  1. 超级容易访问。

缺点:

  1. 可以使用大量的系统内存。虽然现代浏览器只是从构造函数中获取属性的一个实例,但一些较旧的浏览器为构造函数的每个实例存储每个属性的单独实例。
  2. 不一定总是可用。

我所说的“不可用”是这样的:

想象一下,您有一个来自 NodeListdocument.getElementsByTagName并且想要遍历它们。你不能做..

document.getElementsByTagName('p').map(function () { ... });

..因为它是一个 NodeList,而不是一个数组。上述会给你一个错误是这样的:Uncaught TypeError: [object NodeList] doesn't have method 'map'

我应该注意到有非常简单的方法可以将 NodeList 和其他类似数组的对象转换为真正的数组。

收藏

在其上创建一个全新的全局变量和库存堆积实用程序。jQuery 和 Dojo 就是这样做的。

好处:

  1. 永远在那里。
  2. 内存使用率低。

缺点:

  1. 没有那么好放置。
  2. 有时会觉得使用起来很尴尬。

用这个方法你还是做不到..

document.getElementsByTagName('p').map(function () { ... });

..但你可以做..

jQuery.map(document.getElementsByTagName('p'), function () { ... });

..但正如马特指出的那样,在通常的使用中,你会用..

jQuery('p').map(function () { ... });

哪个更好?

最终,这取决于你。如果您可以接受被覆盖/覆盖的风险,那么我强烈建议您进行原型设计。这是我喜欢的风格,我觉得冒险是值得的。如果你和我一样不确定,那么收藏也是一种很好的风格。它们都有优点和缺点,但总而言之,它们通常会产生相同的最终结果。

@tylerwashburn:我从未听说过。你能提供一个提到它的链接吗?现在是第二部分(不一定总是可用):在什么情况下扩展Object.prototype不可用?
2021-03-13 11:27:18
可以$('p').map(function () {...}任何的而不是map你提到的例子。
2021-03-17 11:27:18
@KooiInc 我不完全确定这是真的,但我听说旧浏览器会这样做。我会更新我的答案以注意这一点。
2021-03-26 11:27:18
@Matt 这是为了保持与原型示例的一致性。在实际操作中,这将是更好的方法。
2021-04-02 11:27:18
@tylerwashburn:它必须在每个实例上存储函数/字符串/任何内容的实例。真的吗?Afaik 向原型添加一些东西在构造函数中添加一次实例随后从构造函数原型中获取它们的信息。
2021-04-10 11:27:18

正如 bjornd 指出的那样,只有当涉及多个库时猴子补丁才是一个问题。因此,如果您正在编写可重用的库,那么这样做不是一个好习惯。然而,在 JavaScript 中使用宿主对象时,它仍然是解决跨浏览器兼容性问题的最佳技术。

请参阅2009 年的这篇博文(或Wayback Machine 原版),了解一起使用 prototype.js 和 json2.js 时发生的真实事件。

Nicholas C. Zakas 发表了一篇很棒的文章,解释了为什么在团队或客户项目期间,任何程序员都不应该考虑这种做法(也许您可以出于教育目的进行一些调整,但不适用于一般项目使用) .

可维护的 JavaScript:不要修改你不拥有的对象: https : //www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/

除了其他答案之外,修改内置对象可能产生的一个更持久的问题是,如果非标准更改在足够多的站点上使用,ECMAScript 的未来版本将无法定义使用相同名称的原型方法. 这里

这正是Array.prototype.flatten发生的情况Array.prototype.contains简而言之,规范是为这些方法编写的,他们的提案进入了第 3 阶段,然后浏览器开始发布它。但是,在这两种情况下,都发现有一些古老的库Array使用与新方法同名的自己的方法修补内置对象,并具有不同的行为;结果,网站崩溃了,浏览器不得不退出新方法的实现,并且必须编辑规范。(这些方法已重命名。)

例如,目前有一个String.prototype.replaceAll的提案如果您发布一个被广泛使用的库,并且该库将自定义的非标准方法修补到 上String.prototype.replaceAllreplaceAll则规范编写者将不再使用名称;在浏览器可以实现它之前,它必须被改变。

如果您实现的版本完全符合规范,那完全没问题——这被称为polyfill,并且非常常见。
2021-03-20 11:27:18
如果我覆盖了replaceAll,但它使用与普通replaceAll 相同的方法,并使用完全相同的参数,最终结果会有什么不同吗?如果没有任何问题,那么您是否会提前实施?
2021-04-02 11:27:18