我们还可以将一个方法分配给整个类。这种方法称为静态方法。
在类声明中,它们前面加上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` 方法位于文章“上方”,作为比较它们的一种手段。它不是文章的方法,而是整个类的方法。
另一个示例是所谓的“工厂”方法。
假设我们需要多种方式来创建文章
- 通过给定参数(`title`、`date` 等)创建。
- 使用今天的日期创建一篇空文章。
- …或以其他方式。
第一种方法可以通过构造函数实现。对于第二种方法,我们可以创建类的静态方法。
例如此处的 `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…)