【Javascript】 クラスの書き方
Javascriptでのクラスの書き方と扱いを紹介します。また、privateなフィールドやメソッドを作る方法も記載しています。
宣言
クラス宣言、フィールド、メソッドの一通りを含んだ例です。
class Person { constructor(name) { this.field1 = 100; this.name = name; // 自クラスのインスタンスのメソッド呼び出し this.method1(); } method1() { this.field1 += 100; console.log("parent"); } //... } let p = new Person("MyName"); console.log(p.name); p.method1();
parent MyName parent
クラスのフィールドは、this.fieldname
のように、this.
を付けて宣言します。コンストラクタ以外でも作成できます。
メソッドの宣言では、function
が不要です。
メンバは全てパブリックになります。そのため、プライベートにしたい場合はフィールド名の頭にアンダーバーを付けるなどの対策が一般的です。
デストラクタはありません。
staticなメンバ
クラスの直下にstatic
を付けて配置します。メソッドはstatic
を付けるだけですが、フィールドもクラスの直下に置きます。
class StaticSample { static staticProperty = "static property"; static staticMethod() { return "static method"; } constructor() { this.staticProperty = 300; // 名前は同じだが別の変数で, staticではない } hoge() { // 自身のクラス内でも ClassName.member の形式 StaticSample.staticProperty = 100; } } // staticなメソッド console.log(StaticSample.staticMethod()); // staticな値 console.log(StaticSample.staticProperty);
static method static property
staticなメソッドや値は、ClassName.member
の形式で呼び出します。同名の普通の変数やメソッドを宣言した場合、別々の値として扱われます。
staticなため、すべてのインスタンスでただ1つの値を共有します。
継承
extends
を付けると継承が可能です.
class Person {/* ... */} class Jhon extends Person { constructor() { // 親のコンストラクタ呼び出し super("Jhon"); } // Personのmethod1をオーバーライド method1() { super.method1(); // 親クラスのmethod1を呼び出し this.field2 += 200; console.log("child"); } method2() { // ... } }
オーバーライド
メソッドのオーバーライドを行った場合、親クラスのオーバーライドされたメソッドを呼び出すにはsuper.method(args)
のように書きます。
class Jhon extends Person { // ... // Personのmethod1をオーバーライド method1() { super.method1(); // 親クラスのmethod1を呼び出し this.field2 += 200; console.log("child"); } } let j = new Jhon(); j.method1();
parent child
親クラスのコンストラクタは、super(args);
で呼び出します。
privateなメンバ
Symbol
を利用して、一応privateなメンバは作成できます。
const Person = (() => { // 即時関数で囲まれているため、Symbolは1度だけ作成され、すべてのインスタンスで使い回される const _name = Symbol(); const _setNameInner = Symbol(); return class Person { constructor(name) { this[_name] = name; // 普通の代入 } getName() { return this[_name]; } setName(name) { this[_setNameInner](name); // privateメソッドの呼び出し } [_setNameInner](name) { this[_name] = name + " Inner"; } } })(); const p = new Person("Jhon"); const p2 = new Person("Jack"); console.log(p.getName()); console.log(p2.getName()); p.setName("Jhon2"); p2.setName("Jack2"); console.log(p.getName()); console.log(p2.getName());
クロージャを利用して外部から見えない値を作ることで、実質privateなメンバを作成します。
しかし、書き方が非常に違和感があるため、TypeScriptが使えない場合の最終手段とも言えます。
Symbol
でなくても、この即時関数内でユニークな値にできるなら何でも良いです。ただ、それに最適なのがSymbolであるということです。
const _name = "a"; const _setNameInner = "b";
でも正常に動きます。
その他仕様
- アロー関数とfunctionでthisの扱いが異なる
- interfaceやabstractに相当するものは無い
jsdoc形式で書くのが精一杯 - 多重継承はできない
- メンバはいつでもどこでも作れる
特にinterfaceがないという点で、オブジェクト指向で書くことに限界があります。
そのため、大規模なアプリはTypeScriptで書くことを強く推奨します。
まとめ
Javascriptのクラスには制約があります。特にフィールドの作る場所がどこでも良いということもあり、とんでもないプログラムも書けてしまいます。interfaceはなく、privateなメンバも作りづらい使いづらいという状態で、あまり使い勝手はよくありません。
TypeScriptを利用しない場合、この制約を知った上でクラス設計をしていきましょう。
