JavaScriptでは、関数はプロパティを持つことができます。すべての関数はprototype
という特別なプロパティを持っています。
function doSomething() {}
console.log(doSomething.prototype);
// JavaScriptの関数は常にデフォルトのプロトタイププロパティを持ちます
// 例外: アロー関数はデフォルトのプロトタイププロパティを持ちません
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);
doSomething()
はデフォルトのprototype
プロパティを持っています。上記のコードを実行すると、コンソールには次のようなオブジェクトが表示されます。
{ constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
注意: Chromeコンソールはオブジェクトのプロトタイプを表すために[[Prototype]]
を使用しますが、これは仕様書の用語です。Firefoxは.prototype
を使用します。統一性を持たせるために、ここでは[[Prototype]]
を使用します。
doSomething()
のプロトタイプにプロパティを追加することができます。
function doSomething() {}
doSomething.prototype.foo = "bar";
console.log(doSomething.prototype);
結果:
{ foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
new
演算子を使用して、このプロトタイプに基づいてdoSomething()
のインスタンスを作成します。new
演算子で関数を呼び出すと、その関数のインスタンスであるオブジェクトが返されます。その後、このオブジェクトにプロパティを追加することができます。
function doSomething() {}
doSomething.prototype.foo = "bar"; // プロトタイプにプロパティを追加
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // オブジェクトにプロパティを追加
console.log(doSomeInstancing);
結果:
{ prop: "some value", [[Prototype]]: { foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
doSomeInstancing
の[[Prototype]]
はdoSomething.prototype
です。doSomeInstancing
のプロパティにアクセスすると、ランタイムはまずdoSomeInstancing
にそのプロパティがあるかどうかを確認します。
ない場合、ランタイムはdoSomeInstancing.[[Prototype]]
(つまりdoSomething.prototype
)でプロパティを検索します。doSomeInstancing.[[Prototype]]
に探しているプロパティがあれば、そのプロパティが使用されます。
ない場合、doSomeInstancing.[[Prototype]].[[Prototype]]
がチェックされます。デフォルトでは、任意の関数のprototype
プロパティの[[Prototype]]
はObject.prototype
です。そのため、doSomeInstancing.[[Prototype]].[[Prototype]]
(つまりdoSomething.prototype.[[Prototype]]
またはObject.prototype
)が検索されます。このプロセスはプロトタイプチェーンと呼ばれます。
プロパティがdoSomeInstancing.[[Prototype]].[[Prototype]]
で見つからない場合、doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
が検索されます。しかし、Object.prototype.[[Prototype]]
はnull
なので、doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
は存在しません。プロトタイプチェーン全体が検索された後、ランタイムはプロパティが存在しないと判断し、プロパティの値はundefined
になります。
function doSomething() {}
doSomething.prototype.foo = "bar";
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: ", doSomeInstancing.prop);
console.log("doSomeInstancing.foo: ", doSomeInstancing.foo);
console.log("doSomething.prop: ", doSomething.prop);
console.log("doSomething.foo: ", doSomething.foo);
console.log("doSomething.prototype.prop:", doSomething.prototype.prop);
console.log("doSomething.prototype.foo: ", doSomething.prototype.foo);
結果:
doSomeInstancing.prop: some value
doSomeInstancing.foo: bar
doSomething.prop: undefined
doSomething.foo: undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo: bar