"prototype"
プロパティは、JavaScript 自体のコアによって広く使用されています。すべての組み込みコンストラクター関数はそれを使用します。
最初に詳細を見てから、組み込みオブジェクトに新しい機能を追加するためにどのように使用するかを見ていきます。
Object.prototype
空のオブジェクトを出力するとしましょう。
let obj = {};
alert( obj ); // "[object Object]" ?
文字列 "[object Object]"
を生成するコードはどこにあるのでしょうか?それは組み込みの toString
メソッドですが、どこにあるのでしょうか?obj
は空です!
…しかし、短い記法 obj = {}
は obj = new Object()
と同じです。ここで Object
は独自の prototype
を持つ組み込みオブジェクトコンストラクター関数で、toString
やその他のメソッドを持つ巨大なオブジェクトを参照しています。
これが何が起こっているかです。
new Object()
が呼び出されると(またはリテラルオブジェクト {...}
が作成されると)、前の章で議論したルールに従って、その [[Prototype]]
は Object.prototype
に設定されます。
したがって、obj.toString()
が呼び出されると、メソッドは Object.prototype
から取得されます。
このように確認できます。
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true
Object.prototype
の上にチェーンされた [[Prototype]]
がもうないことに注意してください。
alert(Object.prototype.__proto__); // null
その他の組み込みプロトタイプ
Array
、Date
、Function
などの他の組み込みオブジェクトも、メソッドをプロトタイプに保持します。
たとえば、配列 [1, 2, 3]
を作成すると、デフォルトの new Array()
コンストラクターが内部的に使用されます。したがって、Array.prototype
がそのプロトタイプになり、メソッドを提供します。これは非常にメモリ効率が良いです。
仕様により、すべての組み込みプロトタイプは最上位に Object.prototype
を持っています。そのため、「すべてはオブジェクトから継承する」と言う人がいます。
これが全体図です(3つの組み込みオブジェクトが収まるように)。
プロトタイプを手動で確認してみましょう。
let arr = [1, 2, 3];
// it inherits from Array.prototype?
alert( arr.__proto__ === Array.prototype ); // true
// then from Object.prototype?
alert( arr.__proto__.__proto__ === Object.prototype ); // true
// and null on the top.
alert( arr.__proto__.__proto__.__proto__ ); // null
プロトタイプの一部のメソッドは重複する可能性があります。たとえば、Array.prototype
は、カンマ区切りの要素をリストする独自の toString
を持っています。
let arr = [1, 2, 3]
alert(arr); // 1,2,3 <-- the result of Array.prototype.toString
以前見たように、Object.prototype
にも toString
がありますが、Array.prototype
がチェーン内でより近いため、配列のバリアントが使用されます。
Chrome 開発者コンソールなどのブラウザツールも継承を示します (組み込みオブジェクトには console.dir
の使用が必要な場合があります)。

他の組み込みオブジェクトも同じように動作します。関数でさえ、組み込みの Function
コンストラクターのオブジェクトであり、そのメソッド (call
/ apply
など) は Function.prototype
から取得されます。関数には独自の toString
もあります。
function f() {}
alert(f.__proto__ == Function.prototype); // true
alert(f.__proto__.__proto__ == Object.prototype); // true, inherit from objects
プリミティブ
最も複雑なことは、文字列、数値、およびブール値で発生します。
覚えているように、それらはオブジェクトではありません。しかし、それらのプロパティにアクセスしようとすると、組み込みコンストラクター String
、Number
、Boolean
を使用して一時的なラッパーオブジェクトが作成されます。それらはメソッドを提供し、消えます。
これらのオブジェクトは私たちには見えないように作成され、ほとんどのエンジンはそれらを最適化しますが、仕様はまさにこのように記述しています。これらのオブジェクトのメソッドもプロトタイプに存在し、String.prototype
、Number.prototype
、Boolean.prototype
として利用できます。
null
および undefined
にはオブジェクトラッパーはありません特別な値 null
と undefined
は別です。それらにはオブジェクトラッパーがないため、メソッドとプロパティは利用できません。また、対応するプロトタイプもありません。
ネイティブプロトタイプの変更
ネイティブプロトタイプは変更できます。たとえば、String.prototype
にメソッドを追加すると、すべての文字列で利用できるようになります。
String.prototype.show = function() {
alert(this);
};
"BOOM!".show(); // BOOM!
開発プロセス中に、組み込みメソッドに追加したい新しいメソッドのアイデアが生まれる可能性があり、それらをネイティブプロトタイプに追加したいと思うかもしれません。しかし、それは一般的に悪い考えです。
プロトタイプはグローバルであるため、競合が発生しやすいです。2つのライブラリがメソッド String.prototype.show
を追加した場合、それらの1つが他方のメソッドを上書きします。
したがって、一般的に、ネイティブプロトタイプの変更は悪い考えであると考えられています。
現代のプログラミングでは、ネイティブプロトタイプの変更が承認されるケースは1つだけです。それはポリフィルです。
ポリフィルとは、JavaScript仕様に存在するが、特定のJavaScriptエンジンでまだサポートされていないメソッドの代替を作成するための用語です。
次に、手動で実装し、組み込みプロトタイプにそれを追加できます。
例
if (!String.prototype.repeat) { // if there's no such method
// add it to the prototype
String.prototype.repeat = function(n) {
// repeat the string n times
// actually, the code should be a little bit more complex than that
// (the full algorithm is in the specification)
// but even an imperfect polyfill is often considered good enough
return new Array(n + 1).join(this);
};
}
alert( "La".repeat(3) ); // LaLaLa
プロトタイプからの借用
デコレーターと転送、call/apply の章では、メソッドの借用について説明しました。
これは、あるオブジェクトからメソッドを取得して別のオブジェクトにコピーする場合です。
ネイティブプロトタイプの一部のメソッドは、しばしば借用されます。
たとえば、配列のようなオブジェクトを作成する場合、いくつかの Array
メソッドをそれにコピーしたい場合があります。
例。
let obj = {
0: "Hello",
1: "world!",
length: 2,
};
obj.join = Array.prototype.join;
alert( obj.join(',') ); // Hello,world!
組み込みの join
メソッドの内部アルゴリズムは、正しいインデックスと length
プロパティのみを気にするため、機能します。オブジェクトが実際に配列であるかどうかはチェックしません。多くの組み込みメソッドがそのようになっています。
もう1つの可能性は、obj.__proto__
を Array.prototype
に設定して継承することです。これにより、すべての Array
メソッドが obj
で自動的に利用可能になります。
ただし、obj
がすでに別のオブジェクトから継承している場合は不可能です。一度に継承できるオブジェクトは1つだけであることを忘れないでください。
メソッドの借用は柔軟性があり、必要に応じて異なるオブジェクトの機能を組み合わせることができます。
まとめ
- すべての組み込みオブジェクトは同じパターンに従います。
- メソッドはプロトタイプ (
Array.prototype
、Object.prototype
、Date.prototype
など) に保存されます。 - オブジェクト自体はデータ (配列項目、オブジェクトプロパティ、日付) のみを保存します。
- メソッドはプロトタイプ (
- プリミティブもラッパーオブジェクトのプロトタイプにメソッドを保存します:
Number.prototype
、String.prototype
、Boolean.prototype
。undefined
とnull
のみにはラッパーオブジェクトがありません。 - 組み込みプロトタイプは、変更したり、新しいメソッドを追加したりできます。ただし、それらを変更することはお勧めしません。唯一許容されるケースは、おそらく、新しい標準を追加する場合ですが、まだJavaScriptエンジンでサポートされていません。
コメント
<code>
タグを使用し、数行の場合は<pre>
タグで囲み、10行以上の場合はサンドボックスを使用します (plnkr, jsbin, codepen…)