web-dev-qa-db-ja.com

プロトタイプメソッドでクラスを拡張することの何が問題になっていますか?

昨夜、何人かの同僚とバーにいた。彼らは、基本的なJavaScriptオブジェクトの機能をプロトタイプメソッドで拡張することは悪い考えだと述べました。

たとえば、階乗を見つけるためのメソッドを作成したとしましょう

Number.prototype.factorial = function(n) {
  return n == 0 ? 1 : factorial(n - 1)

}

彼らは、プロトタイプを作成するのにいくつかの危険があったと言いました。なぜこれが悪い習慣なのでしょうか?

3
user439133

問題はglobal scopeです。つまり、変更はすべてコードベース全体に影響し、コードベースの任意の場所で変更が影響を受ける可能性があります。

配列のすべての要素をループしたいとします。ブラウザがまだそれをサポートしていないと想像してください。そのためには、次のように呼び出されるメソッドforEachを作成します。

[5, 7, 1, 1, 3].forEach(function (element) {
    // Do something here.
});

あなたはこのメソッドを実装し、いくつかのEdgeケース(たとえば、空の配列についてはどうですか?)について考えましたが、見逃したわずかなパフォーマンスバグを除いてすべて問題ありません。

さて、他のどこかで、あなたの同僚はforEachメソッドを持つという素晴らしいアイデアを持っていましたが、すでに実装していることを知りません。実際、彼はメソッドがまだ存在しないことを確認するために配列でforEachを呼び出そうとしましたが、コードがすべてのページで実行されないため、[].forEachundefinedを返します。

したがって、彼は独自の実装を作成しますが、空の配列をテストするのを忘れており、実際、配列が空の場合、コードは失敗します。

コードに戻ると、ある日、forEachの周りにエラーがあることを伝えるバグレポートが見つかりました。配列が空の場合、失敗します。同僚が自分のforEachを実装していて、一部のページであなたを上書きしていることを知りません。したがって、あなたが懸念しているのはあなたのコードだと思うので、ユニットテストに合格している間、なぜそれが失敗するのかと思って何時間も費やします。

犯人、つまり複製されたforEachを見つけ、同僚のコードを削除することにしました。翌日、新しいバグレポートは[].forEachは、一部のページ(コードが実行されないページ)でundefinedを返します。 WTF!

同僚と一緒に、この問題の解決にさらに2時間を費やします。最後に、コードがすべてのページで実行され、forEachメソッドがまだない場合にのみプロトタイプが追加されるクリーンなコードが得られます。

if (![].forEach) {
    Array.prototype.forEach = function (...) {
        ...
    };
}

その間、コードにあった小さなパフォーマンスバグが発見されましたが、それはより機能的なものであり、同僚の何人かがこのバグに依存しています。いくつかの場所では、列挙を遅くするために列挙が絶対に必要ですいくつかの画像を表示するのに十分な時間があります。彼らのコードを書き直すことは悪夢のように思われるので、あなたはバグを残すことに決めます。

1年後のある朝、数十の顧客からの数十のバグレポートを発見します。何かが起こっています。チームの誰かがすべてのバグレポートがChromeのユーザーからのものであることを発見し、さらに分析すると、Chromeの新しくリリースされたバージョンのJavaScriptエンジンにはforEachがあり、違いがあります同僚が依存していたバグ/機能が含まれていないことを確認します。今度は、チームが2日かけて、バグのないforEachの新しい実装にコードを適応させます。

9

AbhiはStack Overflowで、一般的なオブジェクトに対してそれを実行したくない理由を説明しましたが、独自のクラスでは、それがその方法です。要するに、答えは[〜#〜] depends [〜#〜]です。

オブジェクトを拡張すると、その動作が変わります。

独自のコードでのみ使用されるオブジェクトの動作を変更しても問題ありません。ただし、他のコードでも使用されているものの動作を変更すると、他のコードが破損する危険があります。

JavaScriptでオブジェクトと配列クラスにメソッドを追加することになると、JavaScriptの仕組みにより、何かを壊すリスクが非常に高くなります。長年の経験から、この種のものはJavaScriptにあらゆる種類のひどいバグを引き起こすことがわかりました。

カスタム動作が必要な場合は、ネイティブクラスを変更するのではなく、独自のクラス(おそらくサブクラス)を定義する方がはるかに優れています。そうすれば、何も壊さなくなります。

クラスをサブクラス化せずに動作を変更する機能は、優れたプログラミング言語の重要な機能ですが、まれに、注意して使用する必要がある機能です。

また、これにはパフォーマンスの側面もあります。

あるクラスのプロトタイプにプロパティを追加したとしましょう。 JSが機能する方法は、任意のオブジェクトのプロパティにアクセスしようとすると、このオブジェクトでプロパティを見つけようとします。見つからない場合、JSはプロトタイプの確認とプロトタイプのプロトタイプの確認を続けます。プロトタイプが見つからない最上部に到達するまで。プロパティをクラスの直接のプロトタイプに追加すると、JSの一般的なオブジェクトのプロトタイプに追加するよりも、基本的にはより効率的なコードが生成されます。

しかし、これが意味することは、それが何をするかを理解しながら慎重に使用することです。不適切に使用すると、何かが悪い。

1
Alexus