LoginSignup
1439

More than 5 years have passed since last update.

図で理解するJavaScriptのプロトタイプチェーン

Last updated at Posted at 2016-10-02
1 / 59

JavaScriptのリファレンスといえばMDNですよね。
JavaScriptで調べものをする際に、真っ先に見る方も多いでしょう。

そんなMDNですが読めていますか?

例えばArrayのページを見てみましょう。


さらっと書かれているprototype

image


JavaScriptを理解するにはプロトタイプチェーンを知る必要がある


本記事の目的

  • プロトタイプチェーンを理解する
  • MDNをより読めるようにする
  • JavaScriptのObjectやArrayなどの基本型の構造を理解する

確認環境や記述について

  • Chrome 52のコンソールで動作確認
  • ES5で記述
  • 説明のしやすさを重視、プロトタイプをあらわすのに__proto__を使って記述

まずはオブジェクトについてみていく


シンプルなオブジェクトを作る

リテラルでオブジェクトを作る
var obj = {name: "taro"};
new演算子でオブジェクトを作る
var obj = new Object();
obj.name = "taro";

上記2つの同じオブジェクトを作っています。
{}new Object()の糖衣構文


console.logで中身を出力してみる

var obj = {name: "taro"};
console.log(obj);

image

Object {name: "taro"}というのが出てきました。


Objectを展開

image

__proto__という未知のプロパティが出てきます。
これはオブジェクトを作ると裏で作られるプロパティです。


__proto__のObjectを展開

プロパティがたくさん設定されているオブジェクトの様です。

image


ここまでの図解

image


謎のオブジェクトの正体とは


MDNで検索

出てきたFunction名で検索をかけるとObjectプロトタイプオブジェクトに含まれるメソッドと一致します。

image


謎のオブジェクトの正体はObject.prototype

確認
obj.__proto__ === Object.prototype; // true

上記コードでtrueになるのが確認できます。


Object自体がオブジェクト

Object.prototypeと言う書き方はobj.nameのような書き方と全く同じです。
Objectという変数名でオブジェクトが定義されており、Objectのプロパティにprototypeが設定されています。

あくまでイメージです
var Object = {prototype: {オブジェクトのプロトタイプ}};

ここまでの図解

image


そもそもObjectとは


Objectという変数はwindowに定義されている

このObjectという変数はブラウザのグローバルオブジェクトであるwindowに定義されています。

確認
obj.__proto__ === window.Object.prototype; // true

おまけ:objという変数もwindowに定義されている

自分で作ったこのobjという変数もブラウザのグローバルオブジェクトであるwindowに定義しています。

確認
window.obj.__proto__ === window.Object.prototype; // true

ここまでの図解

image


いよいよプロトタイプチェーンに触れていく


MDNのリファレンスを見るとObject.prototypeにはtoString()というメソッドがあることがわかる。
試しに呼んでみる。

console.log(obj.name); // taro -> わかる

console.log(
  obj.__proto__.toString
); // function toString() { [native code] } -> 実装はネイティブコードらしい

これはどうなる?

console.log(
  obj.toString
); // function toString() { [native code] }  -> 呼べてしまった

console.log(
  obj.hogehoge
); // undefined -> 未定義?

プロトタイプチェーンの仕組み

  1. 指定したオブジェクトにプロパティが存在を調べる
  2. なかった場合__proto__が参照する先で存在を調べる
  3. それでもなかった場合__proto__が参照する・・・(ループ)
  4. 最終的にnullになるまで行う。nullならundefinedを返す

Objectのプロトタイプの__proto__はnull

obj.__proto__.__proto__ === null; // true
Object.prototype.__proto__; // null

つまり__proto__を辿ってObject.prototypeで見つからなかったらundefinedを返すということ。

obj.__proto__Object.prototypeは同じものです(重要なので何度でも書く)


ここまでの図解

image


プロトタイプチェーンの流れを見る

先程記述したそれぞれのパターンごとに動きを見てみます。

  • obj.name (インスタンスに存在するパターン)
  • obj.toString (プロトタイプチェーン先に存在するパターン)
  • obj.hogehoge (最後まで見つからないパターン)

obj.nameを探す際の動き


1.objを探す

プロトタイプチェーン1_1.png


2.obj.nameを探す

image


obj.toStringを探す際の動き


1.objを探す

プロトタイプチェーン1_1.png


2.obj.toStringを探す

プロトタイプチェーン2_2.png


3.obj.__proto__を探す

プロトタイプチェーン2_3.png


4.obj.__proto__.toStringを探す

image


obj.hogehogeを探す際の動き


1.objを探す

プロトタイプチェーン1_1.png


2.obj.hogehogeを探す

プロトタイプチェーン3_2.png


3.obj.__proto__を探す

プロトタイプチェーン2_3.png


4.obj.__proto__.hogehogeを探す

プロトタイプチェーン3_4.png


5.obj.__proto__.__proto__を探す

image


Functionオブジェクトについて

さらにプロトタイプチェーンを深く知るにはFunctionオブジェクトについて知る必要があります。

※Objectでだいぶプロトタイプチェーンに強くなっているはずです。説明もささっといきます。


シンプルなFunctionオブジェクトを作る

リテラルでFunctionオブジェクトを作る
var func = function(){console.log("hello");};
new演算子でFunctionオブジェクトを作る
var func = new Function('console.log("hello");');

上記2つのほぼ同じオブジェクトを作っています。(後者はfunc.nameが"anonymous"になったりする)
function(){}new Function()の糖衣構文


Functionの__proto__

Objectのインスタンスの__proto__はObject.prototypeでしたね。

(new Object()).__proto__ === Object.prototype; // true

この形はObject以外でも同じです。

(new Function()).__proto__ === Function.prototype; // true
(new Array()).__proto__ === Array.prototype; // true

つまり、FunctionもObjectと同じようなプロトタイプが構成されている


ここまでの図解

image


Functionのプロトタイプにも__proto__がある

プロトタイプチェーンは__proto__を辿るんでしたね。
みてみます。

__proto__を辿る
console.log(func.__proto__.__proto__);

image


これObjectのときに出てきたやつだ!!


FunctionもObjectのプロトタイプチェーンが組まれている

確認
Function.prototype.__proto__ === Object.prototype; // true

Function.prototypeにtoStringが定義されているため(いわゆるオーバーライド)
toStringはFunction.prototype.toStringになります。

確認
func.toString(); // function (){console.log("hello");}
func.toString === Function.prototype.toString; // true;

ここまでの図解

image


コンストラクタ関数

コンストラクタ関数はnew XXX()に入るXXX関数のこと。
つまりObjectFunctionなどはコンストラクタ関数として使用できるFunctionオブジェクト。


constructorプロパティ

コンストラクタ関数を参照するためのプロパティ。
プロトタイプやインスタンスが持っている。

今まで出てきた例のconstructor
var obj = {};
Object.prototype.constructor === Object; // true
obj.constructor === Object; // true

var func = function(){};
Function.prototype.constructor === Function; // true
func.constructor === Function; // true

ここまでの図解

image


最初に見たArrayのMDNにアクセスして見てみよう

image


ある程度は読めるようになったはず

なってなかったら・・・筆者の説明スキル不足、申し訳ない :cry:

本記事の内容が理解できていれば
MDNと以下のコードで次ページのようなイメージができるはずです。

配列を生成
var myArray = [];
myArray.push("hoge"); // これはプロトタイプチェーン先のメソッドから呼べる
myArray.isArray; // 何が返るかわかりますね?
// myArray.isArray(); // undefined()を実行しているようなもの
Array.isArray(myArray); // true isArrayの使い方はこう


image


あとがき

プロトタイプチェーンを理解できた人が一人でも多くいれば幸いです。

本記事の目的はプロトタイプチェーンを理解することです。
プロトタイプチェーンを使ってごりごり実装しようということではありません。
原則としてはclass構文使いましょう。(classも内部的にはプロトタイプチェーン)


参考資料

書籍
開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質
Effective JavaScript JavaScriptを使うときに知っておきたい68の冴えたやり方
JavaScript Ninjaの極意

記事
[JavaScript] そんな継承はイヤだ - クラス定義 - オブジェクト作成
や...やっと理解できた!JavaScriptのプロトタイプチェーン

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1439