JavaScriptでの「プロトタイプ」と「これ」の使用?


776

違いは何ですか

var A = function () {
    this.x = function () {
        //do something
    };
};

そして

var A = function () { };
A.prototype.x = function () {
    //do something
};


このキーワードの概念は、ここで明示的に説明されていますscotch.io/@alZami/understanding-this-in-javascript
AL-

1
「この」スレッドを読むと、JSがどれほどひどいのか、そしてその原理が多くの開発者にとってどれほど不明確であるかがわかります。理解しやすい言語の何が問題になっていますか?私は、開発者がビジネスまたは開発作業にまったくまたはほとんど価値のない混乱しているテクノロジーを拒否する声を上げる時がきたと思います。
NoChance 2017年

オブジェクト:a1.x !== a2.x; プロトタイプ:a1.x === a2.x
Juan Mendes

回答:


467

例には非常に異なる結果があります。

違いを見る前に、次のことに注意してください。

  • コンストラクタのプロトタイプは、インスタンスのプライベート[[Prototype]]プロパティを介してインスタンス間でメソッドと値を共有する方法を提供します。
  • 関数のthisは、関数の呼び出し方法またはバインドの使用(ここでは説明しません)によって設定されます。関数はオブジェクト(例えば上で呼び出された場合はmyObj.method()、その後)このメソッド参照内のオブジェクト。これが呼び出しまたはbindの使用によって設定されていない場合、デフォルトではグローバルオブジェクト(ブラウザのウィンドウ)または厳密モードでは未定義のままになります。
  • JavaScriptはオブジェクト指向言語です。つまり、ほとんどの値は関数を含むオブジェクトです。(文字列、数値、ブール値はオブジェクトではありません。)

だからここに問題のスニペットがあります:

var A = function () {
    this.x = function () {
        //do something
    };
};

この場合、変数にAは関数への参照である値が割り当てられます。その関数がを使用して呼び出される場合A()、関数のthisは呼び出しによって設定されないため、デフォルトでグローバルオブジェクトになり、式this.xは有効になりwindow.xます。その結果、右側の関数式への参照がに割り当てられwindow.xます。

の場合:

var A = function () { };
A.prototype.x = function () {
    //do something
};

非常に異なる何かが発生します。最初の行でAは、変数に関数への参照が割り当てられています。JavaScriptでは、すべての関数オブジェクトにデフォルトでプロトタイププロパティがあるため、A.prototypeオブジェクトを作成するための個別のコードはありません。

2行目では、A.prototype.xに関数への参照が割り当てられています。これにより、xプロパティが存在しない場合は作成され、存在する場合は新しい値が割り当てられます。したがって、オブジェクトのxプロパティが式に含まれる最初の例との違い。

別の例を以下に示します。それは最初のものと似ています(そしておそらくあなたが尋ねようとしていたものです):

var A = new function () {
    this.x = function () {
        //do something
    };
};

この例ではnew、関数がコンストラクターとして呼び出されるように、関数式の前に演算子が追加されています。で呼び出されるとnew、関数のthisは、プライベート[[Prototype]]プロパティがコンストラクターのパブリックプロトタイプを参照するように設定された新しいオブジェクトを参照するように設定されます。したがって、割り当てステートメントでは、xこの新しいオブジェクトにプロパティが作成されます。コンストラクターとして呼び出されると、関数はデフォルトでthisオブジェクトを返すため、個別のreturn this;ステートメントを作成する必要はありません。

Axプロパティがあることを確認するには:

console.log(A.x) // function () {
                 //   //do something
                 // };

コンストラクタを参照する唯一の方法はA.constructorを経由するため、これは珍しいnewの使用です。それを行うことははるかに一般的です:

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

同様の結果を得る別の方法は、すぐに呼び出される関数式を使用することです。

var A = (function () {
    this.x = function () {
        //do something
    };
}());

