Object.prototype是Verboten?

重点回顾:

好吧,我问这个问题已经有一段时间了。 和往常一样,无论如何,尽管在网络的这里和其他地方都有针对它的所有有效论据,但我仍然增加了Object.prototype 。 我想我就是那种顽固的混蛋。

我试图想出一种防止新方法破坏任何预期行为的结论性方法,这被证明是一件非常艰难但是很有意义的事情。
我学到了很多关于JavaScript的东西。 一点也不会,我不会尝试任何像原生原型一样混乱的东西(除了针对IE <9的String.prototype.trim )。

在这个特殊情况下,我不使用任何库,所以冲突并不是我最关心的问题。 但是当玩弄原生原型时,为了更深入地挖掘可能的错误,我不太可能将这些代码与任何lib结合使用。

通过研究这种原型方法,我对模型本身有了更好的理解。 我把原型当作某种形式的灵活的传统抽象类,让我紧紧抓住传统的OOP思想。 这个观点并没有真正做到原型模型正义。 道格拉斯克罗克福德写了关于这个陷阱,遗憾的是粉红色的背景使我无法阅读整篇文章。

我决定更新这个问题的机会,读者可能会亲眼看到。 我所能说的是:无论如何,请做。 我希望你在决定放弃这个相当愚蠢的想法之前,像我一样学习几件简洁的事情。 一个简单的功能可能工作得很好,甚至更好,特别是在这种情况下。 毕竟,它的真正之处在于,通过添加3行代码,您可以使用相同的功能来增强特定对象的原型。


我知道我将要问一个已经存在很长一段时间的问题,但是:为什么Object.prototype被认为是禁用的? 它就在那里,它可以像任何其他原型一样得到增强。 那么,为什么你不应该利用这一点。 在我看来,只要你知道你在做什么,就没有理由避开对象原型。
以此方法为例:

if (!Object.prototype.getProperties)
{
    Object.prototype.getProperties = function(f)
    {
        "use strict";
        var i,ret;
        f = f || false;
        ret = [];
        for (i in this)
        {
            if (this.hasOwnProperty(i))
            {
                if (f === false && typeof this[i] === 'function')
                {
                    continue;
                }
                ret.push(i);
            }
        }
        return ret;
    };
}

基本上, for...in ,它与旧的一样for...in既可以保持函数安全,也可以一遍又一遍地重复写入。 我知道它会被添加到所有对象中,并且由于几乎JavaScript中的每个继承链都可以追溯到Object.prototype ,但在我的脚本中,我认为它是两个Object.prototype中较小的一个。

也许,有人可以做得更好,告诉我我的错在哪里,除此之外。
在寻找人们不接触Object的原型的原因时,有一件事情一直在持续:它打破了for..in循环,但是再一次:许多框架也这样做,更不用提你自己的继承链了。 因此,在我的脑海里,当循环一个对象的属性时不包含.hasOwnProperty检查是一个不好的做法。

我也觉得这很有趣。 再次强调:一个评论是非常明确的:扩展本地原型是不好的做法,但如果V8的人这样做,我是谁说他们错了?
我知道,这个论点并不完全相同。

重点是:我不能真正看到上述代码的问题。 我喜欢它,使用它很多,迄今为止,它并没有让我失望一次。 我甚至想过在对象原型中添加更多的函数。 除非有人能告诉我为什么我不应该那样做。


