静态方法

我们可以直接给类(函数)自身添加方法,而不是在它的“prototype”属性上。这种方式添加的方法称为静态的。

在类中,使用 static 前缀声明静态方法。

  1. class User {
  2. static staticMethod() {
  3. alert(this === User);
  4. }
  5. }
  6. User.staticMethod(); // true

上面的写法,等同于下面的写法:

  1. class User { }
  2. User.staticMethod = function() {
  3. alert(this === User);
  4. };
  5. User.staticMethod(); // true

调用 User.staticMethod() 时,方法里的 this 指向类构造器 User(遵循“点前对象”的规则)

静态方法是绑定于某个特定类上的函数,不是特定于某个实例对象上的函数。

例如,我们有 Article 对象,需要一个函数来对文章做比较。通常的做法是添加一个 Article.compare 方法:

  1. class Article {
  2. constructor(title, date) {
  3. this.title = title;
  4. this.date = date;
  5. }
  6. static compare(articleA, articleB) {
  7. return articleA.date - articleB.date;
  8. }
  9. }
  10. // 使用
  11. let articles = [
  12. new Article("HTML", new Date(2019, 1, 1)),
  13. new Article("CSS", new Date(2019, 0, 1)),
  14. new Article("JavaScript", new Date(2019, 11, 1))
  15. ];
  16. // 按照升序排序
  17. articles.sort(Article.compare);
  18. alert( articles[0].title ); // CSS

Article.compare 不是文章实例对象的方法,而是 Article 类的方法,是“凌驾于”文章实例对象之上的。

还有另一类称为“工厂”方法的例子。

  1. 用给定参数创建(titledate 等)。
  2. 用今天的日期(new Date)创建一个空的文章对象。
  3. ……或者做些其他的些什么

第一种是使用构造器的方式。第二种则可以通过类的静态方法实现。

类似下面的 Artcile.createTodays() 这种:

  1. class Article {
  2. constructor(title, date) {
  3. this.title = title;
  4. this.date = date;
  5. }
  6. static createTodays() {
  7. // 此处的 this 指向的是 Article
  8. return new this("Today's digest", new Date());
  9. }
  10. }
  11. let article = Article.createTodays();
  12. alert( article.title ); // Today's digest

现在,每当我们要创建 Today’s digest 的时候,直接调用 Article.createTodays() 就可以了。再说一下啊,这可不是实例对象的方法,而是类方法。

这类静态方法,还可以用来在数据库操作相关的类上,负责数据的增/删/改/查等操作。

  1. // 假定 Article 是个用于管理文章的特殊类
  2. // 用于删除文章的静态方法:
  3. Article.remove({id: 12345});

静态属性

⚠️ 最近才添加

这是最近才添加到标准的特性。请在最新的 Chrome 浏览器上执行。

类中也支静态属性,就是在普通类属性前面加个 static 关键字前缀:

  1. class Article {
  2. static publisher = "Ilya Kantor";
  3. }
  4. alert( Article.publisher ); // Ilya Kantor

它等同于下面的写法:

  1. Article.publisher = "Ilya Kantor";

继承静态属性和方法

静态属性和方法是能被继承的。

举个例子,下面的代码,Rabbit 继承自 AnimalAnimal.compareAnimal.planet 方法也能在 Rabbit 上访问到。

  1. class Animal {
  2. static planet = "Earth";
  3. constructor(name, speed) {
  4. this.speed = speed;
  5. this.name = name;
  6. }
  7. run(speed = 0) {
  8. this.speed += speed;
  9. alert(`${this.name} runs with speed ${this.speed}.`);
  10. }
  11. static compare(animalA, animalB) {
  12. return animalA.speed - animalB.speed;
  13. }
  14. }
  15. // 继承自 Animal
  16. class Rabbit extends Animal {
  17. hide() {
  18. alert(`${this.name} hides!`);
  19. }
  20. }
  21. let rabbits = [
  22. new Rabbit("White Rabbit", 10),
  23. new Rabbit("Black Rabbit", 5)
  24. ];
  25. rabbits.sort(Rabbit.compare);
  26. rabbits[0].run(); // Black Rabbit runs with speed 5.
  27. alert(Rabbit.planet); // Earth

现在我们调用 Rabbit.compare 调用的就是继承的 Animal.compare 方法。

这里面的工作原理是什么呢?还是使用的原型。大家可能也猜到了,extends 关键字令 Rabbit[[Prototype]] 属性指向了 Animal

image.png

也就是说,Rabbit extends Animal 创建了两个 [[Prototype]] 的引用:

  1. Rabbit 的原型对象指向 Animal。即,Rabbit.__proto__ === Animal 的结果为 true
  2. Rabbit.prototype 的原型对象指向 Animal.prototype。即 Rabbit.prototype``.__proto__ === ``Animal.``prototype 的结果为 true

结果,通过 extends 关键字,普通方法和静态方法都被继承了。

我们用代码检查下:

  1. class Animal {}
  2. class Rabbit extends Animal {}
  3. // 静态方法通过下面的关系得以继承
  4. alert(Rabbit.__proto__ === Animal); // true
  5. // 普通方法则通过下面的关系得以继承
  6. alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

总结

静态方法是绑定于某个特定类上的函数,不是特定于某个实例对象上的函数。

比如,像文中列举的比较方法 Article.compare(article1, article2) 和工厂方法 Article.createTodays() 都属于静态方法。

静态方法和静态属性都是使用 static 前缀标识的。

静态属性用于存储类级别数据,也不是跟某个特定实例对象绑定的。

语法为:

  1. class MyClass {
  2. static property = ...;
  3. static method() {
  4. ...
  5. }
  6. }

技术上,静态属性/方法的声明方式等同于下面的代码:

  1. MyClass.property = ...
  2. MyClass.method = ...

静态属性和方法都是能被继承的。

class B extends A 后,A 的原型对象是 B:即 B.[[Prototype]] === A 的结果为 true。因此,如果在 B 上没有找到的话,就继续从 A 上找。


📄 文档信息

🕘 更新时间:2020/02/05
🔗 原文链接:https://javascript.info/static-properties-methods