第25章類


クラスの導入

ES6にクラスが導入される前に、ES5においてコンストラクタおよびプロトタイプによって継承が達成され得る.
コンストラクション関数とクラスにはいくつかの違いがあります.
演算子を使用せずに
  • クラスを呼び出すと、エラーが発生します.ただし、演算子がない場合にコンストラクション関数を呼び出すと、通常の関数として呼び出されます.
  • クラスは、継承をサポートするnewおよびnewキーワードを提供する.
  • クラス内のコードのデフォルトはextendsです.

  • クラスは、コンストラクション関数に基づくオブジェクトの作成方法よりも堅牢で明瞭です.

    クラス反発


    クラスは関数で測定されます.
    クラス宣言として定義されたクラスは、関数宣言のクラスと同様に、実行時の前に評価を行い、関数オブジェクトを生成します.
    const Person = '';
    
    {
      // 호이스팅이 발생하지 않는다면 ''이 출력되야 한다.
      console.log(Person); // // ReferenceError: Cannot access 'Person' before initialization
      class Person {}
    }
    クラスはクラス定義の前に参照できません.
    console.log(Person); // ReferenceError: Cannot access 'Person' before initialization
    
    class Person {}

    インスタンス%


    定義方法

    class Person {
      constructor(name) {
        // 인스턴스 프로퍼티
        this.name = name;
      }
    }
    
    const me = new Person('Lee');
    console.log(me); // Person {name: "Lee"}
    インスタンス見積書は、superの内部でstrict modeに見積書を追加する必要があります.
    では、クラスの体に職業資格を宣言したらどうなるのでしょうか.
    class Person {
      // 인스턴스 프로퍼티 정의
      name = 'Lee'
    }
    
    const me = new Person();
    console.log(me); // Person {name: "Lee"}
    クラスマスターはメソッドのみを宣言できます.クラス・ボディで前述したようにインスタンス・プロシージャを宣言すると、構文エラーが発生します.
    ただし、上記の例は最新のブラウザまたはノードです.jsで実行すると、構文エラーは発生せず、正常に動作します.
    🔎 推奨定義クラスフィールド
    上記の定義はクラスフィールド定義と呼ばれ、クラスフィールドはクラスベースのオブジェクト向け言語でクラスが生成するインスタンスのpropertyを指す.
    JAvascriptも2021年1月にインスタンスプロセスをクラスフィールドとして定義することを提案した.これまでECMAScriptの標準にアップグレードされていませんが、標準仕様にアップグレードされているため、最新のブラウザと最新のノードです.jsは予めこの方式を実現している.
    最新のブラウザはChrome 72以降、最新のNodeです.jsはバージョン12以上を表す.
    🚸 クラスフィールドの定義方法に関する注意事項
    class Person {
      this.name = ''; // SyntaxError: Unexpected token '.'
    }
    constructorに番組をバインドしてはならない.thisはクラスのthisおよびメソッド内でのみ有効である.
    class Person {
      name;
    }
    
    const me = new Person();
    console.log(me); // Person {name: undefined}
    初期値が割り当てられていない場合は、定義されていない値があります.
    class Person {
      name;
      
      constructor(name) {
        this.name = name; // 초기화
      }
    }
    
    const me = new Person('Lee');
    console.log(me); // Person {name: "Lee"}
    外部初期値に初期化する必要がある場合は、thisで初期化する必要があります.
    class Person {
      constructor(name) {
        this.name = name;
      }
    }
    
    const me = new Person('Lee');
    console.log(me); // Person {name: "Lee"}
    実際には、インスタンスプロセスを初期化する必要がある場合、constructorの外で定義する必要はありません.いずれにしても、constructorの内部には、対応するプログラムを参照して初期値が割り当てられるからである.

    私有財産


    クラスは、constructorconstructorprivatepublicなどのアクセス制限子をサポートしません.ただし、最新のブラウザとノードを含むprotectedのプロパティを定義できる仕様があります.jsはすでに実現した.private位のうち、上位はprivateだった.#参照、privateも添付します.
    class Person {
      // private 프로퍼티 정의
      #name = '';
      
      constructor(name) {
        // private 프로퍼티 참조
        this.#name = name;
      }
    
      // getter 함수
      get name() {
        return this.#name;
      }
    }
    
    const me = new Person('Lee');
    console.log(me.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
    console.log(me.name);  // 'Lee'
    #のパーセントprivateはクラス外では参照できません.#name関数で間接的にアクセスする必要があります.
    class Person {
      constructor(name) {
        this.#name = name;
        // SyntaxError: Private field '#name' must be declared in an enclosing class
      }
    }
    さらに、getterパーセントはクラスで定義する必要があります.privateで直接定義するとエラーが発生します.

    静的構成


    クラスはconstructorキーワードを使用して静的メソッドを定義できますが、静的propertyは定義できません.しかし、最新のブラウザやNodeも含めて、現在も推奨されています.jsはすでに実現した.
    class MyMath {
      // static public 프로퍼티 정의
      static PI = 22 / 7;
    
      // static pirvate 프로퍼티 정의
      static #num = 10;
      
      // static 메서드
      static increment() {
        return ++MyMath.#num;
      }
    }
    
    console.log(MyMath.PI); // 3.142857142857143
    console.log(MyMath.increment()); // 11

    拡張クラス


    プロトコル・タイプ・ベースの継承は、プロトコル・タイプ・チェーンを介して他のオブジェクトの資産を継承する概念ですが、継承ベースのクラス拡張は、既存のクラスを継承することによって新しいクラスを拡張することによって定義されます.
    class Animal {
      constructor(age, weight) {
        this.age = age;
        this.weight = weight;
      }
      
      eat() { return 'eat'; }
      
      move() { return 'move'; }
    }
    
    class Bird extends Animal {
      fly() { return 'fly'; }
    }
    
    const bird = new Bird(1, 5);
    
    console.log(bird); // Bird {age: 1, weight: 5}
    console.log(bird instanceof Bird);   // true
    console.log(bird instanceof Animal); // true
    
    console.log(bird.eat());  // eat
    console.log(bird.move()); // move
    console.log(bird.fly());  // fly
    staticキーワードを使用したクラス拡張は、単純で直感的です.拡張を継承するクラスをサブクラス(subclass)と呼び、サブクラスに継承するクラスをスーパークラス(super-class)と呼ぶ.

    ダイナミック継承

    extendsキーワードは、クラスを継承するだけでなく、構造関数を継承してクラスを拡張することもできます.ただし、extendsキーワードの前にクラスが必要です.
    function Base(a) {
      this.a = a;
    }
    
    class Derived extends Base {}
    
    const derived = new Derived(1);
    console.log(derived); // Derived {a: 1}
    条件に基づいて継承オブジェクトを動的に決定することもできます.
    function Base1() {}
    
    class Base2 {}
    
    let condition = true;
    
    class Derived extends (condition ? Base1 : Base2) {}
    
    const derived = new Derived();
    console.log(derived); // Derived {}
    
    console.log(derived instanceof Base1); // true
    console.log(derived instanceof Base2); // false

    スーパーキー

    extendsキーワードは、関数のように呼び出されてもよいし、識別子のように参照されてもよい.

    1.スーパーコール


    呼び出しsuperは、スーパークラスのsuperを呼び出す.
    class Base {
      constructor(a, b) {
        this.a = a;
        this.b = b;
      }
    }
    
    class Derived extends Base {
      constructor(a, b, c) {
        super(a, b);
        this.c = c;
      }
    }
    
    const derived = new Derived(1, 2, 3);
    console.log(derived); // Derived {a: 1, b: 2, c:3}
    因数1,2,3はconstructorクラスのDerivedに渡され、constructorを呼び出すことによってsuperクラスのBaseに部分的に渡される.constructorを呼び出すときの注意点はいくつかあります.
    💡 サブクラスにsuperが省略されていない場合はsuperを呼び出す必要があります.
    class Base { ... }
    
    class Derived extends Base {
      constructor() {
        // ReferenceError
        console.log('constructor call'); 
      }
    }
    
    const derived = new Derived();
    💡 サブクラスのconstructorからconstructorを呼び出すまで、superは参照できません.
    class Base { ... }
    
    class Derived extends Base {
      constructor() {
        // ReferenceError
        this.a = 1;
        super();
      }
    }
    
    const derived = new Derived(1);
    💡 thisは、サブクラスのsuperで呼び出さなければならない.
    class Base {
      constructor() {
        super(); // SyntaxError: 'super' keyword unexpected here
      }
    }
    
    function Foo() {
      super(); // SyntaxError: 'super' keyword unexpected here
    }

    2.スーパーリファレンス


    メソッド内では、constructorを参照してスーパークラスメソッドを呼び出すことができる.
    class Base {
      constructor(name) {
        this.name = name;
      }
      
      sayHi(){
        return `Hi! ${this.name}`;
      }
    }
    
    class Derived extends Base {
      sayHi() {
        return `${super.sayHi()}. how are you doing?`;
      }
    }
    
    const derived = new Derived('Lee');
    console.log(derived.sayHi()); // Hi! Lee. how are you doing?
    サブクラスのプロトタイプ方法では、superはスーパークラスのプロトタイプ方法super.sayHiを指す.

    継承クラスのインスタンス作成プロセス

    class Rectangle {
      constructor(width, height) {
        this.width = width;
        this.height = height;
      }
    }
    
    class ColorRectangle extends Rectangle {
      constructor(width, height, color) {
        super(width, height);
        this.color = color;
      }
    }
    
    const colorRectangle = new ColorRectangle(2, 4, 'red');
    console.log(colorRectangle); // ColorRectangle {width: 2, height: 4, color: 'red'}

    📚 1.サブクラスのスーパーコール


    呼び出しsayHiは、サブクラスのコンストラクション関数で完了する必要があります.サブクラスは自分でインスタンスを作成するのではなく、スーパークラスにインスタンスの作成を委任するためです.
    サブクラスが新しい演算子とともに呼び出されると、サブクラスcounterchitor内部のsuperキーワードが関数のように呼び出され、スーパークラスのコンストラクション関数が呼び出されます.

    📚 2.スーパークラスインスタンスを作成し、そのインスタンスをバインドする

    // 수퍼클래스
    class Rectangle {
      constructor(width, height) {
        console.log(this); // ColorRectangle {}
     ...
    インスタンスはスーパークラスによって作成されますが、new演算子とともに呼び出されるクラスがサブクラスであることが重要です.superとは、new演算子とともに呼び出される関数のサブクラスを指す.したがって、インスタンスはサブクラスによって作成されたものとみなされます.

    📚 3.スーパークラスインスタンスの初期化

    class Rectangle {
      constructor(width, height) {
        console.log(this); // ColorRectangle {}
        
        this.width = width;
        this.height = height;
        
        console.log(this); // ColorRectangle {width: 2, height: 4}
      }
    ...
    スーパークラスのconstructorが実行され、thisにバインドされたインスタンスを初期化します.

    📚 4.サブクラス構築関数を返してバインド

    class ColorRectangle extends Rectangle {
      constructor(width, height, color) {
        super(width, height);
        
        console.log(this); // ColorRectangle {width: 2, height: 4}
    ...
    呼び出しthisは終了し、サブクラス構築関数はsuperが返すインスタンスをsuperにバインドする.サブクラスは、個別のインスタンスを作成するのではなく、thisから返されたインスタンスをsuperにバインドして使用を続行します.

    📚 5.サブクラスのインスタンスの初期化と戻し

    thisにバインドされたインスタンスにpropertyを追加し、パラメータ伝達の初期値としてコンストラクション関数を使用してインスタンスのpropertyを初期化します.
    クラスのすべての処理が完了すると、バインド完了インスタンスのthisがデフォルトで返されます.
    class ColorRectangle extends Rectangle {
      constructor(width, height, color) {
        super(width, height);
        
        console.log(this); // ColorRectangle {width: 2, height: 4}
        
        this.color = color;
        
        console.log(this); // ColorRectangle {width: 2, height: 4, color: 'red'}
      }
    }
    
    const colorRectangle = new ColorRectangle(2, 4, 'red');