新しいオブジェクトは、 new F()
などのコンストラクター関数を使用して作成できることに注意してください。
F.prototype
がオブジェクトの場合、 new
オペレーターはそれを使用して新しいオブジェクトの[[Prototype]]
を設定します。
ご注意ください:
JavaScript には最初からプロトタイプの継承がありました。これは言語の中核的な機能の 1 つでした。
しかし、昔はそこに直接アクセスすることはできませんでした。確実に動作したのは、この章で説明するコンストラクター関数の"prototype"
プロパティだけです。そのため、今でもそれを使用しているスクリプトがたくさんあります。
ここでのF.prototype
は、 F
の"prototype"
という名前の通常のプロパティを意味することに注意してください。 「プロトタイプ」という用語に似ていますが、ここでは実際にはこの名前の通常のプロパティを意味します。
以下に例を示します。
動物 = { にしてみましょう 食べる:本当 }; 関数 ウサギ(名前) { this.name = 名前; } Rabbit.prototype = 動物; let Rabbit = new Rabbit("White Rabbit"); // ウサギ.__proto__ == 動物 アラート(うさぎ.食べる); // 真実
Rabbit.prototype = animal
設定すると、文字通り次のようになります: 「 new Rabbit
が作成されたら、その[[Prototype]]
animal
に割り当てます」。
それが結果の画像です:
この図では、 "prototype"
は水平方向の矢印で通常のプロパティを意味し、 [[Prototype]]
垂直方向であり、 animal
からのrabbit
の継承を意味します。
F.prototype
new F
時にのみ使用されます
F.prototype
プロパティは、 new F
が呼び出されたときにのみ使用され、新しいオブジェクトの[[Prototype]]
を割り当てます。
作成後にF.prototype
プロパティが変更された場合 ( F.prototype = <another object>
)、 new F
によって作成された新しいオブジェクトは[[Prototype]]
として別のオブジェクトを持ちますが、既存のオブジェクトは古いオブジェクトを保持します。
私たちが提供しなくても、すべての関数には"prototype"
プロパティがあります。
デフォルトの"prototype"
は、関数自体を指す唯一のプロパティconstructor
を持つオブジェクトです。
このような:
関数 Rabbit() {} /* デフォルトのプロトタイプ Rabbit.prototype = { コンストラクター: Rabbit }; */
それを確認できます:
関数 Rabbit() {} // デフォルトでは: // Rabbit.prototype = { コンストラクター: Rabbit } alert( Rabbit.prototype.constructor == Rabbit ); // 真実
当然のことながら、何もしなければ、 [[Prototype]]
を通じてすべてのウサギがconstructor
プロパティを利用できます。
関数 Rabbit() {} // デフォルトでは: // Rabbit.prototype = { コンストラクター: Rabbit } let Rabbit = new Rabbit(); // {コンストラクター: Rabbit} から継承 アラート(rabbit.constructor == Rabbit); // true (プロトタイプから)
constructor
プロパティを使用すると、既存のコンストラクターと同じコンストラクターを使用して新しいオブジェクトを作成できます。
ここのように:
関数 ウサギ(名前) { this.name = 名前; アラート(名前); } let Rabbit = new Rabbit("White Rabbit"); let Rabbit2 = new Rabbit.constructor("Black Rabbit");
これは、オブジェクトがあって、そのコンストラクターがどのコンストラクターに使用されたか分からず (サードパーティのライブラリからのものなど)、同じ種類の別のコンストラクターを作成する必要がある場合に便利です。
しかし、おそらく"constructor"
について最も重要なことは…
…JavaScript 自体は、正しい"constructor"
値を保証しません。
はい、関数のデフォルトの"prototype"
に存在しますが、それだけです。後で何が起こるかは、完全に私たち次第です。
特に、デフォルトのプロトタイプを全体として置き換える場合、そのプロトタイプには"constructor"
は存在しません。
例えば:
関数 Rabbit() {} Rabbit.prototype = { ジャンプ:本当 }; let Rabbit = new Rabbit(); アラート(rabbit.constructor === ウサギ); // 間違い
したがって、正しい"constructor"
を維持するために、デフォルトの"prototype"
全体を上書きするのではなく、プロパティを追加/削除することを選択できます。
関数 Rabbit() {} // Rabbit.prototype を完全には上書きしません // 追加するだけです Rabbit.prototype.jumps = true // デフォルトの Rabbit.prototype.constructor は保持されます
または、 constructor
プロパティを手動で再作成します。
Rabbit.prototype = { ジャンプ: true、 コンストラクター: ウサギ }; // コンストラクターを追加したため、コンストラクターも正しくなりました
この章では、コンストラクター関数を介して作成されたオブジェクトの[[Prototype]]
を設定する方法を簡単に説明しました。後で、これに依存するさらに高度なプログラミング パターンについて説明します。
すべては非常に単純ですが、内容を明確にするためにいくつかのメモを記載するだけです。
F.prototype
プロパティ ( [[Prototype]]
と間違えないでください) は、 new F()
が呼び出されたときに新しいオブジェクトの[[Prototype]]
を設定します。
F.prototype
の値はオブジェクトまたはnull
である必要があります。他の値は機能しません。
"prototype"
プロパティは、コンストラクター関数に設定され、 new
で呼び出された場合にのみ、このような特別な効果を持ちます。
通常のオブジェクトでは、 prototype
は特別なものではありません。
ユーザー = { にします 名前:「ジョン」、 プロトタイプ: "Bla-bla" // 魔法はまったくありません };
デフォルトでは、すべての関数にはF.prototype = { constructor: F }
があるため、オブジェクトの"constructor"
プロパティにアクセスすることでオブジェクトのコンストラクターを取得できます。
重要度: 5
以下のコードでは、 new Rabbit
を作成し、そのプロトタイプを変更してみます。
最初に、次のコードがあります。
関数 Rabbit() {} Rabbit.prototype = { 食べる:本当 }; let Rabbit = new Rabbit(); アラート(うさぎ.食べる); // 真実
文字列を 1 つ追加しました (強調)。 alert
は何が表示されますか?
関数 Rabbit() {} Rabbit.prototype = { 食べる:本当 }; let Rabbit = new Rabbit(); Rabbit.prototype = {}; アラート(うさぎ.食べる); //?
…そして、コードが次のようになったら(1行置換)?
関数 Rabbit() {} Rabbit.prototype = { 食べる:本当 }; let Rabbit = new Rabbit(); Rabbit.prototype.eats = false; アラート(うさぎ.食べる); //?
そしてこのように(1行置き換え)?
関数 Rabbit() {} Rabbit.prototype = { 食べる:本当 }; let Rabbit = new Rabbit(); Rabbit.eats を削除します。 アラート(うさぎ.食べる); //?
最後のバリエーション:
関数 Rabbit() {} Rabbit.prototype = { 食べる:本当 }; let Rabbit = new Rabbit(); Rabbit.prototype.eats を削除します。 アラート(うさぎ.食べる); //?
答え:
true
。
Rabbit.prototype
への代入により、新しいオブジェクトに対して[[Prototype]]
が設定されますが、既存のオブジェクトには影響しません。
false
。
オブジェクトは参照によって割り当てられます。 Rabbit.prototype
のオブジェクトは複製されず、 Rabbit.prototype
とrabbit
の[[Prototype]]
の両方によって参照される単一のオブジェクトのままです。
したがって、1 つの参照を通じてその内容を変更すると、その内容はもう 1 つの参照からも表示されます。
true
。
すべてのdelete
操作はオブジェクトに直接適用されます。ここでdelete rabbit.eats
rabbit
からeats
プロパティを削除しようとしますが、それはありません。したがって、操作は何の効果もありません。
undefined
。
プロパティeats
プロトタイプから削除され、もう存在しません。
重要度: 5
コンストラクター関数によって作成された任意のオブジェクトobj
があると想像してください。どのオブジェクトかはわかりませんが、それを使用して新しいオブジェクトを作成したいとします。
そんなことしてもいいでしょうか?
obj2 = new obj.constructor();
このようなコードを正しく動作させるobj
のコンストラクター関数の例を示します。そして、それが間違って動作する例。
"constructor"
プロパティが正しい値を持っていることが確実な場合は、このようなアプローチを使用できます。
たとえば、デフォルトの"prototype"
変更しない場合、このコードは確実に機能します。
関数 ユーザー(名前) { this.name = 名前; } let user = new User('John'); let user2 = new user.constructor('Pete'); アラート( user2.name ); // ピート (うまくいきました!)
User.prototype.constructor == User
なので、うまくいきました。
…しかし、誰かがいわばUser.prototype
を上書きし、 User
を参照するconstructor
を再作成するのを忘れた場合、失敗します。
例えば:
関数 ユーザー(名前) { this.name = 名前; } ユーザー.プロトタイプ = {}; // (*) let user = new User('John'); let user2 = new user.constructor('Pete'); アラート( user2.name ); // 未定義
user2.name
がundefined
のはなぜですか?
new user.constructor('Pete')
どのように機能するかは次のとおりです。
まず、 user
でconstructor
を探します。何もない。
次に、プロトタイプのチェーンに従います。 user
のプロトタイプはUser.prototype
で、 constructor
もありません (正しく設定することを「忘れていた」ため)。
チェーンをさらに上に進むと、 User.prototype
はプレーン オブジェクトであり、そのプロトタイプは組み込みのObject.prototype
です。
最後に、組み込みのObject.prototype
には、組み込みのObject.prototype.constructor == Object
があります。それで使われているのです。
最後に、最後にlet user2 = new Object('Pete')
としました。
おそらく、それは私たちが望んでいることではありません。 new Object
ではなく、 new User
を作成したいと考えています。これは、 constructor
が欠落していることによる結果です。
(念のために言っておきますが、 new Object(...)
呼び出しはその引数をオブジェクトに変換します。これは理論的なもので、実際には値を指定してnew Object
呼び出す人はいませんし、通常はnew Object
使用しませんオブジェクトを作ること自体)。