扩展Object.prototype JavaScript

42

我不是在问这个是否可以:

Object.prototype.method = function(){};

这被几乎所有人视为“邪恶”,因为它会破坏 for(var i in obj)

真正的问题

忽略以下内容:

  • 无能的浏览器(不支持Object.defineProperty)
  • 可能发生的属性冲突或覆盖

假设您有一些非常有用的方法,这是否被认为是错误/不道德的?

Object.defineProperty(Object.prototype, 'methodOnSteriods',{
  value: function(){ /* */ },
  writable: true,
  configurable: true,
  enumerable: false
});

如果您认为上述行为不道德,那么他们为什么一开始就要实现这个功能呢?

这个 defineProperty 设置会向原型链上传播,还是仅适用于实际的 Object.prototype 对象?换句话说,如果你设置了 somevar = {mthodOnSteroids:'mistakeVal'},那么你是否会失去像 'enumerable: false' 这样的定义设置? - Matt Molnar
1
@XHR,是的,你会失去你的方法。例如:var cards = {forEach:'son'}; cards.forEach(function(){});//world exploids。而且,你确实会失去 enumerable:false,例如:for(var i in cards)console.log(i); 输出 'forEach'。 - Lime
5个回答

27

2021年更新

尽管这是被接受的答案,但10年的经验告诉我这不是最好的想法。任何可以避免污染全局范围的方法都是非常好的。

下面是原始答案,为了纪念和因为stackoverflow不会让我删除一个被接受的答案。


2011年原始答案

如果它在你的目标环境中起作用,那么我认为没问题。

此外,我认为原型扩展的偏执症被吹得过于严重。只要像好的开发者一样使用hasOwnProperty(),就没有问题。最坏的情况是在其他地方过载该属性并丢失方法。但如果你这样做的话,那就是你自己的错。


2
我相信 Object.prototype 可能会有一些奇怪的边缘情况副作用。但我不确定这是否仅限于旧版浏览器。扩展任何其他原型是安全的。 - Raynos
是否考虑过实现“PlainObject”?基本上 var obj = {};Object.defineProperty(PlainObject,'method',{enumerable:false});obj.methd(); - Lime
1
“像一个好的开发者一样使用hasOwnProperty()” - 一个好的开发者不会使用obj.hasOwnProperty(…),也不应该需要使用hasOwnProperty,因为Object.prototype方法被定义为不可枚举 - Bergi
哇,10年真的很长时间。我有点为这个答案感到尴尬。我应该把它删除掉。 - Alex Wayne
1
10年前,我可能会点赞它 :-) 事情在变化,ES6+向我们展示了如何错误地扩展原型可能会阻碍语言的发展。 - Bergi

11

我认为这几乎和以前一样邪恶。最大的问题仍然与以前相同,就是Object.prototype是全局的。虽然您的方法可能当前正在解决世界和平问题,但它可能已经覆盖了其他人的方法(该方法保证了银河系的和平),或者将来可能被您无法控制的某个库覆盖(因此再次陷入混乱)。


JavaScript的新版本有许多与属性相关的功能,例如定义属性为可枚举/不可枚举,具有getter和setter... Object.defineProperty存在是为了控制这些功能。

Mozilla文档中:

此方法允许精确添加或修改对象上的属性。通过赋值进行的常规属性添加会创建在属性枚举时显示出来的属性(for…in循环),其值可能会更改,并且可以删除。此方法允许更改这些额外细节的默认值。


这个新函数基本上是支持新功能必需的,并且应该在自己的代码中使用它。能够修改Object.prototype只是它也是一个“普通”对象的副作用,与以前同样邪恶。


你能重新阅读一下问题吗?因为我没看出这怎么有帮助?在两种形式中修改Object.prototype都是有害的,还是只有其中一种? - Lime
完全没有回答问题。 - Raynos
@missingno 如果方法名由220个随机数字/字符组成怎么办? :D 那么属性冲突的可能性就非常小了 :P - Lime
大多数情况下,一个普通的函数就可以胜任,而且更安全和有命名空间。如果你真的需要将其作为方法(使用 this)使用,可以使用 function.apply 或 function.call。 - hugomg
我假设你认为链接是一个无关的参数。 - Lime
1
链式编程可以使代码看起来很棒,但是完全可以没有它。 - hugomg

4

在《JavaScript: the good parts》中,有一个类似的函数,我认为它非常有用,可以改进JavaScript的基本对象(如String、Date等),但仅限于此。

// Add a method conditionally. from "JavaScript: the good parts"

Function.prototype.method = function (name, func) {
    if (!this.prototype[name]) {
        this.prototype[name] = func;
    }
}

3

.hasOwnProperty() 方法将不包括迭代继承属性,我个人认为这往往更加烦人而不是有用的。它大大削弱了 Object.create() 的实用性——这很讽刺,因为同一个人既提倡 .hasOwnProperty(),又推广 Object.create()

不应该扩展 Object.prototype,原因在此列出。如果您真的想要扩展它,则使扩展不可迭代。

我知道这与所有已发布的最佳实践相悖,但我们真的应该停止在对象键迭代上“强制”使用 .hasOwnProperty(),并拥抱直接对象到对象继承的实用性。


0

简短的答案是:是的,你应该这么做。

在这样做之前,需要采取几项预防措施:
1.在迭代对象时使用hasOwnProperty,但这并不是真正的预防措施,因为在迭代对象时,我已经在使用hasOwnProperty
2.检查Object.prototype.name中的name是否存在,这非常安全,可以避免名称冲突。
3.利用Object.defineProperty(),只需添加额外的保护层即可。

如您所见,这并不是非常复杂。

现在来看一下,在您处理了风险/不利因素后的优点:
1.方法链接使代码更易读、更简洁,并使编码更加愉快。反过来使您更加快乐,生活更轻松。
2.解决了浏览器兼容性问题,您正在进行polyfill。

P.S.:
当然,在与大团队一起工作时不要这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接