class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
面代碼定義了一個“類”,可以看到裏面有一個constructor
方法,這就是構造方法,而this
關鍵字則代表實例對象。也就是說,ES5 的構造函數Point
,對應 ES6 的Point
類的構造方法。
Point
類除了構造方法,還定義了一個toString
方法。注意,定義“類”的方法的時候,前面不需要加上function
這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。
constructor
方法是類的默認方法,通過new
命令生成對象實例時,自動調用該方法。一個類必須有constructor
方法,如果沒有顯式定義,一個空的constructor
方法會被默認添加。
constructor
方法默認返回實例對象(即this
),完全可以指定返回另外一個對象。
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false
上面代碼中,constructor
函數返回一個全新的對象,結果導致實例對象不是Foo
類的實例。
類必須使用new
調用,否則會報錯。這是它跟普通構造函數的一個主要區別,後者不用new
也可以執行。
class Foo {
constructor() {
return Object.create(null);
}
}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
類的所有實例共享一個原型對象。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__
//true
在“類”的內部可以使用get
和set
關鍵字,對某個屬性設置存值函數和取值函數,攔截該屬性的存取行爲。
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
屬性表達式
類的屬性名,可以採用表達式。
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
上面代碼中,Square
類的方法名getArea
,是從表達式得到的。
Class 表達式 § ⇧
與函數一樣,類也可以使用表達式的形式定義。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
上面代碼使用表達式定義了一個類。需要注意的是,這個類的名字是MyClass
而不是Me
,Me
只在 Class 的內部代碼可用,指代當前類。
let inst = new MyClass();
inst.getClassName() // Me
Me.name // ReferenceError: Me is not defined
上面代碼表示,Me
只在 Class 內部有定義。
如果類的內部沒用到的話,可以省略Me
,也就是可以寫成下面的形式。
const MyClass = class { /* ... */ };
採用 Class 表達式,可以寫出立即執行的 Class。
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('張三');
person.sayName(); // "張三"
name屬性
class Point {}
Point.name // "Point"
this 的指向
類的方法內部如果含有this
,它默認指向類的實例。但是,必須非常小心,一旦單獨使用該方法,很可能報錯。
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
如果在一個方法前,加上static
關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱爲“靜態方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
如果靜態方法包含this
關鍵字,這個this
指的是類,而不是實例。
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
上面代碼中,靜態方法bar
調用了this.baz
,這裏的this
指的是Foo
類,而不是Foo
的實例,等同於調用Foo.baz
。另外,從這個例子還可以看出,靜態方法可以與非靜態方法重名。
父類的靜態方法,可以被子類繼承。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod() // 'hello'
上面代碼中,父類Foo
有一個靜態方法,子類Bar
可以調用這個方法。
靜態方法也是可以從super
對象上調用的。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod() // "hello, too"