20

要約:

わかりました、この質問をしてからしばらく経ちました。いつものように、Object.prototypeこことウェブ上の他の場所の両方で、それに対するすべての有効な議論にもかかわらず、とにかく行って拡張しました. 私はそのような頑固なジャークだと思います。

私は、新しいメソッドが予想される動作を台無しにすることを防ぐ決定的な方法を考え出そうとしましたが、これは非常に困難であることが判明しましたが、有益なことです。
JavaScript について非常に多くのことを学びました。少なくとも、ネイティブ プロトタイプをいじるほど無作法なことを試みるつもりはありません ( String.prototype.trimIE < 9 を除く)。

この特定のケースでは、ライブラリを使用していないため、競合は私​​の主な関心事ではありませんでした。しかし、ネイティブ プロトタイプをいじる際に発生する可能性のある事故をもう少し深く掘り下げたので、このコードをライブラリと組み合わせて試すことはおそらくないでしょう。

このプロトタイプ アプローチを調べることで、モデル自体の理解が深まりました。私はプロトタイプを柔軟な伝統的な抽象クラスの何らかの形として扱っていたため、伝統的な 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.

おそらく、誰かが私がどこで間違っているかを私に伝えるのに、このチャップよりも優れた仕事をしてくれるでしょう。人々が のプロトタイプに触れてはならない
理由を探しているうちに、1 つのことが明らかになりました。それはループを壊しますが、繰り返しになりますが、多くのフレームワークもそうです。独自の継承チェーンは言うまでもありません。したがって、オブジェクトのプロパティをループするときにチェックを含めないのは悪い習慣だと思います。Objectfor..in.hasOwnProperty

これもかなり興味深いと思いました。繰り返しますが、1 つのコメントは非常に明白です: ネイティブ プロトタイプを拡張することは悪い習慣ですが、V8 の人々がそれを行う場合、彼らが間違っていると誰が言えますか?
私は知っています、その議論はまったく積み重なっていません。

ポイントは、上記のコードに問題があるとは本当に思えないということです。私はそれが好きで、たくさん使っていますが、これまで一度もがっかりしたことはありません. Object プロトタイプにさらにいくつかの関数を追加することも考えています。誰かが私がすべきでない理由を教えてくれない限り、それはそうです。

4

3 に答える 3

14

実際のところ、自分が何をしていて、どのような費用がかかるかを知っていれば問題ありません。しかし、それは大きな「もし」です。費用の例:

  • 空のオブジェクトには列挙可能なプロパティがないという圧倒的な慣習があるため、を拡張する環境で使用することを選択したライブラリで広範なテストを行う必要があります。Object.prototypeに列挙可能なプロパティを追加することObject.prototypeで、その規則を偽にしています。たとえば、これは非常に一般的です。

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

    ... 「getProperties」ではなく、「a」と「b」のみが表示されるという圧倒的な慣習があります。

  • コードに取り組んでいる人は誰でも、その規則 (上記) が守られていないという事実を教育する必要があります。

サポートされている場合は (および同様の方法で)を使用することで上記の問題を緩和できますが、2014 年になっても、適切にサポートされていないObject.definePropertyIE8 などのブラウザーが引き続きかなり使用されていることに注意してください (ただし、XP が正式に EOL になったため、これがすぐに変更されることを期待できます)。 'd)。これは、 を使用すると、列挙不可能なプロパティ (ループに表示されないプロパティ)を追加できるため、問題が大幅に軽減されるためです (その時点で、名前の競合が主に心配されます)。正しく実装されているシステムで動作します(そして、正しい実装は「シミング」できません)。Object.definePropertyfor-inObject.defineProperty

あなたの例では、私は追加getPropertiesしませんObject.prototype; ObjectES5のように、オブジェクトを引数として追加して受け入れますgetPrototypeOf

Prototype ライブラリは、ループArray.prototypeにどのように影響するかという理由で、拡張に対して多くの批判を受けることに注意してください。for..inそして、それは単なるArrays (ガードをfor..in使用していない限り、とにかく使用しないでください)。hasOwnPropertyString(Number(name)) === name

...もしV8の人々がそれをするなら、彼らが間違っていると私は誰が言いますか?

Object.definePropertyV8 は ES5 に完全に準拠したエンジンであるため、V8 では信頼できます。

プロパティが列挙できない場合でも、問題があることに注意してください。何年も前に、プロトタイプは (間接的に) でfilter関数を定義しましたArray.prototype。そして、期待どおりのことを行います。反復子関数を呼び出し、関数が選択した要素に基づいて新しい配列を作成します。その後、ECMAScript5 が登場し、Array.prototype.filterほとんど同じことを行うように定義されました。しかし、問題があります。ほぼ同じことです。特に、呼び出されるイテレータ関数のシグネチャが異なります (ECMAScript5 には、Prototype にはない引数が含まれています)。それよりもはるかに悪い可能性がありました (TC39 がプロトタイプを認識しており、それとの衝突を意図的に回避したのではないかと私は推測していますが、証明することはできません)。

ですから: もしそうするなら、リスクとコストに注意してください。既製のライブラリを使用しようとした結果として遭遇する可能性のある醜いエッジケースのバグは、本当に時間を浪費する可能性があります...

于 2012-05-25T15:57:40.730 に答える
3

Objectフレームワークとライブラリが一般的にあなたが提案していることをした場合、2つの異なるフレームワークが2つの異なる機能を同じメソッド(またはArrayNumber...または既存のオブジェクトプロトタイプ)として定義することがすぐに起こります。したがって、そのような新しい機能を独自の名前空間に追加することをお勧めします。

たとえば...想像してみてください。オブジェクトをjsonにシリアル化するライブラリと、オブジェクトをXMLにシリアル化するライブラリがあり、どちらもその機能を次のように定義します。

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

そして、後で定義されたものだけを使用することができます。ですから、彼らがこれをしない方が良いのですが、代わりに

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

また、新しい機能が新しいECMAscript標準で定義されているか、ブラウザベンダーによって追加されている可能性もあります。したがって、ブラウザにもserialize関数が追加されると想像してください。これも、同じ関数を定義したライブラリとの競合を引き起こします。ライブラリの機能がブラウザに組み込まれているものと同じであったとしても、解釈されたスクリプト関数はネイティブ関数をオーバーライドし、実際にはより高速になります。

于 2012-05-25T16:00:02.813 に答える
1

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);
});

私はこれを主に、それらをサポートしていない標準定義関数を実装に追加するために使用しました。

于 2012-05-25T16:20:24.397 に答える