我在这里和那里遇到了一些关于如何不赞成修改 JavaScript 对象的原型的评论?我个人不明白这怎么会是一个问题。例如,扩展 Array 对象以具有 map 和 include 方法或创建更强大的 Date 方法?
为什么不赞成修改 JavaScript 对象的原型?
问题是原型可以在几个地方修改。例如,一个库将向 Array 的原型添加 map 方法,而您自己的代码将添加相同但具有其他目的的方法。所以一个实现将被破坏。
主要是因为命名空间冲突。我知道 Prototype 框架在保持它们的名称与本地包含的名称不同方面存在很多问题。
有两种向人们提供公用事业的主要方法。
原型制作
将函数添加到对象的原型。MooTools 和 Prototype 就是这样做的。
好处:
- 超级容易访问。
缺点:
- 可以使用大量的系统内存。虽然现代浏览器只是从构造函数中获取属性的一个实例,但一些较旧的浏览器为构造函数的每个实例存储每个属性的单独实例。
- 不一定总是可用。
我所说的“不可用”是这样的:
想象一下,您有一个来自 NodeListdocument.getElementsByTagName
并且想要遍历它们。你不能做..
document.getElementsByTagName('p').map(function () { ... });
..因为它是一个 NodeList,而不是一个数组。上述会给你一个错误是这样的:Uncaught TypeError: [object NodeList] doesn't have method 'map'
。
我应该注意到有非常简单的方法可以将 NodeList 和其他类似数组的对象转换为真正的数组。
收藏
在其上创建一个全新的全局变量和库存堆积实用程序。jQuery 和 Dojo 就是这样做的。
好处:
- 永远在那里。
- 内存使用率低。
缺点:
- 没有那么好放置。
- 有时会觉得使用起来很尴尬。
用这个方法你还是做不到..
document.getElementsByTagName('p').map(function () { ... });
..但你可以做..
jQuery.map(document.getElementsByTagName('p'), function () { ... });
..但正如马特指出的那样,在通常的使用中,你会用..
jQuery('p').map(function () { ... });
哪个更好?
最终,这取决于你。如果您可以接受被覆盖/覆盖的风险,那么我强烈建议您进行原型设计。这是我喜欢的风格,我觉得冒险是值得的。如果你和我一样不确定,那么收藏也是一种很好的风格。它们都有优点和缺点,但总而言之,它们通常会产生相同的最终结果。
正如 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.replaceAll
,replaceAll
则规范编写者将不再使用该名称;在浏览器可以实现它之前,它必须被改变。