オブジェクト指向プログラミングでは、クラスはオブジェクトを作成するための拡張可能なプログラム コード テンプレートであり、状態 (メンバー変数) の初期値と動作の実装 (メンバー関数またはメソッド) を提供します。
実際には、ユーザーや商品など、同じ種類のオブジェクトを多数作成する必要があることがよくあります。
コンストラクターの章ですでにわかっているように、演算子「new」、 new function
これに役立ちます。
しかし、最新の JavaScript には、より高度な「クラス」構造があり、オブジェクト指向プログラミングに役立つ優れた新機能が導入されています。
基本的な構文は次のとおりです。
クラス MyClass { // クラスメソッド コンストラクタ() { ... } メソッド1() { ... } メソッド2() { ... } メソッド3() { ... } ... }
次に、 new MyClass()
使用して、リストされているすべてのメソッドを含む新しいオブジェクトを作成します。
constructor()
メソッドはnew
によって自動的に呼び出されるので、そこでオブジェクトを初期化できます。
例えば:
クラス ユーザー { コンストラクター(名前) { this.name = 名前; } SayHi() { アラート(この名前); } } // 使用法: let user = new User("John"); user.sayHi();
new User("John")
が呼び出されるとき:
新しいオブジェクトが作成されます。
constructor
指定された引数を使用して実行され、それをthis.name
に割り当てます。
…その後、 user.sayHi()
などのオブジェクト メソッドを呼び出すことができます。
クラスメソッド間にカンマは不要
初心者の開発者にとってよくある落とし穴は、クラス メソッドの間にカンマを入れることであり、構文エラーが発生します。
ここでの表記をオブジェクト リテラルと混同しないでください。クラス内ではカンマは必要ありません。
では、 class
とは正確には何でしょうか?考えられているように、これはまったく新しい言語レベルのエンティティではありません。
魔法を明らかにして、クラスが実際に何であるかを見てみましょう。それは多くの複雑な側面を理解するのに役立ちます。
JavaScript では、クラスは関数の一種です。
ここで見てください:
クラス ユーザー { コンストラクター(名前) { this.name = 名前; } SayHi() { アラート(this.name); } } // 証明: ユーザーは関数です アラート(ユーザーのタイプ); // 関数
class User {...}
コンストラクトが実際に行うことは次のとおりです。
User
という名前の関数を作成します。これはクラス宣言の結果になります。関数コードはconstructor
メソッドから取得されます (そのようなメソッドを作成しない場合は空であると想定されます)。
sayHi
などのクラス メソッドをUser.prototype
に格納します。
new User
オブジェクトが作成された後、そのメソッドを呼び出すと、F.prototype の章で説明したとおり、プロトタイプから取得されます。したがって、オブジェクトはクラスメソッドにアクセスできます。
class User
宣言の結果は次のように説明できます。
これをイントロスペクトするコードは次のとおりです。
クラス ユーザー { コンストラクター(名前) { this.name = 名前; } SayHi() { アラート(this.name); } } // クラスは関数です アラート(ユーザーのタイプ); // 関数 // ...または、より正確には、コンストラクター メソッド アラート(ユーザー === User.prototype.constructor); // 真実 // メソッドは User.prototype にあります。例: アラート(User.prototype.sayHi); //sayHiメソッドのコード // プロトタイプには正確に 2 つのメソッドがあります alert(Object.getOwnPropertyNames(User.prototype)); // コンストラクター、sayHi
時々、 class
「糖衣構文」(読みやすくするために設計された構文ですが、新しいものは何も導入していません)であると言われることがあります。なぜなら、実際にはclass
キーワードをまったく使用しなくても同じことを宣言できるからです。
// 純粋関数内のクラス User を書き換える // 1. コンストラクター関数を作成する 関数 ユーザー(名前) { this.name = 名前; } // 関数プロトタイプにはデフォルトで「constructor」プロパティがあり、 // したがって、作成する必要はありません // 2. メソッドをプロトタイプに追加します User.prototype.sayHi = function() { アラート(この名前); }; // 使用法: let user = new User("John"); user.sayHi();
この定義の結果はほぼ同じです。したがって、 class
そのプロトタイプ メソッドとともにコンストラクターを定義するための糖衣構文とみなされる理由は確かにあります。
それでも、重要な違いがあります。
まず、 class
によって作成された関数は、特別な内部プロパティ[[IsClassConstructor]]: true
によってラベル付けされます。したがって、手動で作成するのとまったく同じではありません。
言語はさまざまな場所でそのプロパティをチェックします。たとえば、通常の関数とは異なり、 new
使用して呼び出す必要があります。
クラス ユーザー { コンストラクター() {} } アラート(ユーザーのタイプ); // 関数 ユーザー(); // エラー: クラス コンストラクター ユーザーは 'new' なしでは呼び出せません
また、ほとんどの JavaScript エンジンにおけるクラス コンストラクターの文字列表現は、「class…」で始まります。
クラス ユーザー { コンストラクター() {} } アラート(ユーザー); // クラス ユーザー { ... }
他にも違いがあります。それについてはすぐに説明します。
クラスメソッドは列挙可能ではありません。クラス定義は、 "prototype"
内のすべてのメソッドに対してenumerable
フラグをfalse
に設定します。
これは良いことです。なぜなら、オブジェクトに対してfor..in
実行する場合、通常はそのクラス メソッドが必要ないからです。
クラスは常にuse strict
。クラス構造内のすべてのコードは、自動的に厳密モードになります。
さらに、 class
構文は、後で説明する他の多くの機能ももたらします。
関数と同様に、クラスも別の式内で定義したり、渡したり、返したり、割り当てたりすることができます。
クラス式の例を次に示します。
let ユーザー = クラス { SayHi() { アラート("こんにちは"); } };
名前付き関数式と同様に、クラス式にも名前がある場合があります。
クラス式に名前がある場合、その式はクラス内でのみ表示されます。
// 「名前付きクラス式」 // (仕様にはそのような用語はありませんが、名前付き関数式に似ています) let User = クラス MyClass { SayHi() { アラート(MyClass); // MyClass 名はクラス内でのみ表示されます } }; new User().sayHi(); // 動作し、MyClass 定義を表示します アラート(MyClass); // エラー。MyClass 名はクラスの外部には表示されません
次のように、クラスを動的に「オンデマンド」で作成することもできます。
関数 makeClass(フレーズ) { // クラスを宣言してそれを返す 戻りクラス { SayHi() { アラート(フレーズ); } }; } // 新しいクラスを作成します let User = makeClass("Hello"); new User().sayHi(); // こんにちは
リテラル オブジェクトと同様に、クラスにはゲッター/セッター、計算プロパティなどが含まれる場合があります。
get/set
使用して実装されたuser.name
の例を次に示します。
クラス ユーザー { コンストラクター(名前) { // セッターを呼び出します this.name = 名前; } 名前を取得() { これを返します。_name; } 名前(値)を設定 { if (value.length < 4) { alert("名前が短すぎます。"); 戻る; } this._name = 値; } } let user = new User("John"); アラート(ユーザー名); // ジョン ユーザー = 新しいユーザー(""); // 名前が短すぎます。
技術的には、このようなクラス宣言は、 User.prototype
にゲッターとセッターを作成することで機能します。
以下は、括弧[...]
を使用して計算されたメソッド名の例です。
クラス ユーザー { ['言う' + 'こんにちは']() { アラート("こんにちは"); } } new User().sayHi();
このような特徴は、文字通りのオブジェクトの特徴に似ているため、覚えやすいです。
古いブラウザにはポリフィルが必要な場合があります
クラス フィールドは最近言語に追加されたものです。
以前は、クラスにはメソッドしかありませんでした。
「クラスフィールド」は、任意のプロパティを追加できる構文です。
たとえば、 name
プロパティをclass User
に追加してみましょう。
クラス ユーザー { 名前 = "ジョン"; SayHi() { alert(`こんにちは、${this.name}!`); } } new User().sayHi(); // こんにちは、ジョン!
したがって、単に「」と書きます。
クラス フィールドの重要な違いは、 User.prototype
ではなく、個々のオブジェクトに設定されることです。
クラス ユーザー { 名前 = "ジョン"; } let user = new User(); アラート(ユーザー名); // ジョン アラート(ユーザー.プロトタイプ.名); // 未定義
より複雑な式や関数呼び出しを使用して値を割り当てることもできます。
クラス ユーザー { name = プロンプト("名前をお願いしますか?", "ジョン"); } let user = new User(); アラート(ユーザー名); // ジョン
「関数バインディング」の章で説明したように、JavaScript の関数には動的なthis
があります。それは通話のコンテキストによって異なります。
したがって、オブジェクト メソッドが渡されて別のコンテキストで呼び出された場合、 this
そのオブジェクトへの参照ではなくなります。
たとえば、次のコードではundefined
表示されます。
クラスボタン{ コンストラクター(値) { this.value = 値; } クリック() { アラート(この値); } } let button = new Button("hello"); setTimeout(button.click, 1000); // 未定義
この問題は「 this
失う」と呼ばれます。
「関数バインディング」の章で説明したように、これを修正するには 2 つのアプローチがあります。
setTimeout(() => button.click(), 1000)
などのラッパー関数を渡します。
コンストラクターなどでメソッドをオブジェクトにバインドします。
クラス フィールドは、別の非常に洗練された構文を提供します。
クラスボタン { コンストラクター(値) { this.value = 値; } クリック = () => { アラート(この値); } } let button = new Button("hello"); setTimeout(button.click, 1000); // こんにちは
クラス フィールドclick = () => {...}
はオブジェクトごとに作成され、 Button
オブジェクトごとに個別の関数があり、 this
内部でそのオブジェクトを参照します。 button.click
どこにでも渡すことができ、 this
値は常に正しいものになります。
これは、ブラウザ環境のイベント リスナーにとって特に便利です。
基本的なクラス構文は次のようになります。
クラス MyClass { プロパティ = 値; // 財産 constructor(...) { // コンストラクター // ... } Method(...) {} // メソッド get something(...) {} // ゲッター メソッド set something(...) {} // セッターメソッド [Symbol.iterator]() {} // 計算された名前を持つメソッド (ここではシンボル) // ... }
MyClass
は技術的には関数 ( constructor
として提供される関数) ですが、メソッド、ゲッター、セッターはMyClass.prototype
に書き込まれます。
次の章では、継承やその他の機能を含むクラスについて詳しく学習します。
重要度: 5
Clock
クラス (サンドボックスを参照) は関数型スタイルで記述されています。これを「class」構文に書き換えます。
PS コンソールの時計が動いているので、開いて確認してください。
タスクのサンドボックスを開きます。
クラスクロック{ コンストラクター({ テンプレート }) { this.template = テンプレート; } 与える() { let date = new Date(); let hours = date.getHours(); if (時間 < 10) 時間 = '0' + 時間; let mins = date.getMinutes(); if (分 < 10) 分 = '0' + 分; let secs = date.getSeconds(); if (秒 < 10) 秒 = '0' + 秒; 出力 = this.template にします .replace('h', 時間) .replace('m', 分) .replace('s', 秒); console.log(出力); } 停止() { clearInterval(this.timer); } 始める() { this.render(); this.timer = setInterval(() => this.render(), 1000); } } let Clock = new Clock({template: 'h:m:s'}); クロック.スタート();
サンドボックスでソリューションを開きます。