web-dev-qa-db-ja.com

JavaScriptでプロトタイププログラミングを使用する場合

以下の方法で、プロジェクト用の単純なウィジェットを開発するのにかなりの時間を費やしました。

var project = project || {};

(function() {

  project.elements = {
    prop1: val1,
    prop2: val2
  }

  project.method1 = function(val) {
    // Do this
  }

  project.method2 = function(val) {
    // Do that
  }

  project.init = function() {
    project.method1(project.elements.prop1)
    project.method2(project.elements.prop2)
  }
})()

project.init();

しかし、私は自分のフォーマットを次のように変更し始めました:

function Project() {
  this.elements = {
    prop1: val1,
    prop2: val2
  }

  this.method_one(this.elements.prop1);
  this.method_two(this.elements.prop2);
}

Project.prototype.method_one = function (val) {
  // 
};

Project.prototype.method_two = function (val) {
  //
};

new Project();

確かに、これらはばかげた例なので、アクセルに巻き込まれないでください。しかし、機能的な違いは何ですか、いつどちらかを選択する必要がありますか?

15
JDillon522

最初の違いは、次のように要約できます。thisは、クラスのInstanceを指します。 prototypeDefinitionを指します。

次のクラスがあるとします。

var Flight = function ( number ) { this.number = number; };

したがって、ここではクラスのすべてのインスタンスにthis.numberをアタッチしています。すべてのFlightに独自のフライト番号が必要なため、これは理にかなっています。

var flightOne = new Flight( "ABC" );
var flightTwo = new Flight( "XYZ" );

対照的に、prototypeは、すべてのインスタンスからアクセスできる単一のプロパティを定義します。

フライト番号を取得したい場合は、次のスニペットを記述するだけで、すべてのインスタンスがこの新しくプロトタイプ化されたオブジェクトへのReferenceを取得します。

Flight.prototype.getNumber = function () { return this.number; };

2番目の違いは、JavaScriptがオブジェクトのプロパティを検索する方法についてです。 Object.whateverを探している場合、JavaScriptはメインObjectオブジェクト(他のすべてが継承したオブジェクト)まで到達します、一致が見つかるとすぐに、それを返すか呼び出します。

しかし、それはプロトタイプ化されたプロパティに対してのみ起こります。したがって、上位層this.whateverのどこかにある場合、JavaScriptはそれを一致と見なさず、検索を続行します。

それが実際にどのように起こるか見てみましょう。

まず、JavaScriptでは[ほぼ]すべてがObjectsであることに注意してください。これを試して:

typeof null

では、Objectの内側を見てみましょう(最後に大文字のO.があることに注意してください)。 Google Chromeのデベロッパーツールで.を入力すると、その特定のオブジェクト内で使用可能なプロパティのリストが表示されます。

Object.

次に、Functionに対して同じことを行います。

Function.

nameメソッドに気付くでしょう。起動して起動し、何が起こるか見てみましょう。

Object.name
Function.name

次に、関数を作成します。

var myFunc = function () {};

そして、ここにもnameメソッドがあるかどうかを見てみましょう。

myFunc.name

空の文字列を取得する必要がありますが、それで問題ありません。エラーや例外は発生しません。

それでは、その神のようなObjectに何かを追加して、他の場所でも取得できるかどうか確認してみましょう。

Object.prototype.test = "Okay!";

そして、あなたは行き​​ます:

Object.prototype.test
Function.prototype.test
myFunc.prototype.test

すべてのケースで"Okay!"が表示されます。

各メソッドの長所と短所については、プロトタイピングを「より効率的な」方法として考えることができます。これは、各オブジェクトのプロパティ全体をコピーするのではなく、すべてのインスタンスで参照を保持するためです。一方、これはTightly Couplingの例であり、実際に理由を正当化できるまでは大きな問題です。 thisはコンテキストに関連するため、かなり複雑です。あなたはインターネットで無料でたくさんの良いリソースを見つけることができます。

とにかく、どちらの方法も言語ツールにすぎず、それはあなたとあなたが解決しようとしている問題に実際に依存し、より適切なものを選択します。

クラスのすべてのインスタンスに関連するプロパティが必要な場合は、thisを使用します。すべてのインスタンスで同じように機能するプロパティが必要な場合は、prototypeを使用します。

更新

サンプルスニペットに関して、最初のものはSingletonの例であるため、オブジェクト本体内でthisを使用することは理にかなっています。このようにモジュール化することで、例を改善することもできます(また、常にthisを使用する必要はありません)。

/* Assuming it will run in a web browser */
(function (window) {
    window.myApp = {
        ...
    }
})( window );

/* And in other pages ... */
(function (myApp) {
    myApp.Module = {
        ...
    }
})( myApp );

/* And if you prefer Encapsulation */
(function (myApp) {
    myApp.Module = {
         "foo": "Foo",
         "bar": function ( string ) {
             return string;
         },
         return {
             "foor": foo,
             "bar": bar
         }
    }
})( myApp );

2番目のスニペットはそれほど意味がありません。最初にthisを使用していて、後でprototypeを使用してハッキングしようとしているため、thisが機能しないため機能しませんprototypeよりも優先されます。そのコードからどのような期待があり、どのように機能していたかはわかりませんが、リファクタリングすることを強くお勧めします。

更新

thisprototypeに優先して詳しく説明するために、例を示し、それをどのように説明できるかを説明できますが、それをバックアップする外部リソースはありません。

例は非常に簡単です:

var myClass = function () { this.foo = "Foo"; };
myClass.prototype.foo = "Nice try!";
myClass.prototype.bar = "Bar";

var obj = new myClass;
obj.foo;     // Still contains "Foo" ...
obj.bar;     // Contains "Bar" as expected

説明は、私たちが知っているように、thisはコンテキストに関連しています。したがって、コンテキストが準備できるまで、それは存在しません。コンテキストの準備ができたら?新しいインスタンスが作成されているとき!残りは今すぐ推測してください!つまり、prototypeの定義はありますが、thisが優先されるのは、その時点で作成される新しいインスタンスがすべてであるためです。

6
53777A