1.類的簡介
JavaScript 語言中,生成實例對象的傳統方法是通過構造函數。下面是一個例子。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
上面的代碼用 ES6 的class
改寫,就是下面這樣。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
上面的constructor
方法,稱爲構造方法,- this代表實例對象
- ES5 的構造函數
Point
,對應 ES6 的Point
類的構造方法。 - 類的所有方法都定義在類的
prototype
屬性上面。 - 類必須使用
new
調用,否則會報錯。這是它跟普通構造函數的一個主要區別,後者不用new
也可以執行。
類的所有方法都定義在類的prototype
屬性上面。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同於
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
在類的實例上面調用方法,其實就是調用原型上的方法。b
是B
類的實例,它的constructor
方法就是B
類原型的constructor
方法。
class B {}
let b = new B();
b.constructor === B.prototype.constructor // true
與 ES5 一樣,實例的屬性除非顯式定義在其本身(即定義在this
對象上),否則都是定義在原型上(即定義在class
上)。
//定義類
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
類的所有實例共享一個原型對象。p1
和p2
都是Point
的實例,它們的原型都是Point.prototype
,所以__proto__
屬性是相等的。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__
//true
類的方法內部如果含有this
,它默認指向類的實例。
現在回憶一下原型鏈
2.靜態方法(類的方法。加static)
類相當於實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上static
關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱爲“靜態方法”。
Foo
類的classMethod
方法前有static
關鍵字,表明該方法是一個靜態方法,可以直接在Foo
類上調用(Foo.classMethod()
),而不是在Foo
類的實例上調用。如果在實例上調用靜態方法,會拋出一個錯誤,表示不存在該方法。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
注意,如果靜態方法包含this
關鍵字,這個this
指的是類,而不是實例。
靜態方法bar
調用了this.baz
,這裏的this
指的是Foo
類,而不是Foo
的實例,等同於調用Foo.baz
。
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
3.靜態屬性(類的屬性,加static)
靜態屬性指的是 Class 本身的屬性,即Class.propName
,而不是定義在實例對象(this
)上的屬性。
因爲 ES6 明確規定,Class 內部只有靜態方法,沒有靜態屬性。
現在有一個提案提供了類的靜態屬性,寫法是在實例屬性法的前面,加上static
關鍵字。
class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // 42
}
}
4.實例屬性
實例屬性除了定義在constructor()
方法裏面的this
上面,也可以定義在類的最頂層。
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
上面代碼中,實例屬性this._count
定義在constructor()
方法裏面。另一種寫法是,這個屬性也可以定義在類的最頂層,其他都不變。
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
5.私有屬性和私有方法
私有方法和私有屬性,是隻能在類的內部訪問的方法和屬性,外部不能訪問。
class
加了私有屬性。方法是在屬性名之前,使用#
表示。
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
上面代碼中,#count
就是私有屬性,只能在類的內部使用(this.#count
)。如果在類的外部使用,就會報錯。
這種寫法不僅可以寫私有屬性,還可以用來寫私有方法。
class Foo {
#a;
#b;
constructor(a, b) {
this.#a = a;
this.#b = b;
}
#sum() {
return #a + #b;
}
printSum() {
console.log(this.#sum());
}
}
上面代碼中,#sum()
就是一個私有方法。
總結一下
類的所有方法都定義在類的
prototype
屬性上面。靜態方法(類的方法。加static)
靜態屬性(類的屬性,加static)
實例屬性