W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
我們可以把一個方法作為一個整體賦值給類。這樣的方法被稱為 靜態(tài)的(static)。
在一個類的聲明中,它們以 static
關(guān)鍵字開頭,如下所示:
class User {
static staticMethod() {
alert(this === User);
}
}
User.staticMethod(); // true
這實(shí)際上跟直接將其作為屬性賦值的作用相同:
class User { }
User.staticMethod = function() {
alert(this === User);
};
User.staticMethod(); // true
在 User.staticMethod()
調(diào)用中的 this
的值是類構(gòu)造器 User
自身(“點(diǎn)符號前面的對象”規(guī)則)。
通常,靜態(tài)方法用于實(shí)現(xiàn)屬于整個類,但不屬于該類任何特定對象的函數(shù)。
例如,我們有對象 Article
,并且需要一個方法來比較它們。
通常的解決方案就是添加 Article.compare
靜態(tài)方法:
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static compare(articleA, articleB) {
return articleA.date - articleB.date;
}
}
// 用法
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
方法代表“上面的”文章,意思是比較它們。它不是文章的方法,而是整個 class 的方法。
另一個例子是所謂的“工廠”方法。
比如說,我們需要通過多種方式來創(chuàng)建一篇文章:
title
?,?date
? 等)。第一種方法我們可以通過 constructor 來實(shí)現(xiàn)。對于第二種方式,我們可以創(chuàng)建類的一個靜態(tài)方法來實(shí)現(xiàn)。
例如這里的 Article.createTodays()
:
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static createTodays() {
// 記住 this = Article
return new this("Today's digest", new Date());
}
}
let article = Article.createTodays();
alert( article.title ); // Today's digest
現(xiàn)在,每當(dāng)我們需要創(chuàng)建一個今天的文章時,我們就可以調(diào)用 Article.createTodays()
。再說明一次,它不是一個文章的方法,而是整個 class 的方法。
靜態(tài)方法也被用于與數(shù)據(jù)庫相關(guān)的公共類,可以用于搜索/保存/刪除數(shù)據(jù)庫中的條目, 就像這樣:
// 假定 Article 是一個用來管理文章的特殊類
// 通過 id 來移除文章的靜態(tài)方法:
Article.remove({id: 12345});
靜態(tài)方法不適用于單個對象
靜態(tài)方法可以在類上調(diào)用,而不是在單個對象上。
例如,這樣的代碼無法正常工作:
// ... article.createTodays(); /// Error: article.createTodays is not a function
最近新增的特性
這是一個最近添加到 JavaScript 的特性。 示例可以在最近的 Chrome 工作。
靜態(tài)的屬性也是可能的,它們看起來就像常規(guī)的類屬性,但前面加有 static
:
class Article {
static publisher = "Levi Ding";
}
alert( Article.publisher ); // Levi Ding
這等同于直接給 Article
賦值:
Article.publisher = "Levi Ding";
靜態(tài)屬性和方法是可被繼承的。
例如,下面這段代碼中的 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;
}
}
// 繼承于 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
現(xiàn)在我們調(diào)用 Rabbit.compare
時,繼承的 Animal.compare
將會被調(diào)用。
它是如何工作的?再次,使用原型。你可能已經(jīng)猜到了,extends
讓 Rabbit
的 [[Prototype]]
指向了 Animal
。
所以,Rabbit extends Animal
創(chuàng)建了兩個 [[Prototype]]
引用:
Rabbit
? 函數(shù)原型繼承自 ?Animal
? 函數(shù)。Rabbit.prototype
? 原型繼承自 ?Animal.prototype
?。結(jié)果就是,繼承對常規(guī)方法和靜態(tài)方法都有效。
這里,讓我們通過代碼來檢驗(yàn)一下:
class Animal {}
class Rabbit extends Animal {}
// 對于靜態(tài)的
alert(Rabbit.__proto__ === Animal); // true
// 對于常規(guī)方法
alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
靜態(tài)方法被用于實(shí)現(xiàn)屬于整個類的功能。它與具體的類實(shí)例無關(guān)。
舉個例子, 一個用于進(jìn)行比較的方法 Article.compare(article1, article2)
或一個工廠(factory)方法 Article.createTodays()
。
在類聲明中,它們都被用關(guān)鍵字 static
進(jìn)行了標(biāo)記。
靜態(tài)屬性被用于當(dāng)我們想要存儲類級別的數(shù)據(jù)時,而不是綁定到實(shí)例。
語法如下所示:
class MyClass {
static property = ...;
static method() {
...
}
}
從技術(shù)上講,靜態(tài)聲明與直接給類本身賦值相同:
MyClass.property = ...
MyClass.method = ...
靜態(tài)屬性和方法是可被繼承的。
對于 class B extends A
,類 B
的 prototype 指向了 A
:B.[[Prototype]] = A
。因此,如果一個字段在 B
中沒有找到,會繼續(xù)在 A
中查找。
正如我們所知道的,所有的對象通常都繼承自 Object.prototype
,并且可以訪問“通用”對象方法,例如 hasOwnProperty
等。
例如:
class Rabbit {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
// hasOwnProperty 方法來自于 Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true
但是,如果我們像這樣 "class Rabbit extends Object"
把它明確地寫出來,那么結(jié)果會與簡單的 "class Rabbit"
有所不同么?
不同之處在哪里?
下面是此類的示例代碼(它無法正常運(yùn)行 —— 為什么?修復(fù)它?):
class Rabbit extends Object {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // Error
首先,讓我們看看為什么之前的代碼無法運(yùn)行。
如果我們嘗試運(yùn)行它,就會發(fā)現(xiàn)原因其實(shí)很明顯。派生類的 constructor 必須調(diào)用 super()
。否則 "this"
不會被定義。
下面是修復(fù)后的代碼:
class Rabbit extends Object {
constructor(name) {
super(); // 需要在繼承時調(diào)用父類的 constructor
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // true
但這還不是全部原因。
即便修復(fù)了它,"class Rabbit extends Object"
和 class Rabbit
之間仍存在著一個重要的差異。
我們知道,“extends” 語法會設(shè)置兩個原型:
"prototype"
? 之間設(shè)置原型(為了獲取實(shí)例方法)。在 class Rabbit extends Object
的例子中,意味著:
class Rabbit extends Object {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) true
所以,現(xiàn)在 Rabbit
可以通過 Rabbit
訪問 Object
的靜態(tài)方法,像這樣:
class Rabbit extends Object {}
// 通常我們調(diào)用 Object.getOwnPropertyNames
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b
但是如果我們沒有 extends Object
,那么 Rabbit.__proto__
將不會被設(shè)置為 Object
。
下面是示例:
class Rabbit {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) false (!)
alert( Rabbit.__proto__ === Function.prototype ); // true,所有函數(shù)都是默認(rèn)如此
// error,Rabbit 中沒有這樣的函數(shù)
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error
所以,在這種情況下,Rabbit
沒有提供對 Object
的靜態(tài)方法的訪問。
順便說一下,Function.prototype
也有一些“通用”函數(shù)方法,例如 call
和 bind
等。在上述的兩種情況下它們都是可用的,因?yàn)閷τ趦?nèi)建的 Object
構(gòu)造函數(shù)而言,Object.__proto__ === Function.prototype
。
我們用一張圖來解釋:
所以,簡而言之,這里有兩點(diǎn)區(qū)別:
class Rabbit | class Rabbit extends Object |
---|---|
– | 需要在 constructor 中調(diào)用 super()
|
Rabbit.__proto__ === Function.prototype
|
Rabbit.__proto__ === Object
|
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: