ECMAScript 6 で導入された JavaScript クラスは、JavaScript にすでにあるプロトタイプベース継承の糖衣構文です。クラス構文は、新しいオブジェクト指向継承モデルを JavaScript に導入しているわけではありません。JavaScript クラスは、オブジェクトを作成して継承を扱うためのシンプルで明確な構文を用意します。

クラス定義

クラスは実際「特別な関数」であり、関数式関数宣言で定義するように、クラス構文にはクラス式クラス宣言という2つの定義方法があります。

クラス宣言

クラスを定義する一つの方法は、クラス宣言を使うことです。クラスを宣言するには、クラス名 (この例では "Polygon") 付きで class キーワードを使います。

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

ホイスティング(巻き上げ)

関数宣言クラス宣言の重要な違いは、関数宣言では hoisting されるのに対し、クラス宣言ではされないことです。クラスにアクセスする前に、そのクラスを宣言する必要があります。そうしないと、ReferenceError がスローされます:

var p = new Polygon(); // ReferenceError

class Polygon {}

クラス式

クラス式はクラスを定義するもう 1 つの方法です。クラス式は、名前付きでも名前なしでもできます。名前付きクラスの名前は、クラス内部にローカルです。

// 名前なし
var Polygon = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// 名前つき
var Polygon = class Porygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

注: クラスにもクラス宣言で言及したのと同じホイスティング問題があります。

クラス本体とメソッド定義

中括弧 {} 内にクラス本体を記述します。クラス本体には、メソッドやコンストラクタといったクラスメンバを記述します。

strict モード

クラス宣言、もしくはクラス式で定義されたクラス本体は、strict モード で実行されます。

コンストラクタ

コンストラクタ は、そのクラスによって定義されるオブジェクトの生成時に、初期化を行う特別なメソッドです。"constructor" という名前のメソッドは、クラスに1つしか定義できません。2 回 以上定義されている場合は、SyntaxError がスローされます。

親クラスのコンストラクタは super というキーワードで呼び出せます。

プロトタイプメソッド

メソッド定義 を参照してください。

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.height * this.width;
  }
}

const square = new Polygon(10, 10);

console.log(square.area);

静的メソッド

static キーワードは、クラスに静的メソッドを定義します。静的メソッドは、クラスのインスタンス化なしで呼ばれ、インスタンス化されていると呼べません。静的メソッドは、アプリケーションのユーテリティメソッドを作るのによく使います。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

    return Math.sqrt(dx*dx + dy*dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));

プロトタイプと静的メソッドによるボクシング

"this" にオブジェクトの値が付けられずに静的メソッドまたはプロトタイプメソッドが呼ばれると、"this" の値は関数内で undefined になります。自動ボクシングは行われません。非 strict モードでコードを書く場合でも、同じふるまいになります。

class Animal { 
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
let speak = obj.speak;
speak(); // undefined

let eat = Animal.eat;
eat(); // undefined

上のコードを従来の関数ベースのクラスを使って書くと、"this" の値にもとづいて自動ボクシングが行われます。

function Animal() { }

Animal.prototype.speak = function(){
  return this;
}

Animal.eat = function() {
  return this;
}

let obj = new Animal();
let speak = obj.speak;
speak(); // global object

let eat = Animal.eat;
eat(); // global object

拡張によるサブクラス化

extends キーワードはクラス宣言またはクラス式内で使って、クラスを別クラスの子として作成します。

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

サブクラスで現れたコンストラクタがあると、"this" を使う前に super() を呼ぶ必要があります。

従来の関数ベースの「クラス」も拡張できます:

function Animal (name) {
  this.name = name;  
}
Animal.prototype.speak = function () {
  console.log(this.name + ' makes a noise.');
}

class Dog extends Animal {
  speak() {
    super.speak();
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
d.speak();

クラスはregularな(生成不可能な)オブジェクトを拡張することはできないことに注意してください。regularなオブジェクトから継承したければ、代わりにObject.setPrototypeOf() を使います。

Species

Array の派生型である MyArray の中で Array オブジェクトを返したいときもあるでしょう。species パターンは、デフォルトコンストラクタをオーバライドすることができます。

例えば、map() のようなデフォルトコンストラクタを返すメソッドを使っているとき、MyArray ではなく Array オブジェクトを返したいとします。 Symbol.species シンボルを使うと:

class MyArray extends Array {
  // Overwrite species to the parent Array constructor
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true

super による親クラスの呼び出し

super キーワードを使って、親クラスのメソッドを呼び出せます。

class Cat { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}

Mix-in

抽象クラスや mix-in はクラスのためのテンプレートです。ECMAScript のクラスは 1 つだけ親クラスを持つことができます。そのため、多重継承はできません。機能は親クラスから提供されます。

ECMAScript では親クラスをインプットとして、そして親クラスを継承した派生クラスをアウトプットとする関数を mix-in で実装できます:

var CalculatorMixin = Base => class extends Base {
  calc() { }
};

var RandomizerMixin = Base => class extends Base {
  randomize() { }
};

mix-in を使用したクラスを次のように記述することもできます:

class Foo { }
class Bar extends CalculatorMixin(RandomizerMixin(Foo)) { }

仕様

仕様 状態 コメント
ECMAScript 2015 (6th Edition, ECMA-262)
Class definitions の定義
標準 初期定義。
ECMAScript 2017 Draft (ECMA-262)
Class definitions の定義
ドラフト  

ブラウザ実装状況

機能 Chrome Firefox (Gecko) Internet Explorer Opera Safari
基本サポート 42.0[1]
49.0
45 13 未サポート 未サポート 9.0
機能 Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile Chrome for Android
基本サポート 未サポート 45 ? ? 9 42.0[1]
49.0

[1] strict モードが必要。既定で非 strict モードでのサポートは、"Enable Experimental JavaScript" フラグで無効化されている。

関連項目

ドキュメントのタグと貢献者

 このページの貢献者: ambi, YuichiNukiyama, chikoski, fand, lv7777, GoToLoop
 最終更新者: ambi,