サーチ…


構文

  • クラスFoo {}
  • クラスFooはBar {}を拡張します
  • クラスFoo {コンストラクタ(){}}
  • クラスFoo {myMethod(){}}
  • クラスFoo {get myProperty(){}}
  • クラスFoo {set myProperty(newValue){}}
  • クラスFoo {static myStaticMethod(){}}
  • クラスFoo {static get myStaticProperty(){}}
  • const Foo =クラスFoo {};
  • const Foo =クラス{};

備考

classサポートは、2015 標準の一部としてJavaScriptに追加されました。

Javascriptクラスは、JavaScriptの既存のプロトタイプベースの継承を超える構文上の砂糖です。この新しいシンタックスでは、オブジェクト指向の継承モデルをJavaScriptに導入することはなく、オブジェクトや継承を処理する簡単な方法です。 class宣言は、基本的に、コンストラクタfunctionを手動で定義し、そのコンストラクタのプロトタイプにプロパティを追加するための省略表現です。重要な違いは、関数を直接( newキーワードなしで)呼び出すことができますが、直接呼び出されたクラスは例外をスローする点です。

class someClass {
    constructor () {}
    someMethod () {}
}
 
console.log(typeof someClass);               
console.log(someClass);
console.log(someClass === someClass.prototype.constructor);                         
console.log(someClass.prototype.someMethod);
 
// Output:
// function
// function someClass() { "use strict"; }
// true
// function () { "use strict"; }

以前のバージョンのJavaScriptを使用している場合は、ターゲットプラットフォームが理解できるバージョンにコードをコンパイルするために、 ようなトランスパイライザーが必要です。

クラスコンストラクタ

ほとんどのクラスの基本的な部分は、各インスタンスの初期状態を設定し、 new呼び出すときに渡されたパラメータを処理するコンストラクタです。

これは実際に特殊なケースとして扱われますが、 constructorという名前のメソッドを定義しているかのようにclassブロックで定義されています。

class MyClass {
    constructor(option) {
        console.log(`Creating instance using ${option} option.`);
        this.option = option;
    }
}

使用例:

const foo = new MyClass('speedy'); // logs: "Creating instance using speedy option"

staticキーワードを使ってクラスコンストラクタを静的にすることはできません。他のメソッドについては後述します。

静的メソッド

静的メソッドおよびプロパティは、クラス/コンストラクタ自体で定義されインスタンスオブジェクトでは定義されません。これらは、 staticキーワードを使用してクラス定義で指定します。

class MyClass {
    static myStaticMethod() {
        return 'Hello';
    }

    static get myStaticProperty() {
        return 'Goodbye';
    }
}

console.log(MyClass.myStaticMethod()); // logs: "Hello"
console.log(MyClass.myStaticProperty); // logs: "Goodbye"

静的プロパティがオブジェクトインスタンスで定義されていないことがわかります。

const myClassInstance = new MyClass();

console.log(myClassInstance.myStaticProperty); // logs: undefined

ただし、それらサブクラスで定義されています

class MySubClass extends MyClass {};

console.log(MySubClass.myStaticMethod()); // logs: "Hello"
console.log(MySubClass.myStaticProperty); // logs: "Goodbye"

ゲッターとセッター

GettersとSetterを使用すると、クラスのプロパティを読み書きするためのカスタム動作を定義できます。ユーザーには、典型的なプロパティと同じように見えます。ただし、内部的には、プロパティにアクセスするときの値(ゲッター)を決定し、プロパティが割り当てられたときに必要な変更(設定)を行うために、提供するカスタム関数を使用します。

class定義では、getterは、引数なしのメソッドのgetキーワードで始まります。 setterは、1つの引数(新しい値が割り当てられます)を受け入れ、代わりにsetキーワードが使用される点を除いて同様です。

.nameプロパティのゲッターとセッターを提供するクラスの例を次に示します。割り当てられるたびに、新しい名前が内部の.names_配列に記録されます。アクセスするたびに、最新の名前が返されます。

class MyClass {
    constructor() {
        this.names_ = [];
    }

    set name(value) {
        this.names_.push(value);
    }

    get name() {
        return this.names_[this.names_.length - 1];
    }
}

const myClassInstance = new MyClass();
myClassInstance.name = 'Joe';
myClassInstance.name = 'Bob';

console.log(myClassInstance.name); // logs: "Bob"
console.log(myClassInstance.names_); // logs: ["Joe", "Bob"]

セッターを定義するだけであれば、プロパティにアクセスしようとすると常にundefinedが返されます。

const classInstance = new class {
    set prop(value) {
        console.log('setting', value);
    }
};

classInstance.prop = 10; // logs: "setting", 10

console.log(classInstance.prop); // logs: undefined

ゲッターを定義するだけであれば、プロパティの割り当てを試みても効果はありません。

const classInstance = new class {
    get prop() {
        return 5;
    }
};

classInstance.prop = 10;

console.log(classInstance.prop); // logs: 5

クラス継承

継承は、他のオブジェクト指向言語と同様に機能します。スーパークラスに定義されたメソッドは、拡張サブクラスでアクセス可能です。

サブクラスは、独自のコンストラクタを宣言した場合、それは経由して親のコンストラクタを呼び出す必要がありますsuper()それがアクセスする前this

class SuperClass {

    constructor() {
        this.logger = console.log;
    }

    log() {
        this.logger(`Hello ${this.name}`);
    }

}

class SubClass extends SuperClass {

    constructor() {
        super();
        this.name = 'subclass';
    }

}

const subClass = new SubClass();

subClass.log(); // logs: "Hello subclass"

プライベートメンバー

JavaScriptは技術的にプライベートメンバーを言語機能としてサポートしていません。 Douglas Crockfordによって記述されたプライバシーはコンストラクタ関数のインスタンス化の呼び出しごとに生成されるクロージャ(保存された関数スコープ)を介して代わりにエミュレートされます。

Queue例では、コンストラクタ関数を使用してローカルステートを保持し、特権メソッドによってアクセス可能にする方法を示します。

class Queue {

  constructor () {                    // - does generate a closure with each instantiation.

    const list = [];                  // - local state ("private member").

    this.enqueue = function (type) {  // - privileged public method
                                      //   accessing the local state
      list.push(type);                //   "writing" alike.
      return type;
    };
    this.dequeue = function () {      // - privileged public method
                                      //   accessing the local state
      return list.shift();            //   "reading / writing" alike.
    };
  }
}


var q = new Queue;            //
                              //
q.enqueue(9);                 // ... first in ...
q.enqueue(8);                 //
q.enqueue(7);                 //
                              //
console.log(q.dequeue());     // 9 ... first out.
console.log(q.dequeue());     // 8
console.log(q.dequeue());     // 7
console.log(q);               // {}
console.log(Object.keys(q));  // ["enqueue","dequeue"]

Queue型の各インスタンス化では、コンストラクタがクロージャを生成します。

このように、両方のQueue種類の独自のメソッドのenqueuedequeue (参照Object.keys(q) )まだへのアクセス持っているlist構築時に、保存されている、ことを囲みスコープに住み続けています。

特権を持つパブリックメソッドを使用してプライベートメンバーをエミュレートするこのパターンを利用すると、すべてのインスタンスで追加のメモリがすべてのプロパティメソッドで消費されることに注意する必要があります(共有/再利用できないコード)。そのようなクロージャ内に格納される状態の量/サイズについても同じことが当てはまります。

動的メソッド名

[]オブジェクトのプロパティにアクセスする方法に似た名前を付けるときに、式を評価する機能もあります。これは動的なプロパティ名を持つのに便利ですが、しばしば記号と組み合わせて使用​​されます。

let METADATA = Symbol('metadata');

class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
  // example using symbols
  [METADATA]() {
    return {
      make: this.make,
      model: this.model
    };
  }

  // you can also use any javascript expression

  // this one is just a string, and could also be defined with simply add()
  ["add"](a, b) {
    return a + b;
  }

  // this one is dynamically evaluated
  [1 + 2]() {
    return "three";
  }
}

let MazdaMPV = new Car("Mazda", "MPV");
MazdaMPV.add(4, 5); // 9
MazdaMPV[3](); // "three"
MazdaMPV[METADATA](); // { make: "Mazda", model: "MPV" }

メソッド

メソッドをクラス内で定義して、関数を実行し、オプションで結果を返すことができます。
彼らは呼び出し元から引数を受け取ることができます。

class Something {
    constructor(data) {
        this.data = data
    }

    doSomething(text) {
        return {
            data: this.data,
            text
        }
    }
}

var s = new Something({})
s.doSomething("hi") // returns: { data: {}, text: "hi" }

クラスによるプライベートデータの管理

クラスを使用する際の最も一般的な障害の1つは、プライベートな状態を処理するための適切なアプローチを見つけることです。プライベートな状態を扱うための4つの共通の解決策があります:

シンボルの使用

シンボルは、ES2015で導入された新しいプリミティブ型で、 MDNで定義されています

シンボルは、オブジェクトプロパティの識別子として使用できる一意で不変なデータ型です。

シンボルをプロパティキーとして使用する場合は、列挙できません。

そのfor var inObject.keysを使っfor var in Object.keysれることはありません。

したがって、シンボルを使用してプライベートデータを格納することができます。

const topSecret = Symbol('topSecret'); // our private key; will only be accessible on the scope of the module file
export class SecretAgent{
    constructor(secret){
        this[topSecret] = secret; // we have access to the symbol key (closure)
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret[topSecret]); // we have access to topSecret
        };
    }
}

symbolsは一意であるため、プライベートプロパティにアクセスするには元のシンボルを参照する必要があります。

import {SecretAgent} from 'SecretAgent.js'
const agent = new SecretAgent('steal all the ice cream');
// ok lets try to get the secret out of him!
Object.keys(agent); // ['coverStory'] only cover story is public, our secret is kept.
agent[Symbol('topSecret')]; // undefined, as we said, symbols are always unique, so only the original symbol will help us to get the data.

しかし、100%プライベートではありません。そのエージェントを壊そう! Object.getOwnPropertySymbolsメソッドを使用してオブジェクトシンボルを取得できます。

const secretKeys = Object.getOwnPropertySymbols(agent);
agent[secretKeys[0]] // 'steal all the ice cream' , we got the secret.

弱マップの使用

WeakMapWeakMapために追加された新しいタイプのオブジェクトです。

MDNで定義されている

WeakMapオブジェクトは、キーが弱く参照されるキーと値のペアの集合です。キーはオブジェクトでなければならず、値は任意の値にすることができます。

WeakMap 1つの重要な機能は、 MDNで定義されているWeakMapです。

WeakMapのキーは弱く保持されます。これが意味することは、キーへの他の強い参照がない場合、ガベージコレクタによって項目全体がWeakMapから削除されるということです。

アイデアは、クラス全体の静的マップとしてWeakMapを使用して、各インスタンスをキーとして保持し、プライベートデータをそのインスタンスキーの値として保持することです。

したがって、クラス内のみWeakMapコレクションにアクセスできます。

WeakMapWeakMapてエージェントに試してみましょう:

const topSecret = new WeakMap(); // will hold all private data of all instances.
export class SecretAgent{
    constructor(secret){
        topSecret.set(this,secret); // we use this, as the key, to set it on our instance private data
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret.get(this)); // we have access to topSecret
        };
    }
}

const topSecretはモジュールクロージャ内で定義されており、インスタンスプロパティにバインドしていないので、このアプローチは完全にプライベートであり、エージェントtopSecret到達することはできません。

コンストラクタ内のすべてのメソッドを定義する

ここでのアイデアは、コンストラクタ内のすべてのメソッドとメンバーを定義し、クロージャを使用してprivateメンバーにアクセスするthisです。

   export class SecretAgent{
        constructor(secret){
            const topSecret = secret;
            this.coverStory = 'just a simple gardner';
            this.doMission = () => {
                figureWhatToDo(topSecret); // we have access to topSecret
            };
        }
    }

この例でも、データは100%プライベートであり、クラス外には到達できないため、エージェントは安全です。

命名規則の使用

非公開の不動産の先頭に_が付くことになります。

このアプローチでは、データは実際にはプライベートではありません。

export class SecretAgent{
    constructor(secret){
        this._topSecret = secret; // it private by convention
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(this_topSecret); 
        };
    }
}

クラス名バインド

ClassDeclarationのNameは、異なるスコープで異なる方法でバインドされています。

  1. クラスが定義されているスコープ - letバインディング
  2. クラス自体のスコープ - class {} - constバインディングの{および}
class Foo {
  // Foo inside this block is a const binding
}
// Foo here is a let binding

例えば、

class A {
  foo() {
    A = null; // will throw at runtime as A inside the class is a `const` binding
  }
}
A = null; // will NOT throw as A here is a `let` binding

これは、関数 -

function A() {
  A = null; // works
}
A.prototype.foo = function foo() {
  A = null; // works
}
A = null; // works


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow