【问题标题】:Prototyping Object in Javascript breaks jQuery?Javascript中的原型对象破坏了jQuery?
【发布时间】:2025-12-09 03:30:02
【问题描述】:

我在我的页面中添加了一个简单的.js 文件,该文件在ObjectArray 原型中添加了一些非常普通的常见任务类型的函数。

通过反复试验,我发现在Object.prototype 中添加任何函数,无论它的名称或功能如何都会导致 jQuery 中的 Javascript 错误:

罪魁祸首?

Object.prototype.foo = function() {
    /*do nothing and break jQuery*/
};

我在 attr:function { } 声明中得到jquery-1.3.2.js 的第 1056 行的错误:

/*Object doesn't support this property or method*/
name = name.replace(/-([a-z])/ig, function(all, letter) {
            return letter.toUpperCase();
        });

显然 G.replace 是未定义的。

虽然很明显有些东西我只是没有用原型设计来解决问题,但我很难弄清楚它是什么。

明确地说,我不是在寻找解决方法,我已经解决了......我正在寻找的是为什么?的答案。为什么向Object.prototype 添加函数会破坏这段代码?

【问题讨论】:

  • 如果您切换到完整的 jquery 文件(未缩小),您可能会遇到更有意义的错误。这样您就可以更清楚地看到哪些代码存在问题。
  • @CrescentFresh 的链接已过时。更新:bugs.jquery.com/ticket/2721

标签: javascript jquery prototype prototyping


【解决方案1】:

如果只是搞乱 for...in 循环的情况,你不能使用 Object.defineProperty 添加你的 fn 而不使其可枚举吗?

所以:

Object.defineProperty(Object.prototype, "foo", { 
    value: function() {
        // do stuff
    },
    enumerable : false
});

似乎对我有用。这还会被认为是不好的形式吗?

【讨论】:

  • 我不知道...很高兴知道它是否不好的形式,但这对我很有用。
  • 你可以,但是 for..in 循环设计是为了枚举原型扩展。这就是您探索原型链的方式。它不会破坏 for..in 循环它会破坏错误的代码,即盲目假设迭代值将始终属于某种类型,而由于 Object.prototype 链可以包含函数,大括号之间的代码可以在以下情况下引发异常他们只期望标量和对象。
  • @NoOne 我不认为扩展对象原型与任何其他原型有什么不同。它只需要负责任地完成。 Web 的开源性质,以及所有可用的框架、插件和工具,意味着您的项目可能比您自己的项目更多的是第三方代码。 JavaScript 的核心租户是不要修改你不拥有的任何东西。如果你只是不加思索地扩展 Object,你可能会破坏一些期望它以某种方式表现的东西。
  • @NoOne 您的应用程序可能看起来像:核心 Javascript(由浏览器拥有)-> 工具包(如 jquery、下划线等)-> 框架(主干、角度、反应、等)-> 您的应用程序代码。每一层在以某种方式表现之前都依赖于这些层。在任何语言中,向后追溯并改变核心数据类型的工作方式总是一个问题。
  • @NoOne 您可以通过多种方式在应用程序代码中获得所需的功能。您可以使用 underscore.string 之类的库、使用 Object.create()、mixins 或工厂方法将功能构建到您的对象中。 JavaScript Patterns (amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) 是一本涵盖很多技术的好书。
【解决方案2】:

你永远不应该扩展Object.prototype。它所做的不仅仅是破坏 jQuery;它完全打破了 Javascript 的“object-as-hashtables”特性。不要这样做。

你可以问 John Resig,他会告诉你same thing