この場合、A右側の関数呼び出しの戻り値が代入されています。ここでも、これは呼び出しで設定されていないため、グローバルオブジェクトを参照し、this.x有効window.xです。関数は何も返さないため、のA値を持ちますundefined

これら2つのアプローチの違いは、JavaScriptオブジェクトをJSONとの間でシリアル化および逆シリアル化する場合にも明らかになります。オブジェクトのプロトタイプで定義されたメソッドは、オブジェクトをシリアル化するときにシリアル化されません。これは、たとえば、オブジェクトのデータ部分だけをシリアル化したいが、メソッドではない場合に便利です。

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

関連する質問

補足: 2つの方法の間に大幅なメモリの節約はないかもしれませんが、プロトタイプを使用してメソッドとプロパティを共有すると、独自のコピーを持つ各インスタンスよりもメモリの使用量が少なくなる可能性があります。

JavaScriptは低水準言語ではありません。メモリの割り当て方法を明示的に変更する方法として、プロトタイピングや他の継承パターンを考えることはあまり価値がないかもしれません。


49
@keparo:あなたは間違っている。すべてのオブジェクトには[内部]プロトタイプオブジェクト(ある可能性がありますnull)がありますが、これはprototypeプロパティとは非常に異なります。これは、関数上にあり、すべてのインスタンスのプロトタイプがで構築されるときに設定されるプロパティnewです。これは本当に87 upvotes :-(だと信じてすることはできません
Bergi

8
"The language is functional"これが機能的意味であると確信していますか?
phant0m 2012

23
@Bergiがプロトタイプについて言ったことは2番目です。関数にはプロトタイププロパティがあります。関数を含むすべてのオブジェクトには、Object.getPrototypeOf(myObject)または一部のブラウザーではmyObject .__ proto__を使用してアクセスできる別の内部プロパティがあります。プロト・プロパティは、プロトタイプチェーン(またはこのオブジェクトが継承するオブジェクト)内のオブジェクトの親を示しています。プロトタイププロパティ(関数のみ)は、関数を利用して新しいキーワードを使用して新しいオブジェクトを作成するオブジェクトの親になるオブジェクトを示しています。
ジムクーパー

11
この記事はかなり見当違いであり、これがどのように設定されているかを混乱させます。書き直し作業中。
RobG 2014

37
この答えはかなり奇妙で、質問の要点を完全に逃しているようです。質問はコンストラクタとプロトタイプ内で型プロパティを定義することに関して非常に一般的なもののようですが、答えAの半分は関数として使用した場合に何が起こるかに関するものであり、残りの半分は不明瞭で正統でない方法についてです簡単なもの。
JLRishe 2014年

235

他の人が最初のバージョンを言ったように、「this」を使用すると、クラスAのすべてのインスタンスが関数メソッド「x」の独自の独立したコピーを持つことになります。一方、「プロトタイプ」を使用すると、クラスAの各インスタンスがメソッド「x」の同じコピーを使用することになります。

この微妙な違いを示すコードを次に示します。

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

他の人が述べたように、いずれかの方法を選択するさまざまな理由があります。私のサンプルは、違いを明確に示すためのものです。


5
これは私が予期することですが、上記のようにAxを変更した後で新しいオブジェクトをインスタンス化したとき、シングルトンのようにAを使用しない限り、「A」が表示されます。jsbin.com/omida4/2/edit
jellyfishtree

19
それは私の例が間違っていたからです。それは2年間だけ間違っていました。はぁ。しかし、ポイントはまだ有効です。実際に機能するもので例を更新しました。指摘してくれてありがとう。
Benry

4
これは静的メソッドです!:D

6
はい...「プロトタイプ」は、静的またはクラスレベルを意味します...作成されたすべてのインスタンスによって共有されます...一方、「これ」は、各インスタンスが独自のコピーを持つインスタンスメソッドです
Aneer Dev

7
静的ではありません。ほとんどのオブジェクト指向言語で使用される静的thisは、メソッドの所有者であるオブジェクトへの依存がないことを意味します。つまり、メソッドにはその所有者であるオブジェクトがありません。この場合this、例のクラスAに示すように、オブジェクトがあります。
CJStuart 2015年

152

次の2つの例を見てください。

var A = function() { this.hey = function() { alert('from A') } };

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

ここにいるほとんどの人々(特にトップ評価の回答)は、なぜかを説明することなく、自分たちの違いを説明しようとしました。これは間違っていると思います。最初に基礎を理解すれば、違いが明らかになります。最初に基礎を説明してみましょう...

a)関数はJavaScriptのオブジェクトです。JavaScriptのすべてのオブジェクトは内部プロパティを取得します(つまり、他のプロパティのようにアクセスすることはできません。ただし、Chromeなどのブラウザを除く)。これは、(Chromeに__proto__実際に入力anyObject.__proto__して、参照先を確認することができます。これだけです) 、プロパティ、それ以上。JavaScriptのプロパティ=オブジェクト内の変数、それ以上。変数は何をするのでしょうか。

この__proto__プロパティは何を指しているのでしょうか?まあ、通常は別のオブジェクトです(理由は後で説明します)。__proto__プロパティのJavaScript が別のオブジェクトをポイントしないようにする唯一の方法は、を使用することvar newObj = Object.create(null)です。これを行っても、__proto__プロパティSTILLはオブジェクトのプロパティとして存在し、別のオブジェクトを指すのではなく、を指すだけnullです。

ここでほとんどの人が混乱します:

JavaScriptで新しい関数を作成すると(これもオブジェクトです。覚えていますか?)、関数が定義されると、JavaScriptはその関数にという新しいプロパティを自動的に作成しますprototype。それを試してみてください:

var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined

A.prototype__proto__プロパティとはまったく異なります。この例では、「A」に「プロトタイプ」と呼ばれる2つのプロパティがあり__proto__ます。これは人々にとって大きな混乱です。prototypeそして、__proto__プロパティは、彼らが別の値を指している別のものだ、決して関連しています。

疑問に思われるかもしれません:なぜJavaScriptは__proto__すべてのオブジェクトにプロパティを作成しているのですか?さて、一言:委任。オブジェクトのプロパティを呼び出し、そのオブジェクトにプロパティがない場合、JavaScriptはによって参照され__proto__ているオブジェクトを探して、それが含まれているかどうかを確認します。ない場合は、そのオブジェクトの__proto__プロパティなどを調べ、チェーンが終了するまで続けます。したがって、名前のプロトタイプチェーン。もちろん、__proto__がオブジェクトをポイントせず、代わりにをポイントしているnull場合、幸運なことに、JavaScriptはそれを認識しundefined、プロパティに戻ります。

またprototype、関数を定義するときにJavaScriptが関数に対して呼び出されるプロパティを作成するのはなぜでしょうか。それはあなたをだまそうとするので、はい、それはクラスベースの言語のように機能することあなただます

例を先に進めて、以下から「オブジェクト」を作成しますA

var a1 = new A();

このことが起こったとき、バックグラウンドで何かが起こっています。a1新しい空のオブジェクトが割り当てられた通常の変数です。

new関数を呼び出す前に演算子を使用したという事実によりA()、バックグラウンドで追加の処理が行われました。newキーワードは、今の参照を新しいオブジェクトを作成a1し、そのオブジェクトは空です。さらに、次のことが起こります。

それぞれの関数定義で、呼び出されたprototype(プロパティとは異なり、アクセスできる)という新しいプロパティが作成されたと言い__proto__ましたか?さて、そのプロパティは現在使用されています。

これで、焼きたての空のa1オブジェクトが作成されました。JavaScriptのすべてのオブジェクトには、nullであろうと別のオブジェクトであろうと、__proto__何かを指す(a1また持っている)内部プロパティがあると言いました。どのようなnew作業が行うことは、それがその設定されることである__proto__関数のにポイントにプロパティをprototypeプロパティ。もう一度お読みください。それは基本的にこれです:

