クラス

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

クラスの定義

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

クラス宣言

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

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

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

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

const p = new Rectangle(); // ReferenceError

class Rectangle {}

クラス式

クラスを定義する別の方法はクラス式です。クラス式は、名前付きでも名前なしでもできます。名前付きクラスの名前は、クラス内のローカルとして扱われます。(ただし (インスタンスのではなく) クラスの 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);
p1.distance; //未定義
p2.distance; //未定義

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

上のコードを従来の関数ベースの構文を使って書くと、非 Strict モードでは、最初の this の値をもとにして、メソッド呼び出しの中で自動ボクシングが行われます。最初の値が 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;

フィールド宣言

パブリックフィールドとプライベートフィールドの宣言は JavaScript 標準委員会の TC39 で提案されている実験的機能(ステージ 3)です。ブラウザでのサポートは限られていますが、この機能はBabelのようなシステムでのビルドステップを通して使用できます。 

パブリックフィールド宣言

JavaScriptのフィールド宣言構文を使って、上記の例は次のように書くことができます。

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

フィールドを事前宣言することで、クラス定義はより自己文書化され、フィールドは常に存在するようになります。

上記のように、フィールドはデフォルト値の有無にかかわらず宣言できます。

プライベートフィールド宣言

プライベートフィールドを使うと、宣言は下記のように洗練できます。

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

プライベートフィールドの参照はクラス本体内でのみ可能となり、クラス外からの参照はエラーとなります。クラス外からは見えないものを定義することで、クラスのユーザーが(変更される可能性のある)内部状態に依存できないようにします。

プライベートフィールドは、事前宣言のみ可能です。

プライベートフィールドは通常のプロパティとは違い、 this への追加によって後から作成することができません。

詳しい情報は、 class fields も参照してください。

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.

//NB: For similar methods, the child's method takes precedence over parent's method

クラスは通常の (生成不可能な) オブジェクトを拡張できないことに注意してください。通常のオブジェクトから継承したければ、代わりに 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.

ミックスイン

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

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

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

let randomizerMixin = Base => class extends Base {
  randomize() { }
};

ミックスインを使用したクラスを次のように記述することもできます:

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

仕様書

仕様書 状態 備考
ECMAScript Latest Draft (ECMA-262)
Class definitions の定義
ドラフト
ECMAScript 2017 (ECMA-262)
Class definitions の定義
標準
ECMAScript 2016 (ECMA-262)
Class definitions の定義
標準
ECMAScript 2015 (6th Edition, ECMA-262)
Class definitions の定義
標準 初回定義

ブラウザー実装状況

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
classesChrome 完全対応 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 完全対応 36
補足
完全対応 36
補足
補足 From Opera 29 to 35, strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Safari 完全対応 9WebView Android 完全対応 49
補足
完全対応 49
補足
補足 From WebView 42 to 48, strict mode is required.
Chrome Android 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48, strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Firefox Android 完全対応 45Opera Android 完全対応 36
補足
完全対応 36
補足
補足 From Opera 29 to 35, strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Safari iOS 完全対応 9Samsung Internet Android 完全対応 5.0
補足
完全対応 5.0
補足
補足 In Samsung Internet 4.0, strict mode is required.
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 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Chrome Android 完全対応 ありFirefox 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 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Chrome Android 完全対応 ありFirefox 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.
Private class fieldsChrome 完全対応 74Edge 未対応 なしFirefox 未対応 なしIE 未対応 なしOpera 完全対応 62Safari 未対応 なしWebView Android 完全対応 74Chrome Android 完全対応 74Firefox Android 未対応 なしOpera Android 完全対応 53Safari iOS 未対応 なしSamsung Internet Android 未対応 なしnodejs 完全対応 12.0.0
Public class fieldsChrome 完全対応 72Edge 未対応 なしFirefox 完全対応 69IE 未対応 なしOpera 完全対応 60Safari 未対応 なしWebView Android 完全対応 72Chrome Android 完全対応 72Firefox Android 未対応 なしOpera Android 完全対応 51Safari iOS 未対応 なしSamsung Internet Android 未対応 なしnodejs 完全対応 12.0.0
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 完全対応 49
補足
完全対応 49
補足
補足 From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript".
Chrome Android 完全対応 ありFirefox 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.
Static class fieldsChrome 完全対応 72Edge 未対応 なしFirefox 未対応 なし
補足
未対応 なし
補足
補足 Static fields aren't supported, see bug 1535804.
IE 未対応 なしOpera 完全対応 60Safari 未対応 なしWebView Android 完全対応 72Chrome Android 完全対応 72Firefox Android 未対応 なしOpera Android 完全対応 51Safari iOS 未対応 なしSamsung Internet Android 未対応 なしnodejs 完全対応 12.0.0

凡例

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

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

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

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

関連情報