【讨论】:

  • 扩展Object.prototype 很好。需要注意的是在for..in 循环中使用hasOwnProperty。自 2.0 以来,包括 Safari 在内的所有主流浏览器都支持它。 jQuery 在其for..in 循环中没有这样做只是懒惰。性能影响可以忽略不计,Resig 知道这一点:osdir.com/ml/jquery-dev/2009-02/msg00543.html 不过只是我的看法。
  • @Crescent 比这要深得多。当然,您可以使用 for...in 这样的循环来解决这个问题,但是在 Javascript 中使用 object-as-hashtables 可以做很多其他事情。比如toStringvalueOf等就不列举了。这确实有影响。此外,当你是一个被很多人使用的图书馆的首席开发人员时,你不能将他的决定归咎于懒惰。我认为一个更好的词是谨慎。
  • @JoshStodola - 实际上这不是懒惰而是无知(大多数人不知道使用 hasOwnProperty...)在 jQuery Resig 的早期根本不知道这样做。在意识到这个缺陷后不久,他总是接受这个修复作为未来需要的实现,但可惜它从来没有进入核心。
  • @CrescentFresh 非常感谢!几个月来我一直在寻找那个帖子! (我是那个帖子/代码差异的作者,也是与 Resig 一起修复 jQuery 中的错误的人。)我认为它永远丢失了。作为更新,我很遗憾地报告几个月前 Resig 正式关闭了票,因为无法修复。这个缺陷在 UI 库中太普遍了,糟糕的编码实践在社区中太普遍了。这就是我不再使用 jQuery 的原因 :( 但 Sizzle 与 Object.prototype 扩展兼容,这确实是我最喜欢的 jQuery 部分。
  • 恕我直言,此答案现在不正确且已过时。在 ES5 中,您可以使用 Object.defineProperty 安全地扩展 Object.prototype,并使用创建 不可枚举 属性的默认设置。
【解决方案3】:

我同意,向Object.prototype 添加内容需要谨慎,应该避免。寻找其他解决方案,例如:

将其添加到 Object,然后根据需要使用 callapply 访问它。 例如:

Object.foo = function () { return this.whatever()}

然后通过以下方式在对象上调用它:

Object.foo.call(Objname);  // this invokes the function as though it were a
                           // method of Objname.  That is, its like Objname.foo()

为了好玩,您可以添加以下内容(是的,我知道这有点危险......):

Function.using = Function.call; // syntactic sugar

现在你可以写Object.foo.using(Objname),它读起来就像一句句子。

但作为一项规则,请远离更改任何大型原型。

【讨论】:

    【解决方案4】:

    我想在我的所有对象中实现“真正的”面向对象,就像这样:

    interface Object
    {
        GetType: () => string;
        ToString: () => string;
        GetHashcode: () => number;
        Equals: (obj: any) => boolean;
    }
    

    由于 Object.prototype 破坏了 JQuery,我默认使用上述解决方案来使用 defineProperty 但它不带任何参数。

    好消息是您可以破解 defineProperty 并实际接受参数。这是我的实现:

    Object.defineProperty(Object.prototype, "Equals",
        {
            value: function (obj: any)
            {
                return obj == null && this == null
                        ? true
                        : obj != null && this == null
                            ? false
                            : obj == null && this != null
                                ? false
                                : this.GetHashcode() == obj.GetHashcode();
            },
            enumerable: false
        });
    

    这可行,不会与 JQuery 冲突。

    【讨论】:

    • 这似乎是最好的答案。似乎也不与 Google Maps API 冲突。
    【解决方案5】:

    我怀疑向 Object.prototype 添加函数会直接破坏 jQuery。只需确保整个站点中的每个 for..in 循环都包含在 hasOwnProperty 检查中,因为您已全局添加该函数,并且迭代它的结果可能是不可预测的:

    Object.prototype.foo = function() {};    
    var myObject = {m1: "one", m2: "two" };
    
    for(var i in myObject) { if(myObject.hasOwnProperty(i)) {
       // Do stuff... but not to Object.prototype.foo
    }}
    

    【讨论】:

    • 好吧,如果我注释掉 Object.prototype.foo 声明,一切正常。此外,在它崩溃的那一刻,它甚至没有到达我的任何代码,超出了 foo 声明。
    • 你说得对,它不会直接破坏 jQuery,它会破坏 Javascript!
    • 取决于你如何看待它。理想情况下,您应该能够毫无问题地扩展 Object,但实际上,是的,这是一个坏主意,而且很少有充分的理由。 Crockford 认为枚举添加到原型中的函数是“语言中的错误”,因此最佳实践是防御性的,并始终将 hasOwnProperty 添加到 for..in 循环中。这很糟糕,但我虔诚地这样做;)
    • 是的,它破坏了 jQuery,jQuery 将遍历您的 Object.prototype 扩展,但假定它不是一个函数。当该函数出现在迭代中时,它会引发异常。它不会破坏 JavaScript,它是 JavaScript 设计不可或缺的一部分。 @jshalvi - 你总是可以创建一个像 jQuery 的 $.each 这样的交互器函数并且只写一次。 Crockford 对该语言有一些误解,但他开创了狂野的西部 JavaScript 前沿并不是他的错,他在旅途中只遇到了一些错误。 :D