a1.__proto__ = A.prototype;

これA.prototypeは、空のオブジェクトにすぎないと(定義する前に別のオブジェクトに変更しない限りa1)。したがって、今は基本的にa1.__proto__同じものをA.prototype指します。つまり、その空のオブジェクトです。これらは両方とも、この行が発生したときに作成された同じオブジェクトを指しています。

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

ここで、var a1 = new A()ステートメントが処理されるときに別のことが起こります。基本的にA()は実行され、Aが次のようなものである場合:

var A = function() { this.hey = function() { alert('from A') } };

内部のすべてのものfunction() { }が実行されます。this.hey..行に到達すると、thisはに変わりa1、次のようになります。

a1.hey = function() { alert('from A') }

this変更の理由については説明しませんa1が、これは詳細を知るための素晴らしい回答です。

つまり、要約var a1 = new A()すると、バックグラウンドで3つのことが起こっています。

  1. まったく新しい空のオブジェクトが作成され、に割り当てられa1ます。a1 = {}
  2. a1.__proto__プロパティはA.prototype、(別の空のオブジェクト{})を指すのと同じものを指すように割り当てられます

  3. 関数A()this、ステップ1で作成された新しい空のオブジェクトに設定して実行されています(なぜにthis変更されるかについて上記で参照した回答を読んでくださいa1

次に、別のオブジェクトを作成してみましょう。

var a2 = new A();

ステップ1、2、3が繰り返されます。何か気づきましたか?キーワードは繰り返しです。ステップ1:a2新しい空のオブジェクトになります。ステップ2:その__proto__プロパティは同じものをA.prototype指します。最も重要なのは、ステップ3:関数A()が再度実行されることです。つまり、関数を含むプロパティをa2取得しheyます。a1およびa2という名前の2つのSEPARATE特性を有するhey2つの別々の機能を指しています!これで、同じことをする同じ2つの異なるオブジェクトに重複する関数があります。おっと... new Aすべての関数宣言が2のようなものよりも多くのメモリを消費した後、で作成された1000個のオブジェクトがある場合、これがメモリに与える影響を想像できます。これを防ぐにはどうすればよいですか?

__proto__プロパティがすべてのオブジェクトに存在する理由を覚えていますか?したがって、(存在しない)yoManプロパティを取得するとa1、その__proto__プロパティが参照され、オブジェクト(ほとんどの場合はオブジェクト)であるかどうかが確認されyoMan、含まれているかどうかが確認されます。そのオブジェクト__proto__などを参照します。そうする場合は、そのプロパティ値を取得して表示します。

したがって、誰かがこの事実+を作成するときにa1、その__proto__プロパティが同じ(空の)オブジェクトをA.prototype指すという事実を使用することを決定しました:

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

涼しい!これで、を作成するとa1、上記の3つのステップすべてが再度実行され、ステップ3では何もfunction A()実行されないため、何も実行されません。そして、私たちが行う場合:

a1.hey

これにはがa1含まれていないことがわかり、heyその__proto__プロパティオブジェクトをチェックして、それが含まれているかどうかが確認されます。

このアプローチでは、新しいオブジェクトが作成されるたびに機能が複製されるステップ3の部分を排除します。別のプロパティを持つ代わりにa1、それらを持たないようになりました。おそらく、あなたは今までにあなた自身を理解しました。あなたが理解していればそれは...素敵なことだと、これらのような質問はかなり明らかであろう。a2hey__proto__Function.prototype

注:一部の人々は内部のプロトタイププロパティをとして呼び出さない傾向があります。__proto__この名前を投稿で使用して、Functional.prototype2つの異なるものとしてプロパティを明確に区別しています。


1
本当に徹底的で有益な答え。上記のオブジェクト構造(A.prototype.heyとオブジェクトthis.hey)を使用してメモリテストを行い、それぞれのインスタンスを1000個作成しました。オブジェクトプロパティアプローチのメモリフットプリントは、プロトタイプと比較して約100kb大きくなりました。次に、同じ目的で「愚かな」と呼ばれる別の関数を追加したところ、直線的に200kbに​​増加しました。重要ではありませんが、ピーナッツでもありません。
jookyone 2017

さらに興味深いのは、プロトタイプメソッドがローカルで実行されているオブジェクトプロパティメソッドよりもわずかに遅いことです。全体として、10kを超える番号のオブジェクトのデータ操作にjavascriptを使用する必要があるかどうかはわかりません。そのため、潜在的なメモリ効果に基づいてアプローチを変更する理由はありません。その時点で、作業はサーバーにオフロードされます。
jookyone 2017

ポイントは__proto__.prototypeまったく違うものです。
Wayou

1
私はあなたに賛成票を与えるだけでは満足していません...よくできました!
Kristianmitk

58

ほとんどの場合、それらは基本的に同じですが、2番目のバージョンでは、オブジェクトごとに個別の関数ではなく、関数のインスタンスが1つしかないため、メモリが節約されます。

最初のフォームを使用する理由は、「プライベートメンバー」にアクセスするためです。例えば:

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

JavaScriptのスコープ規則により、private_varはthis.xに割り当てられた関数で使用できますが、オブジェクトの外部では使用できません。


1
プロトタイプを介してプライベートメンバーにアクセスする方法の例については、この投稿:stackoverflow.com/a/1441692/654708を参照してください。
GFoley83 2014年

その答え@ GFoley83はそれを示していませ -プロトタイプメソッドは、特定のオブジェクトの「パブリック」プロパティにのみアクセスできます。(プロトタイプではなく)特権メソッドのみがプライベートメンバーにアクセスできます。
Alnitak

27

最初の例では、そのオブジェクトのインターフェースのみを変更します。2番目の例は、そのクラスのすべてのオブジェクトのインターフェースを変更します。


どちらも、関数が行いますx。そのプロトタイプAの新しいインスタンスを割り当てられているすべてのオブジェクトのために利用可能function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
スペンサー・ウィリアムズ

21

this代わりにを使用prototypeする場合の最終的な問題は、メソッドをオーバーライドするときに、基本クラスのコンストラクターがオーバーライドされたメソッドを引き続き参照することです。このことを考慮:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

対:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

これが問題ではないと思われる場合は、プライベート変数なしで生活できるかどうか、リークが発生したときにリークを知る十分な経験があるかどうかによって異なります。また、メソッド定義の後にコンストラクタロジックを配置する必要があるのも不便です。

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

対:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1

20

すべてのオブジェクトはプロトタイプオブジェクトにリンクされています。存在しないプロパティにアクセスしようとすると、JavaScriptはそのプロパティのオブジェクトのプロトタイプオブジェクトを検索し、存在する場合はそれを返します。

prototype関数コンストラクタのプロパティは、の使用時にその関数で作成されたすべてのインスタンスのプロトタイプオブジェクトを参照しますnew


最初の例ではxA関数で作成された各インスタンスにプロパティを追加しています。

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

2番目の例では、すべてのインスタンスがAポイントして作成したプロトタイプオブジェクトにプロパティを追加しています。

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

結論として、最初の例では、関数のコピーが各インスタンスに割り当てられます。2番目の例では、関数の単一のコピーがすべてのインスタンスで共有されます


1
質問に対する最も直接的な回答であるとして、これに投票しました。
Nick Pineda

1
私はあなたの率直なアプローチが好きでした!どきどきする!
Vijay Pratap王子

16

違いは何ですか?=>たくさん。

このthisバージョンは、カプセル化、つまりデータの非表示を可能にするために使用されていると思います。プライベート変数の操作に役立ちます。

次の例を見てみましょう。

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

これで、prototype構造を次のように適用できます。

大人によって年齢は異なりますが、すべての大人が同じ権利を取得します。
そのため、これではなくプロトタイプを使用して追加します。

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

ここで実装を見てみましょう。

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

お役に立てれば。


3
+1他の解答に比べて、複雑ではなく、グラフィカルな解答です。ただし、これらの(良い)例を提供する前に、もう少し詳しく説明する必要があります。
yerforkferchips 2014

1
「このバージョンはカプセル化、つまりデータの非表示を有効にするために使用されます」についてはわかりません。関数内のプロパティが「this.myProperty = ...」のように「this」を使用して定義されている場合、そのようなプロパティは「プライベート」ではなく、「new」を使用してクラス外のオブジェクトからアクセスできます。
NoChance 2017年

14

プロトタイプはクラスのテンプレートです。これは、今後のすべてのインスタンスに適用されます。一方、これはオブジェクトの特定のインスタンスです。


14

私はこれが死の答えになっていることを知っていますが、速度の違いの実際の例を示したいと思います。

オブジェクトで直接機能する

プロトタイプの機能

ここではprint、Chromeのメソッドで2,000,000の新しいオブジェクトを作成しています。すべてのオブジェクトを配列に格納しています。置くprintプロトタイプには限り1/2程度かかります。


13

JavaScriptトレーニングコースで学んだより包括的な答えを挙げましょう。

ほとんどの回答はすでに違いを述べています。つまり、関数のプロトタイピングをすべての(将来の)インスタンスで共有する場合です。一方、クラスで関数を宣言すると、インスタンスごとにコピーが作成されます。

一般に、正しいか間違っているかはありません。それは、要件に応じて、好みまたは設計上の決定の問題です。ただし、プロトタイプは、オブジェクト指向の方法で開発するために使用される手法です。この回答の最後で説明します。

質問には2つのパターンがありました。さらに2つ説明し、必要に応じて違いを説明します。自由に編集/拡張してください。すべての例で、それは場所を持ち、移動できる自動車オブジェクトに関するものです。

オブジェクトデコレータパターン

このパターンが現在も関連性があるかどうかはわかりませんが、存在しています。そして、それについて知るのは良いことです。オブジェクトとプロパティをデコレータ関数に渡すだけです。デコレータは、プロパティとメソッドを持つオブジェクトを返します。

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

関数クラス

JavaScriptの関数は特殊なオブジェクトです。関数は、呼び出されるだけでなく、他のオブジェクトと同様にプロパティを格納できます。

この場合Carは、慣れているとおりに呼び出すことができる関数オブジェクト考える)です。これには、プロパティ(関数を持つオブジェクト)があります。ときに呼び出されるいくつかの魔法を行い、かつ拡張呼び出される関数、関数内で定義されたメソッドを持つ(オブジェクトと思います)。methodsmoveCarextendCarmethods

