クラス全体にメソッドを割り当てることもできます。そのようなメソッドは静的と呼ばれます。
クラス宣言では、このような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メソッドは、記事の「上」にあり、それらの比較に使用されます。これら記事のメソッドではなく、むしろクラス全体のメソッドです。
もう1つの例としては、いわゆる「ファクトリ」メソッドがあります。
記事を作成するための方法が複数必要だとしましょう
- 指定されたパラメータ(
title、dateなど)による作成 - 今日の日付で空の記事を作成する
- …あるいは他の何らかの方法
最初の方法はコンストラクタによって実装できます。2番目のメソッドについては、クラスの静的メソッドを作成できます。
ここでは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 は 2 つの [[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...)を使用します。