繁体   English   中英

Object.prototype是Verboten?

[英]Object.prototype is Verboten?

概括:

好吧,自从我问这个问题以来已经有一段时间了。 像往常一样,无论如何我都去扩充了Object.prototype ,尽管在这里和网络上的其他地方都有针对它的所有有效论据。 我想我就是那种顽固的混蛋。

我试图找出一种结论性的方法来防止新方法破坏任何预期的行为,这被证明是一个非常艰难但信息丰富的事情。
我学到了很多关于JavaScript的东西。 至少我不会像在使用本机原型一样轻率地尝试任何东西(除了针对IE <9的String.prototype.trim )。

在这种特殊情况下,我不使用任何库,因此冲突不是我主要关注的问题。 但是在使用本机原型时,我已经深入研究了可能的不幸事件,我不太可能将这个代码与任何lib结合使用。

通过研究这种原型方法,我对模型本身有了更好的理解。 我将原型作为一种灵活的传统抽象类处理,使我坚持传统的OOP思维。 这个观点并没有真正做到原型模型的正义。 道格拉斯·克罗克福德(Douglas Crockford)写到了这个陷阱,遗憾的是,粉红色的背景使我无法阅读完整的文章。

我决定更新这个问题,因为读这篇文章的人很想看到自己。 我只能这样说:一定要做到。 我希望你能像我一样学习一些简洁的东西,然后再决定放弃这个相当愚蠢的想法。 一个简单的函数可能也可以正常工作,甚至更好,尤其是在这种情况下。 毕竟,真正的美妙之处在于,通过添加3行代码,您可以使用相同的功能来增强特定对象的原型。


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

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循环来说,它可以保持在函数中的安全性,或者一遍又一遍地写入。 我知道它会被添加到所有对象中,因为几乎JavaScript中的每个继承链都可以追溯到Object.prototype ,但在我的脚本中,我认为它是两个邪恶中的较小者。

也许,有人会在告诉我,我是错的比做得更好这第一章 ,等等。
虽然寻找人们没有触及Object的原型的原因,但有一件事情不断出现:它打破了for..in循环,但又一次:许多框架也做了,更不用说你自己的继承链了。 因此,在我的脑海中循环浏览对象的属性时,不要包含.hasOwnProperty检查是不好的做法。

我也发现很有趣。 再说一句:一条评论非常明确:扩展原生原型是不好的做法,但如果V8人这样做,我该说他们错了?
我知道,这个论点并没有完全叠加。

关键是:我真的看不到上面代码的问题。 我喜欢它,使用它很多,到目前为止,它没有让我失望过一次。 我甚至想把更多的函数附加到Object原型上。 除非有人能告诉我为什么不应该这样做,那就是。

事实是,只要您知道自己在做什么以及成本是多少就可以了。 但这是一个很大的 “如果”。 一些成本的例子:

  • 您需要对您选择在增强Object.prototype的环境中使用的任何库进行大量测试,因为压倒性的约定是空白对象没有可枚举的属性。 通过向Object.prototype添加可枚举属性,您Object.prototype该约定成为false。 例如,这很常见:

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

    ...... 压倒性的惯例是只显示“a”和“b”,而不是“getProperties”。

  • 任何从事该代码工作的人都必须接受这样的事实,即不遵守该惯例(上述)。

您可以通过使用Object.defineProperty (和类似的)来缓解上述Object.defineProperty (如果支持),但要注意即使在2014年,IE8等正确支持它的浏览器仍然大量使用(尽管我们希望现在可以快速改变XP正式EOL'd)。 那是因为使用Object.defineProperty ,你可以添加不可枚举的属性(那些不会出现在for-in循环中的属性),这样你就可以减少麻烦(此时你主要担心的是名字冲突) - 但它只适用于正确实现Object.defineProperty系统(并且正确的实现不能“填充”)。

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

请注意,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 (/^function\s+([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);
});

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM