ECMAScript 2015 で導入された JavaScript クラスは、JavaScript にすでにあるプロトタイプベース継承の糖衣構文です。クラス構文は、新しいオブジェクト指向継承モデルを JavaScript に導入しているわけではありません

クラス定義

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

クラス宣言

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

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

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

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

const p = new Rectangle(); // ReferenceError

class Rectangle {}

クラス式

クラス式はクラスを定義するもう 1 つの方法です。クラス式は、名前付きでも名前なしでもできます。名前付きクラスの名前は、クラス内のローカルとして扱われます。((インスタンスのではなく)クラスの .name プロパティで取得可能ですが)

// 名前なし
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 出力: "Rectangle"

// 名前つき
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 出力: "Rectangle2"

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

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

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

Strict モード

クラス本体は Strict モード で実行されます。つまり、ここで書かれたコードは、パフォーマンスを向上させるために、より厳密な構文に従います。そうでない場合はサイレントエラーがスローされます。なお、特定のキーワードは将来のバージョンの ECMAScript 用に予約されています。

コンストラクタ

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

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

プロトタイプメソッド

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

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // ゲッター
  get area() {
    return this.calcArea();
  }
  // メソッド
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

静的メソッド

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.hypot(dx, dy);
  }
}

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

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

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

this に値が付けられずに静的メソッドまたはプロトタイプメソッドが呼ばれると、this の値はメソッド内で undefined になります。たとえ "use strict" ディレクティブがなくても同じふるまいになります。なぜなら、class 本体の中のコードは常に Strict モードで実行されるからです。

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

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

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

上のコードを従来の関数ベースの構文を使って書くと、最初の this の値に基づいた非 Strict モードとして、メソッド呼び出しの中で自動ボクシングが行われます。最初の値が undefined の場合、this にはグローバルオブジェクトが入ります。

Strict モードでは自動ボクシングは行われません。this の値はそのまま渡されます。

function Animal() { }

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

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

let obj = new Animal();
let speak = obj.speak;
speak(); // グローバルオブジェクト

let eat = Animal.eat;
eat(); // グローバルオブジェクト

インスタンスのプロパティ

インスタンスのプロパティはクラスのメソッドの中で定義しなければなりません:

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

クラスに付随する静的なプロパティやプロトタイプのプロパティは、クラス本体の宣言の外で定義しなければなりません:

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;

extends によるサブクラス

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

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

class Dog extends Animal {
  constructor(name) {
    super(name); // 親クラスのコンストラクタを呼び出し、name パラメータを渡す
  }

  speak() {
    console.log(this.name + ' barks.');
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie 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() {
    console.log(this.name + ' barks.');
  }
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

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

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

class Dog {
  constructor(name) {
    this.name = name;
  }
}

// 下記のようにしなければ、speak() を実行した時に TypeError となる
Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.

Species

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

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

class MyArray extends Array {
  // speciesを親のArrayコンストラクタで上書きする
  static get [Symbol.species]() { return Array; }
}

let a = new MyArray(1,2,3);
let 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.`);
  }
}

let l = new Lion('Fuzzy');
l.speak(); 
// Fuzzy makes a noise.
// Fuzzy roars.

Mix-ins

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

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

let calculatorMixin = Base => class extends Base {
  calc() { }
};

let 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 2016 (ECMA-262)
Class definitions の定義
標準  
ECMAScript 2017 (ECMA-262)
Class definitions の定義
標準  
ECMAScript Latest Draft (ECMA-262)
Class definitions の定義
ドラフト  

ブラウザー実装状況

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeEdge MobileAndroid 版 FirefoxAndroid 版 OperaiOS 版 SafariSamsung InternetNode.js
基本対応Chrome 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Edge 完全対応 13Firefox 完全対応 45IE 未対応 なしOpera 完全対応 36Safari 完全対応 9WebView Android ? Chrome Android 完全対応 ありEdge Mobile 完全対応 13Firefox Android 完全対応 45Opera Android ? Safari iOS 完全対応 9Samsung Internet Android 完全対応 ありnodejs 完全対応 6.0.0
完全対応 6.0.0
完全対応 4.0.0
無効
無効 From version 4.0.0: this feature is behind the --use_strict runtime flag.
完全対応 5.0.0
無効
無効 From version 5.0.0: this feature is behind the --harmony runtime flag.
constructorChrome 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Edge 完全対応 13Firefox 完全対応 45IE 未対応 なしOpera 完全対応 36Safari 完全対応 9WebView Android ? Chrome Android 完全対応 ありEdge Mobile 完全対応 13Firefox Android 完全対応 45Opera Android ? Safari iOS 完全対応 9Samsung Internet Android 完全対応 ありnodejs 完全対応 6.0.0
完全対応 6.0.0
完全対応 4.0.0
無効
無効 From version 4.0.0: this feature is behind the --use_strict runtime flag.
完全対応 5.0.0
無効
無効 From version 5.0.0: this feature is behind the --harmony runtime flag.
extendsChrome 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Edge 完全対応 13Firefox 完全対応 45IE 未対応 なしOpera 完全対応 36Safari 完全対応 9WebView Android ? Chrome Android 完全対応 ありEdge Mobile 完全対応 13Firefox Android 完全対応 45Opera Android ? Safari iOS 完全対応 9Samsung Internet Android 完全対応 ありnodejs 完全対応 6.0.0
完全対応 6.0.0
完全対応 4.0.0
無効
無効 From version 4.0.0: this feature is behind the --use_strict runtime flag.
完全対応 5.0.0
無効
無効 From version 5.0.0: this feature is behind the --harmony runtime flag.
staticChrome 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Edge 完全対応 13Firefox 完全対応 45IE 未対応 なしOpera 完全対応 36Safari 完全対応 9WebView Android ? Chrome Android 完全対応 ありEdge Mobile 完全対応 13Firefox Android 完全対応 45Opera Android ? Safari iOS 完全対応 9Samsung Internet Android 完全対応 ありnodejs 完全対応 6.0.0
完全対応 6.0.0
完全対応 4.0.0
無効
無効 From version 4.0.0: this feature is behind the --use_strict runtime flag.
完全対応 5.0.0
無効
無効 From version 5.0.0: this feature is behind the --harmony runtime flag.

凡例

完全対応  
完全対応
未対応  
未対応
実装状況不明  
実装状況不明
実装ノートを参照してください。
実装ノートを参照してください。
ユーザーが明示的にこの機能を有効にしなければなりません。
ユーザーが明示的にこの機能を有効にしなければなりません。

スクラッチパッドでの動作

クラスは再定義できません。スクラッチパッド (Firefox メニュー > Web開発 > スクラッチパッド) を使っていて、同じ名前のクラスをで2回定義しているコードを「実行」すると、SyntaxError: redeclaration of let <class-name>というエラーに戸惑うことでしょう。

定義を再実行するには、スクラッチパッドのメニューから 実行メニュー > 再読込みして実行 を選んでください。
バグ報告 #1428672 への評価をお願いします。

関連情報

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

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