この例は異なりますが、質問の最初の例に最も近くなります。

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

プロトタイプクラス

最初の2つのパターンでは、テクニックを使用して共有メソッドを定義したり、コンストラクターの本体でインラインで定義されているメソッドを使用したりする方法について説明します。どちらの場合も、すべてのインスタンスに独自のmove機能があります。

プロトタイプの委任による機能の共有がプロトタイプのパターンの目標であるので、プロトタイプのパターンは同じ検査には適していません。他の人が指摘したように、メモリフットプリントが改善されると予想されます。

ただし、知っておくべき興味深い点が1つありprototypeます。すべてのオブジェクトには便利なプロパティconstructorがあります。これは、アタッチされた関数(オブジェクト)を指し示します。

最後の3行について:

この例でCarprototype、それ自体を介しconstructorてリンクするオブジェクトにリンクしています。CarつまりCar.prototype.constructorCarそれ自体です。これにより、特定のオブジェクトを構築したコンストラクター関数を特定できます。

amy.constructorの検索は失敗するためCar.prototype、コンストラクタープロパティを持っているに委任されます。そしてそうamy.constructorですCar

さらに、amyありますinstanceof Carinstanceof演算子は、右オペランドのプロトタイプオブジェクトは、(あれば見ることによって動作しますCar)、左オペランドのプロトタイプ(のどこにでも見つけることができますamy)チェーン。

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

