Introduction
上篇文章大致介紹了一些ES6的特性,以及如何在低版本瀏覽器中使用它們。本文是對class的詳解。
譯自Axel Rauschmayer的Classes in ECMAScript 6
另外,如果只是想測試ES6,可以到這個網站。
Overview
借助class 我們可以寫出這樣的代碼:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
toString() {
return super.toString() + ' in ' + this.color;
}
}
let cp = new ColorPoint(25, 8, 'green');
cp.toString(); // '(25, 8) in green'
console.log(cp instanceof ColorPoint); // true
console.log(cp instanceof Point); // true
Base classes
我們可以定義如下的class:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
我們可以像使用ES5標准中的constructor一樣實例化class
> var p = new Point(25, 8);
> p.toString()
'(25, 8)'
實際上,class還是用function實現的,並沒有為js創造一個全新的class體系。
> typeof Point
'function'
但是,與function相比,它是不能直接調用的,也就是說必須得new出來
> Point()
TypeError: Classes can’t be function-called
另外,它不會像function一樣會被hoisted(原因是語義階段無法解析到extends的內容)
foo(); // works, because `foo` is hoisted
function foo() {}
new Foo(); // ReferenceError
class Foo {}
function functionThatUsesBar() {
new Bar();
}
functionThatUsesBar(); // ReferenceError
class Bar {}
functionThatUsesBar(); // OK
與函數一樣,class的定義表達式也有兩種,聲明形式、表達式形式。之前用的都是聲明形式,以下是表達式式的:
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
let inst = new MyClass();
console.log(inst.getClassName()); // Me
console.log(Me.name); // ReferenceError: Me is not defined
Inside the body of a class definition
class定義體是只能包含方法,不能包含屬性的(標准定義組織認為原型鏈中不應包含屬性),屬性被寫在constructor中。以下是三種會用到的方法(constructor 、static method、 prototype method):
class Foo {
constructor(prop) {
this.prop = prop;
}
static staticMethod() {
return 'classy';
}
prototypeMethod() {
return 'prototypical';
}
}
let foo = new Foo(123);
如下圖([[Prototype]]代表着繼承關系)當對象被new出來,拿的是Foo.prototype : Object分支,從而可以調prototype method

constructor,這個方法本身,代表了class
> Foo === Foo.prototype.constructor
true
constructor有時被稱為類構造器。相較於ES5,它可以調用父類的constructor(使用super())。
static methods。它們歸屬於類本身。
> typeof Foo.staticMethod
'function'
> Foo.staticMethod()
'classy'
關於Getters and setters,它們的語法如下:
class MyClass {
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
> let inst = new MyClass();
> inst.prop = 123;
setter: 123
> inst.prop
'getter'
方法名是可以動態生成的
class Foo() {
myMethod() {}
}
class Foo() {
['my'+'Method']() {}
}
const m = 'myMethod';
class Foo() {
[m]() {}
}
增加了迭代器的支持,需要給方法前面加一個*
class IterableArguments {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new IterableArguments('hello', 'world')) {
console.log(x);
}
// Output:
// hello
// world
Subclassing
通過extends,我們可以繼承其它實現constructor的函數或對象。需要注意一下,constructor與非constructor調用父類方法的途徑是不同的。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // (A)
this.color = color;
}
toString() {
return super.toString() + ' in ' + this.color; // (B)
}
}
> let cp = new ColorPoint(25, 8, 'green');
> cp.toString()
'(25, 8) in green'
> cp instanceof ColorPoint
true
> cp instanceof Point
true
子類的原型就是它的父類
> Object.getPrototypeOf(ColorPoint) === Point
true
所以,static method也被繼承了
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod(); // 'hello'
static方法也是支持調用父類的。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod(); // 'hello, too'
關於子類中使用構造器,需要注意的是,調用this之前,需要調用super()
class Foo {}
class Bar extends Foo {
constructor(num) {
let tmp = num * 2; // OK
this.num = num; // ReferenceError
super();
this.num = num; // OK
}
}
constructors是可以被顯示覆蓋(override)的。
class Foo {
constructor() {
return Object.create(null);
}
}
console.log(new Foo() instanceof Foo); // false
如果基類中不顯示定義constructor,引擎會生成如下代碼
constructor() {}
對於子類
constructor(...args) {
super(...args);
}
The details of classes
- 類名不能為eval 或者 arguments,不能有重復的類名,constructor不支持getter,setter。
- classes不能像函數一樣調用。
- 原型方法不能用作構造器:
class C {
m() {}
}
new C.prototype.m(); // TypeError
The details of subclassing
ES 6中,子類的使用方法如下:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
···
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
···
}
let cp = new ColorPoint(25, 8, 'green');
原型鏈實現:
> const getProto = Object.getPrototypeOf.bind(Object);
> getProto(Point) === Function.prototype
true
> getProto(function () {}) === Function.prototype
true
> getProto(Point.prototype) === Object.prototype
true
> getProto({}) === Object.prototype
true