事实是,只要你知道你在做什么和成本是多少就没关系。 但这是一个很大的 “如果”。 成本的一些例子:

  • 您需要对选择使用的任何库进行广泛的测试,以Object.prototype的环境,因为压倒性的惯例是空白对象不具有可枚举的属性。 通过将一个枚举属性添加到Object.prototype ,可以使该约定为假。 例如,这很常见:

    var obj = {"a": 1, "b": 2};
    var name;
    for (name in obj) {
        console.log(name);
    }
    

    ...... 压倒性的惯例是只有“a”和“b”才会出现,而不是“getProperties”。

  • 任何从事代码工作的人都必须接受这一惯例(上文)未被遵守。

  • 如果支持,您可以通过使用Object.defineProperty (以及类似的)来缓解上述情况,但是要注意,即使在2014年,IE8等浏览器也不能很好地支持它(尽管我们可以希望这会在XP是正式EOL'd)。 这是因为使用Object.defineProperty ,你可以添加不可枚举的属性(那些不能在for-in循环中显示的属性),所以你的问题会少很多(在那个时候,你主要担心名字冲突) - 但它只适用于正确实现Object.defineProperty系统(并且正确的实现不能“闪烁”)。

    在你的例子中,我不会将getProperties添加到Object.prototype ; 我将它添加到Object并接受该对象作为参数,就像ES5为getPrototypeOf和类似做的一样。

    请注意,Prototype库因扩展Array.prototype而受到很大Array.prototype因为它影响着for..in循环。 这只是Array s(你不应该使用for..in (除非你使用hasOwnProperty后卫,而且很可能是String(Number(name)) === name )。

    ...如果V8的人这样做,我是谁说他们错了?

    在V8上,您可以依赖Object.defineProperty ,因为V8完全符合ES5兼容引擎。

    请注意,即使属性不可枚举,也存在问题。 几年前,Prototype(间接)在Array.prototype上定义了一个filter函数。 它可以满足你的期望:调用一个迭代器函数,并根据函数选择的元素创建一个新数组。 然后ECMAScript5出现并定义了Array.prototype.filter来做同样的事情。 但难就难在:同样事情。 特别是,被调用的迭代器函数的签名是不同的(ECMAScript5包含Prototype没有的参数)。 它可能比这更糟糕(我怀疑 - 但不能证明 - TC39知道Prototype并故意避免与它产生太多冲突)。

    所以:如果你打算这样做,请注意风险和成本。 由于尝试使用现成的库,可能遇到的丑陋的边缘情况错误实际上可能会花费您的时间......


    如果框架和库通常做你提出的,很快就会发生,两个不同的框架将把两个不同的功能定义为Object (或ArrayNumber ...或任何现有对象原型)的相同方法。 因此,将这种新功能添加到自己的名称空间中会更好。

    例如...想象一下,您将有一个将对象序列化为json的库和一个将它们序列化为XML的库,并且两者都将它们的功能定义为

    Object.prototype.serialize = function() { ... }
    

    你只能使用稍后定义的那个。 所以如果他们不这样做,反而会更好

    JSONSerializingLibrary.seralize = function(obj) { ... }
    XMLSerializingLibrary.seralize = function(obj) { ... }
    

    也可能发生这样的情况:在新的ECMAscript标准中定义了新功能,或者由浏览器供应商添加了新功能。 所以想象你的浏览器也会添加一个serialize函数。 这又会与定义相同功能的库发生冲突。 即使库的功能与内置在浏览器中的功能相同,解释的脚本函数也会覆盖原生函数,而实际上它会更快。


    见http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way

    其中提到了一些但并非全部的反对意见。 如果在Object.prototype中已经存在特定于域的方法,则可以通过引发异常来缓解关于不同库创建冲突方法的异议。 这至少会在发生这种不良事件时提供警报。

    受这篇文章的启发,我开发了以下内容,也可以在引用页面的评论中找到。

    !Object.implement && Object.defineProperty (Object.prototype, 'implement', {
      // based on http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way
      value: function (mthd, fnc, cfg) { // adds fnc to prototype under name mthd
          if (typeof mthd === 'function') { // find mthd from function source
            cfg = fnc, fnc = mthd;
            (mthd = (fnc.toString ().match (/^functions+([a-z$_][w$]+)/i) || [0, ''])[1]);
          }
          mthd && !this.prototype[mthd] && 
            Object.defineProperty (this.prototype, mthd, {configurable: !!cfg, value: fnc, enumerable: false});
        }
    });
    
    Object.implement (function forEach (fnc) {
      for (var key in this)
        this.hasOwnProperty (key) && fnc (this[key], key, this);
    });
    

    我主要用它来在不支持它们的实现上添加标准的定义函数。

    链接地址: http://www.djcxy.com/p/4685.html

    上一篇: Object.prototype is Verboten?

    下一篇: How to remove key+value pair from an object of Array