目次
クラス
クラス構文
ECMAScript 2015からクラス構文が導入された。
'use strict'; // クラス構文の使用にはstrictモードが必須です class Hoge { // コンストラクタ constructor(val) { this._value = val } // メソッド print() { console.log(this._value); } // ゲッター get value() { return this._value; } // セッター set value(val) { this._value = val; } } var hoge = new Hoge(1); hoge.print(); // => 1 と出力 hoge.value = 2; console.log(hoge.value) // => 2 と出力
これは以前のprototypeによるクラス定義のシンタックスシュガーにすぎず、Hogeは関数オブジェクトとして定義されている。
typeof(Hoge); // => function
ただし関数としての呼び出しには制限がかけられており、newを付けずに呼び出すとエラーになる。
Hoge(); // => TypeError: Class constructors cannot be invoked without 'new'
継承
class Fuga extends Hoge { // メソッド print2() { console.log(this._value * 2); } } var fuga = new Fuga(3); fuga.print(); // => 3 と出力 fuga.print2(); // => 6 と出力
staticメソッド
class Piyo extends Hoge{ // メソッド static piyo() { console.log('ピヨピヨ'); } } Piyo.piyo(); // => ピヨピヨ と出力
prototypeによるクラス定義
ここからはES5以前の話
コンストラクタ
関数はコンストラクタとして働く。コンストラクタとして使う関数は、慣例として大文字で名前を定義する。
// クラス定義1 // コンストラクタ関数 function Greet() { // プロパティの定義 this.message = "hello"; // メソッドの定義 this.say = function() { return this.message; }; } // インスタンスの生成 var greet = new Greet(); // メソッドの呼び出し greet.say();
constructor プロパティ
生成されたインスタンスは constructor というプロパティを持っており、自分のコンストラクタにアクセスできる。
function Greet() { // ... } var greet = new Greet(); greet.constructor; // => Greet
プロトタイプ
コンストラクタは prototype というプロパティを持っている。
function Greet() { // ... } Greet.prototype; // => Greet { } var greet = new Greet(); greet.constructor.prototype; // => Greet { }
new演算子で作成されたインスタンスは、自分のコンストラクタの prototype が持っているプロパティやメソッドに、あたかも自分のメソッドやプロパティであるかのようにアクセスできる。
よって prototype にプロパティやメソッドを定義してインスタンスで利用することができる。ただし全てのインスタンスで共有されてしまうため、メソッドのみ prototype に定義するのが一般的である。
// クラス定義2 function Greet(message) { // プロパティ定義 this.message = message; } // メソッド定義 Greet.prototype.say = function() { return this.message } var greet = new Greet("hello"); greet.say(); // => "hello"
prototypeを丸ごと上書きして置き換えることもできる。
// クラス定義3 function Greet(message) { // プロパティ定義 this.message = message; } Greet.prototype = { say: function() { return this.message } }; var greet = new Greet("world"); greet.say();
__proto__ プロパティ
new演算子で作成されたインスタンスは、内部的に <html>proto</html> というプロパティを持っている。これはコンストラクタ関数の prototype と全くオブジェクトを参照している。
var greet = new Greet(); greet.__proto__ === Greet.prototype; // => true greet.__proto__ === greet.constructor.prototype; // => true
これは隠しプロパティ扱いの場合もあるので、実際のプログラミングでは constructor.prototype を使うのがよい。
プロトタイプチェーン
インスタンスは、<html>proto</html>(以下プロトタイプと呼ぶ)が持っているメソッドやプロパティを、自分のメソッドやプロパティであるかのように呼び出すことができる。
あるインスタンスに対してメソッドAを呼び出したとき、もし、インスタンスがメソッドAを持っておらず、プロトタイプがメソッドAを持っていれば、プロトタイプのメソッドAが呼び出される。もしプロトタイプもメソッドAを持っていなければ、プロトタイプのプロトタイプがメソッドAを持っているか探す。
この仕組みをプロトタイプチェーンと呼ぶ。プロトタイプチェーンを使うと継承に似たことができる。
function Greet() { this.message = "hello"; } Greet.prototype.say = function() { return this.message } function NightGreet() { this.message = "good night"; } NightGreet.prototype = new Greet(); // Greetを継承 function JpNightGreet() { this.message = "おやすみなさい"; } NightGreet.prototype = new NightGreet(); // NightGreetを継承 // JpNightGreet型のインスタンス生成 var jpNightGreet = new JpNightGreet(); // Greet型の(プロトタイプのプロトタイプ)メソッドsayが呼び出される jpNightGreet.say();
プロトタイプチェーンを最後までさかのぼると、最終的にはObjectになる。よってObjectのメソッドは全てのインスタンスから呼び出すことができる。(といってもvalueOfとtoString、toSourceぐらいしか無い)