最初は混乱する開発者もいます。以下の例を参照してください:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

instanceofオペレータが戻るfalse、ためDogのプロトタイプはのどこに見つけることができないfidoのプロトタイプチェーン"。fidoオブジェクトリテラルで作成された単純なオブジェクトですObject.prototype。つまり、単にに委譲されます。

疑似古典的パターン

これは実際には、単純化された形式のプロトタイプパターンのもう1つの形式であり、newコンストラクタを使用するため、たとえばJavaでプログラミングを行う人にとってはより身近なものです。

これはプロトタイプパターンと同じですが、プロトタイプパターンの上にある構文上の砂糖です。

ただし、主な違いは、擬似クラシックパターンを使用する場合にのみ適用される最適化がJavaScriptエンジンに実装されていることです。疑似古典的パターンは、プロトタイプパターンのおそらくより高速なバージョンだと考えてください。両方の例のオブジェクトの関係は同じです。

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

最後に、オブジェクト指向プログラミングがどのように実行できるかを理解することはそれほど難しくないはずです。2つのセクションがあります。

プロトタイプ(チェーン)の一般的なプロパティ/メソッドを定義する1つのセクション。

また、オブジェクトを互いに区別する定義を配置する別のセクション(loc例では変数)。

これにより、JavaScriptでスーパークラスやサブクラスなどの概念を適用できます。

