クラス全体にメソッドを割り当てることもできます。このようなメソッドはstaticと呼ばれます。
クラス宣言では、次のようにstatic
キーワードが先頭に追加されます。
クラス ユーザー { 静的 staticMethod() { アラート(この === ユーザー); } } User.staticMethod(); // 真実
これは実際には、プロパティとして直接割り当てるのと同じことを行います。
クラス ユーザー { } User.staticMethod = function() { アラート(この === ユーザー); }; User.staticMethod(); // 真実
User.staticMethod()
呼び出しのthis
の値は、クラス コンストラクターUser
自体 (「ドットの前のオブジェクト」ルール) です。
通常、静的メソッドは、クラス全体に属するが、クラスの特定のオブジェクトには属さない関数を実装するために使用されます。
たとえば、 Article
オブジェクトがあり、それらを比較する関数が必要です。
自然な解決策は、 Article.compare
静的メソッドを追加することです。
クラス記事 { コンストラクター(タイトル、日付) { this.title = タイトル; this.date = 日付; } 静的比較(記事A, 記事B) { 記事A.date - 記事B.dateを返します。 } } // 使用法 記事 = [ 新しい記事("HTML", 新しい日付(2019, 1, 1)), 新しい記事("CSS", 新しい日付(2019, 0, 1)), 新しい記事("JavaScript", 新しい日付(2019, 11, 1)) ]; 記事.並べ替え(記事.比較); アラート(記事[0].タイトル); // CSS
ここで、 Article.compare
メソッドは、記事を比較する手段として「上」の記事に立っています。これは記事のメソッドではなく、クラス全体のメソッドです。
別の例は、いわゆる「ファクトリー」メソッドです。
たとえば、記事を作成するには複数の方法が必要だとします。
指定されたパラメータ ( title
、 date
など) を使用して作成します。
今日の日付を含む空の記事を作成します。
…あるいは何らかの方法で。
最初の方法はコンストラクターによって実装できます。 2 つ目では、クラスの静的メソッドを作成できます。
ここではArticle.createTodays()
のようになります。
クラス記事 { コンストラクター(タイトル、日付) { this.title = タイトル; this.date = 日付; } static createTodays() { // 覚えておいてください、これ = 記事 return new this("今日のダイジェスト", new Date()); } } letarticle = Article.createTodays(); アラート(記事.タイトル); // 今日のダイジェスト
これで、今日のダイジェストを作成する必要があるたびに、 Article.createTodays()
を呼び出すことができます。繰り返しますが、これは記事のメソッドではなく、クラス全体のメソッドです。
静的メソッドは、次のようにデータベースからエントリを検索、保存、削除するためにデータベース関連クラスでも使用されます。
// Article が記事を管理するための特別なクラスであると仮定します // ID によって記事を削除する静的メソッド: Article.remove({id: 12345});
静的メソッドは個々のオブジェクトには使用できません
静的メソッドは、個々のオブジェクトではなく、クラスに対して呼び出すことができます。
たとえば、次のようなコードは機能しません。
// ... 記事.createTodays(); /// エラー:article.createTodays は関数ではありません
最近の追加
これは言語に最近追加されたものです。 例は最近の Chrome で動作します。
静的プロパティも使用できます。これらは通常のクラス プロパティのように見えますが、先頭にstatic
が付加されます。
クラス記事 { 静的発行者 = "イリヤ カントール"; } アラート(記事.パブリッシャー); // イリヤ・カントール
これは、 Article
への直接代入と同じです。
Article.publisher = "イリヤ カントール";
静的なプロパティとメソッドは継承されます。
たとえば、以下のコードのAnimal.compare
とAnimal.planet
は継承され、 Rabbit.compare
とRabbit.planet
としてアクセスできます。
クラス動物{ 静止惑星 = "地球"; コンストラクター(名前, 速度) { this.speed = 速度; this.name = 名前; } 実行(速度 = 0) { this.speed += 速度; alert(`${this.name} は ${this.speed} の速度で実行されます。`); } 静的比較(動物A, 動物B) { 動物A.スピード - 動物B.スピードを返します; } } // 動物から継承 クラス Rabbit extends Animal { 隠れる() { alert(`${this.name} は非表示になります!`); } } ウサギにしましょう = [ 新うさぎ(『白うさぎ』、10)、 新うさぎ(『黒うさぎ』、5) ]; Rabbits.sort(Rabbit.compare); ウサギ[0].run(); // 黒うさぎは速度 5 で走ります。 アラート(Rabbit.planet); // 地球
ここでRabbit.compare
を呼び出すと、継承されたAnimal.compare
呼び出されます。
どのように機能するのでしょうか?ここでもプロトタイプを使用します。すでにご想像のとおり、 extends
Rabbit
Animal
への[[Prototype]]
参照を与えます。
したがって、 Rabbit extends Animal
2 つの[[Prototype]]
参照を作成します。
Rabbit
関数は、 Animal
関数をプロトタイプ的に継承しています。
Rabbit.prototype
プロトタイプはAnimal.prototype
を継承します。
その結果、継承は通常のメソッドと静的メソッドの両方で機能します。
ここで、コードで確認してみましょう。
クラス動物 {} クラス Rabbit は Animal {} を拡張します // 静的な場合 alert(Rabbit.__proto__ === 動物); // 真実 // 通常のメソッドの場合 alert(Rabbit.prototype.__proto__ === Animal.prototype); // 真実
静的メソッドは、「全体として」クラスに属する機能に使用されます。具体的なクラスのインスタンスとは関係ありません。
たとえば、比較メソッドArticle.compare(article1, article2)
やファクトリ メソッドArticle.createTodays()
などです。
これらは、クラス宣言でstatic
単語でラベル付けされます。
静的プロパティは、インスタンスにバインドされていないクラスレベルのデータを保存したい場合に使用されます。
構文は次のとおりです。
クラス MyClass { 静的プロパティ = ...; 静的メソッド() { ... } }
技術的には、静的宣言はクラス自体に代入することと同じです。
MyClass.property = ... MyClass.メソッド = ...
静的なプロパティとメソッドは継承されます。
class B extends A
場合、クラスB
自体のプロトタイプはA
: B.[[Prototype]] = A
を指します。したがって、 B
でフィールドが見つからない場合、検索はA
で続行されます。
重要性: 3
ご存知のとおり、すべてのオブジェクトは通常、 Object.prototype
を継承し、 hasOwnProperty
などの「汎用」オブジェクト メソッドにアクセスします。
例えば:
クラスウサギ{ コンストラクター(名前) { this.name = 名前; } } let Rabbit = new Rabbit("Rab"); // hasOwnProperty メソッドは Object.prototype からのものです alert( Rabbit.hasOwnProperty('name') ); // 真実
しかし、 "class Rabbit extends Object"
のように明示的に記述すると、結果は単純な"class Rabbit"
とは異なります。
違いは何ですか?
そのようなコードの例を次に示します (機能しません。なぜですか? 修正してください)。
class Rabbit extends Object { コンストラクター(名前) { this.name = 名前; } } let Rabbit = new Rabbit("Rab"); alert( Rabbit.hasOwnProperty('name') ); // エラー
まず、後者のコードが機能しない理由を見てみましょう。
その理由は実際に実行してみると明らかです。継承クラスのコンストラクターはsuper()
を呼び出す必要があります。そうしないと、 "this"
「定義」されません。
それで、修正は次のとおりです。
class Rabbit extends Object { コンストラクター(名前) { 素晴らしい(); // 継承時に親コンストラクタを呼び出す必要がある this.name = 名前; } } let Rabbit = new Rabbit("Rab"); alert( Rabbit.hasOwnProperty('name') ); // 真実
しかし、それだけではありません。
修正後でも、 "class Rabbit extends Object"
とclass Rabbit
の間には依然として重要な違いがあります。
ご存知のとおり、「extends」構文は 2 つのプロトタイプを設定します。
コンストラクター関数の"prototype"
間 (メソッドの場合)。
コンストラクター関数自体の間 (静的メソッドの場合)。
class Rabbit extends Object
の場合、次のことを意味します。
クラス Rabbit はオブジェクト {} を拡張します alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) 真 alert( Rabbit.__proto__ === オブジェクト ); // (2) true
したがって、 Rabbit
次のようにRabbit
経由でObject
の静的メソッドへのアクセスを提供するようになりました。
クラス Rabbit はオブジェクト {} を拡張します // 通常は Object.getOwnPropertyNames を呼び出します アラート ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a、b
ただし、 extends Object
がない場合、 Rabbit.__proto__
Object
に設定されません。
デモは次のとおりです。
クラスウサギ {} alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) 真 alert( Rabbit.__proto__ === オブジェクト ); // (2) 偽 (!) alert( Rabbit.__proto__ === Function.prototype ); // デフォルトでは任意の関数として // エラー、Rabbit にはそのような関数はありません アラート ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // エラー
したがって、その場合、 Rabbit
Object
の静的メソッドへのアクセスを提供しません。
ちなみに、 Function.prototype
は、 call
やbind
などの「汎用」関数メソッドもあります。組み込みのObject
コンストラクターの場合、 Object.__proto__ === Function.prototype
であるため、これらは最終的にどちらの場合でも使用できます。
写真は次のとおりです。
簡単に言うと、次の 2 つの違いがあります。
うさぎクラス | クラス Rabbit はオブジェクトを拡張します |
---|---|
– | コンストラクターでsuper() 呼び出す必要があります |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |