我們也可以將方法指定給整個類別。此類方法稱為靜態。
在類別宣告中,它們會加上 static
關鍵字,如下所示
class User {
static staticMethod() {
alert(this === User);
}
}
User.staticMethod(); // true
這實際上與直接將其指定為屬性相同
class User { }
User.staticMethod = function() {
alert(this === User);
};
User.staticMethod(); // true
在 User.staticMethod()
呼叫中,this
的值是類別建構函式 User
本身(「點之前的物件」規則)。
通常,靜態方法用於實作屬於整個類別的功能,但不屬於任何特定物件。
例如,我們有 Article
物件,需要一個函式來比較它們。
一個自然的解決方案是新增 Article.compare
靜態方法
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static compare(articleA, articleB) {
return articleA.date - articleB.date;
}
}
// usage
let articles = [
new Article("HTML", new Date(2019, 1, 1)),
new Article("CSS", new Date(2019, 0, 1)),
new Article("JavaScript", new Date(2019, 11, 1))
];
articles.sort(Article.compare);
alert( articles[0].title ); // CSS
在此,Article.compare
方法位於文章「上方」,作為比較文章的方法。它不是文章的方法,而是整個類別的方法。
另一個範例是所謂的「工廠」方法。
假設我們需要多種方式來建立文章
- 透過給定的參數建立(
標題
、日期
等)。 - 建立一個空的,日期為今天的文章。
- …或其他方式。
第一種方式可以透過建構函式來實作。而第二種方式,我們可以建立類別的靜態方法。
例如此處的 Article.createTodays()
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static createTodays() {
// remember, this = Article
return new this("Today's digest", new Date());
}
}
let article = Article.createTodays();
alert( article.title ); // Today's digest
現在,每當我們需要建立今天的摘要時,我們可以呼叫 Article.createTodays()
。再次強調,這不是文章的方法,而是整個類別的方法。
靜態方法也用於與資料庫相關的類別,用於搜尋/儲存/移除資料庫中的項目,如下所示
// assuming Article is a special class for managing articles
// static method to remove the article by id:
Article.remove({id: 12345});
靜態方法可以呼叫類別,但不能呼叫個別物件。
例如,以下程式碼將無法執行
// ...
article.createTodays(); /// Error: article.createTodays is not a function
靜態屬性
靜態屬性也是可行的,它們看起來像是常規類別屬性,但前面加上 static
class Article {
static publisher = "Ilya Kantor";
}
alert( Article.publisher ); // Ilya Kantor
這與直接指定給 Article
相同
Article.publisher = "Ilya Kantor";
靜態屬性和方法的繼承
靜態屬性和方法會被繼承。
例如,以下程式碼中的 Animal.compare
和 Animal.planet
會被繼承,並可作為 Rabbit.compare
和 Rabbit.planet
存取
class Animal {
static planet = "Earth";
constructor(name, speed) {
this.speed = speed;
this.name = name;
}
run(speed = 0) {
this.speed += speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
static compare(animalA, animalB) {
return animalA.speed - animalB.speed;
}
}
// Inherit from Animal
class Rabbit extends Animal {
hide() {
alert(`${this.name} hides!`);
}
}
let rabbits = [
new Rabbit("White Rabbit", 10),
new Rabbit("Black Rabbit", 5)
];
rabbits.sort(Rabbit.compare);
rabbits[0].run(); // Black Rabbit runs with speed 5.
alert(Rabbit.planet); // Earth
現在,當我們呼叫 Rabbit.compare
時,會呼叫繼承的 Animal.compare
。
它是如何運作的?同樣地,使用原型。您可能已經猜到了,extends
會給予 Rabbit
對 Animal
的 [[Prototype]]
參考。
因此,Rabbit extends Animal
會建立兩個 [[Prototype]]
參考
Rabbit
函式會從Animal
函式原型繼承。Rabbit.prototype
會從Animal.prototype
原型繼承。
因此,繼承適用於常規方法和靜態方法。
在此,讓我們透過程式碼來檢查
class Animal {}
class Rabbit extends Animal {}
// for statics
alert(Rabbit.__proto__ === Animal); // true
// for regular methods
alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
摘要
靜態方法用於屬於「整體」類別的功能。它與具體類別實例無關。
例如,用於比較的 Article.compare(article1, article2)
方法或工廠方法 Article.createTodays()
。
它們在類別宣告中標記為 static
字詞。
當我們想要儲存類別層級資料時,會使用靜態屬性,而且不繫結到執行個體。
語法為
class MyClass {
static property = ...;
static method() {
...
}
}
技術上來說,靜態宣告與指派給類別本身相同
MyClass.property = ...
MyClass.method = ...
靜態屬性和方法會被繼承。
對於 class B extends A
,類別 B
本身的原型指向 A
:B.[[Prototype]] = A
。因此,如果在 B
中找不到欄位,搜尋會繼續在 A
中進行。
留言
<code>
標籤,若要插入多行程式碼,請將它們包覆在<pre>
標籤中,若要插入超過 10 行程式碼,請使用沙盒 (plnkr、jsbin、codepen…)