自由に追加または編集してください。もう少し完成したら、これをコミュニティWikiにすることができます。


非常に完全な投稿をノックするわけではありませんが、OOとプロトタイプの継承は本質的に異なる考え方の集まりであると思いました。
Nick Pineda

彼らはそうですが、人はさまざまなテクニック/思考で「OOを行う」ことができますね。
Ely

本当にわからない 多くの人は、プロトタイプの哲学がちょうど違うと言い、OOと比較しようとする人もいます。
Nick Pineda

つまり、OOスタイルを練習したいが、その言語がそうするのに役立つ一連のテクニックを提供している場合、それは必ずしも間違っているとは限りません。
Ely

11

@Matthew Crumleyは正しいと思います。それらは、構造的ではないにしても、機能的には同等です。Firebugを使用してnew、を使用して作成されたオブジェクトを見ると、それらが同じであることがわかります。ただし、私の好みは次のようになります。C#/ Javaで慣れているように見えるだけだと思います。つまり、クラスを定義し、フィールド、コンストラクター、およびメソッドを定義します。

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

編集変数のスコープがプライベートであることを意味するのではなく、JavaScriptでクラスを定義する方法を説明しようとしていました。これを反映して変数名が変更されました。


2
_instance_var はインスタンスの_instance_var`プロパティinitializex methods do not refer to the 同じですAが、グローバルプロパティです。インスタンスのプロパティthis._instance_varを使用する場合に使用します。_instance_varA
Lekensteyn、2011

2
おもしろいのは、ベンリーもそのようなエラーを犯したということです。2年後にも明らかになりました:p
Lekensteyn

10

他の回答で説明したように、プロトタイプ内の関数は、インスタンス化ごとに作成される関数ではなく、すべてのインスタンス化と共有されるため、これは実際にはパフォーマンスの考慮事項です。

これを示すためにjsperfをまとめました。クラスをインスタンス化するのにかかる時間には劇的な違いがありますが、実際に関連するのは、多くのインスタンスを作成している場合のみです。

http://jsperf.com/functions-in-constructor-vs-prototype


8

静的に型付けされた言語について考えてみてください。prototype物事は静的であり、物事thisはインスタンスに関連しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.