ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为JavaScript引入新的面向对象的继承模型。

## 定义类

类实际上是个“特殊的函数”，就像你能够定义的函数表达式和函数声明一样，类语法有两个组成部分：类表达式和类声明。

### 类声明

定义一个类的一种方法是使用一个类声明。要声明一个类，你可以使用带有`class`关键字的类名（这里是“Rectangle”）。

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

#### 提升

函数声明和类声明之间的一个重要区别是函数声明会提升，类声明不会。你首先需要声明你的类，然后访问它，否则像下面的代码会抛出一个`ReferenceError`：

    let p = new Rectangle();
    // ReferenceError

    class Rectangle {}

### 类表达式

一个类表达式是定义一个类的另一种方式。类表达式可以是被命名的或匿名的。赋予一个命名类表达式的名称是类的主体的本地名称。

    /* 匿名类 */
    let Rectangle = class {
      constructor(height, width) {
        this.height = height;
        this.width = width;
      }
    };

    /* 命名的类 */
    let Rectangle = class Rectangle {
      constructor(height, width) {
        this.height = height;
        this.width = width;
      }
    };

注意: 类表达式也同样受到类声明中提到的提升问题的困扰。

## 类体和方法定义

一个类的类体是一对花括号/大括号 `{}` 中的部分。这是你定义类成员的位置，如方法或构造函数。

### 严格模式

类声明和类表达式的主体都执行在严格模式下。比如，构造函数，静态方法，原型方法，getter和setter都在严格模式下执行。

### 构造函数

constructor方法是一个特殊的方法，这种方法用于创建和初始化一个由`class`创建的对象。一个类只能拥有一个名为 “constructor”的特殊方法。如果类包含多个`constructor`的方法，则将抛出 一个`SyntaxError` 。

一个构造函数可以使用 `super` 关键字来调用一个父类的构造函数。

### 原型方法

参见方法定义。

    class Rectangle {
        // constructor
        constructor(height, width) {
            this.height = height;
            this.width = width;
        }
        // Getter
        get area() {
            return this.calcArea()
        }
        // Method
        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));

### 用原型和静态方法包装

当一个对象调用静态或原型方法时，如果该对象没有“this”值（或“this”作为布尔，字符串，数字，未定义或null) ，那么“this”值在被调用的函数内部将为 `undefined`。不会发生自动包装。即使我们以非严格模式编写代码，它的行为也是一样的，因为所有的函数、方法、构造函数、getters或setters都在严格模式下执行。因此如果我们没有指定this的值，this值将为`undefined`。

    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”值将发生自动装箱。

    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` 创建子类

`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.');
      }
    }

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

如果子类中存在构造函数，则需要在使用“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();

请注意，类不能继承常规（非可构造）对象。如果要继承常规对象，可以改用`Object.setPrototypeOf()`：

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

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

    Object.setPrototypeOf(Dog.prototype, Animal);// If you do not do this you will get a TypeError when you invoke speak

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

## Species

你可能希望在派生数组类 `MyArray` 中返回 `Array`对象。这种 species 方式允许你覆盖默认的构造函数。

例如，当使用像`map()`返回默认构造函数的方法时，您希望这些方法返回一个父`Array`对象，而不是`MyArray`对象。`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-ins

抽象子类或者 mix-ins 是类的模板。 一个 ECMAScript 类只能有一个单超类，所以想要从工具类来多重继承的行为是不可能的。子类继承的只能是父类提供的功能性。因此，例如，从工具类的多重继承是不可能的。该功能必须由超类提供。

一个以超类作为输入的函数和一个继承该超类的子类作为输出可以用于在ECMAScript中实现混合：

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

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

使用 mix-ins 的类可以像下面这样写：

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

## 规范

Specification | Status | Comment
--- | --- | ---
ECMAScript 2015 (6th Edition, ECMA-262) Class definitions | Standard | Initial definition.
ECMAScript 2016 (ECMA-262) Class definitions | Standard |
ECMAScript 2017 (ECMA-262) Class definitions | Standard |
ECMAScript Latest Draft (ECMA-262) Class definitions | Draft |

## 浏览器兼容

The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.

Update compatibility data on GitHub

| Desktop | Mobile | Server
| Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | Android webview | Chrome for Android | Edge Mobile | Firefox for Android | Opera for Android | Safari on iOS | Samsung Internet | Node.js
`classes` | Chrome Full support 49 Notes Full support 49 Notes Notes From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript". | Edge Full support 13 | Firefox Full support 45 | IE No support No | Opera Full support 36 | Safari Full support 9 | WebView Android ? | Chrome Android Full support Yes | Edge Mobile Full support 13 | Firefox Android Full support 45 | Opera Android ? | Safari iOS Full support 9 | Samsung Internet Android Full support Yes | nodejs Full support 6.0.0 Full support 6.0.0 Full support 4.0.0 Disabled Disabled From version 4.0.0: this feature is behind the `--use_strict` runtime flag. Full support 5.0.0 Disabled Disabled From version 5.0.0: this feature is behind the `--harmony` runtime flag.
`constructor` | Chrome Full support 49 Notes Full support 49 Notes Notes From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript". | Edge Full support 13 | Firefox Full support 45 | IE No support No | Opera Full support 36 | Safari Full support 9 | WebView Android ? | Chrome Android Full support Yes | Edge Mobile Full support 13 | Firefox Android Full support 45 | Opera Android ? | Safari iOS Full support 9 | Samsung Internet Android Full support Yes | nodejs Full support 6.0.0 Full support 6.0.0 Full support 4.0.0 Disabled Disabled From version 4.0.0: this feature is behind the `--use_strict` runtime flag. Full support 5.0.0 Disabled Disabled From version 5.0.0: this feature is behind the `--harmony` runtime flag.
`extends` | Chrome Full support 49 Notes Full support 49 Notes Notes From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript". | Edge Full support 13 | Firefox Full support 45 | IE No support No | Opera Full support 36 | Safari Full support 9 | WebView Android ? | Chrome Android Full support Yes | Edge Mobile Full support 13 | Firefox Android Full support 45 | Opera Android ? | Safari iOS Full support 9 | Samsung Internet Android Full support Yes | nodejs Full support 6.0.0 Full support 6.0.0 Full support 4.0.0 Disabled Disabled From version 4.0.0: this feature is behind the `--use_strict` runtime flag. Full support 5.0.0 Disabled Disabled From version 5.0.0: this feature is behind the `--harmony` runtime flag.
`static` | Chrome Full support 49 Notes Full support 49 Notes Notes From Chrome 42 to 48 strict mode is required. Non-strict mode support can be enabled using the flag "Enable Experimental JavaScript". | Edge Full support 13 | Firefox Full support 45 | IE No support No | Opera Full support 36 | Safari Full support 9 | WebView Android ? | Chrome Android Full support Yes | Edge Mobile Full support 13 | Firefox Android Full support 45 | Opera Android ? | Safari iOS Full support 9 | Samsung Internet Android Full support Yes | nodejs Full support 6.0.0 Full support 6.0.0 Full support 4.0.0 Disabled Disabled From version 4.0.0: this feature is behind the `--use_strict` runtime flag. Full support 5.0.0 Disabled Disabled From version 5.0.0: this feature is behind the `--harmony` runtime flag.

### Legend

Full support
  Full support
No support
  No support
Compatibility unknown
  Compatibility unknown
See implementation notes.
  See implementation notes.
User must explicitly enable this feature.
  User must explicitly enable this feature.

## 在Scratchpad中运行

一个类不能被重新定义。如果你正在使用Scratchpad中的代码（Firefox菜单工具> Web Developer> Scratchpad），并且您运行了两次具有相同名称的类的定义，那么你将遇到一个令人困惑的SyntaxError：let <class name>。

要重新运行定义，请使用Scratchpad菜单 执行>重新加载并运行。
请为这个bug投票 #1428672。

## 相关链接

- Functions
- `class` declaration
- `class` expression
- `super`
- Blog post: "ES6 In Depth: Classes"
- Fields and public/private class properties proposal (stage 3)