【俺的理解】JavaScriptのprototype

公開日: 

参考記事

https://developer.mozilla.org/ja/docs/Learn/JavaScript/Objects/Object_prototypes

https://jsprimer.net/basic/prototype-object

https://qiita.com/saka212/items/e897af630a135981fbc3

プロトタイプとは

概要

JavaScript ですべてのオブジェクトは、そのプロトタイプと呼ばれる組み込みプロパティを持っています。プロトタイプはそれ自体がオブジェクトなので、プロトタイプは自分自身でプロトタイプを持ち、プロトタイプチェーンと呼ばれるものを持ちます。自分自身でプロトタイプに null を持つプロトタイプに到達すると、その連鎖は終わります。

https://developer.mozilla.org/ja/docs/Learn/JavaScript/Objects/Object_prototypes

プロトタイプは JavaScript の全てのオブジェクトが持つ、組み込みのプロパティです。規定では、__proto__というプロパティ名です。

試しにブラウザでログに出力してみても、空のオブジェクトを生成したつもりがプロトタイプというプロパティを持っていることがわかります。

そして、このプロトタイプには.__proto__でアクセスできます。

要するに、JavaScript では全てのオブジェクトが自動的に__proto__というプロパティを持ち、そのプロパティに設定されている値のことをそのオブジェクトのプロトタイプと呼ぶわけです。

じゃ、プロトタイプはどんな特徴を持つねん

詳細

プロトタイプがオブジェクトの特殊なプロパティだということは理解しました。次はその特徴を見ていきましょう。

簡単にいうと、

オブジェクトは外部から存在しないプロパティにアクセスされたとき、自身のプロトタイプに設定されているオブジェクトの中を探しにいきます。

そして、自身のプロトタイプにも存在しなかった場合、さらに自身のプロトタイプのプロトタイプ、さらにそのプロトタイプ、というように指定されたプロパティを探し、プロパティが存在した時点でその値を返します。プロトタイプがnullになった時点で、プロパティの検索が終了し、全てのプロトタイプを探しても見つからない場合はundefinedを返します。

const user = {
  name: '太郎',
};

オブジェクトを作って、userという変数に格納しました。先ほど説明したように、自分で定義したわけではないですが、このオブジェクトはプロトタイプを持ちます。そして、JavaScript ではオブジェクトを生成したときに自動的に Object.prototype(詳細は後述)というオブジェクトがプロトタイプに設定されます。そして、Object.prototype自身ののプロトタイプは null です。

このオブジェクトのtoStringメソッドを呼んでみましょう。もちろん自分ではtoStringというメソッドは定義していないのでメソッドが見つからずエラーになりそうですが、

console.log(user.toString()); // '[object Object]'

実際に呼び出すことが可能です。これはプロトタイプであるObject.prototypeオブジェクトにtoStringメソッドが定義されているから、呼び出すことができています。

次に、オブジェクトのhogeプロパティにアクセスしてみましょう。

console.log(user.hoge); // undefined

undefinedが返ってきています。これは、オブジェクトにhogeという名前のプロパティが存在せず、さらにObject.prototypeにも存在せず、Object.prototypeのプロトタイプがnullなので検索が終了してundefinedが返ってきているわけです。

なんとなくつかめたでしょうか。言葉選びが難しいですが、クラスの『継承』のような特徴を持ちます。この特殊なプロトタイプというプロパティでの繋がりを『プロトタイプチェーン』と呼びます。

Object.prototype について

JavaScript でオブジェクトを生成した時に、デフォルトではObject.prototypeというオブジェクトがプロトタイプになります。

というかDateオブジェクトなどもプロトタイプチェーンを辿ると最終的にObject.prototypeに辿り着きます。要するにObject.prototypeは、JavaScript において最も基礎的なオブジェクトです。始祖のオブジェクトです。Object.prototypeの詳細を探りましょう。

まず最初に、Object は『関数』です。

console.log(typeof Object); // function

newを用いてオブジェクトが生成できることから、コンストラクタ関数であることは想像できます。

const obj = new Object({});

https://ja.javascript.info/function-prototype

  • 関数はprototypeという特殊なプロパティを持つことができる。
  • new 演算子とコンストラクタ関数を用いてオブジェクトを生成したとき、生成されたオブジェクトのプロトタイプになるのは、コンストラクタ関数のprototypeオブジェクトに指定されているオブジェクトである。

これらがこのオブジェクトを理解する上でのポイントであり、結論です。

要するに、Object.prototypeObjectという関数のprototypeというプロパティに設定されているただのオブジェクトです。

コンストラクターだのプロパティだのややこしいですが、Object.prototypeはただの『オブジェクト』です。

console.log(typeof Object.prototype); // 'object'

色々いじってみる

// 1つ目のオブジェクトを生成
// プロトタイプは指定していないので、デフォルトのObject.prototypeがプロトタイプになる
const fuga = {
  fugafuga: 'fugafuga',
};

// 2つ目のオブジェクトを生成
// プロトタイプにはfugaを指定
const hoge = {
  hogehoge: 'hogehoge',
  __proto__: fuga,
};

// コンストラクタ関数を生成
function Piyo() {
  this.piyopiyo = 'piyopiyo';
}

// コンストラクタ関数のプロトタイプにhogeを指定
Piyo.prototype = hoge;

// コンストラクタ関数からオブジェクトを生成
// コンストラクタ関数のプロトタイプがhogeなので生成されたオブジェクトのプロトタイプもhoge
const piyo = new Piyo();

// プロトタイプチェーンにより全てのプロパティにアクセスが可能
console.log(piyo.piyopiyo); // piyopiyo
console.log(piyo.hogehoge); // hogehoge
console.log(piyo.fugafuga); // fugafuga
console.log(piyo.toString()); // [object Object]

最後に

いっつも良くわかんねぇなぁって思いながら prototype の謎が解けました。

それと同時に、JavaScript は一見複雑なものでも、結局『オブジェクト』とかの単純なものにいくつくんだなぁと感じました。

では

Bye