ES 6クラスベース

8942 ワード

概要


ES 6のclassはただ1つの文法糖で、完全に構造関数の別の書き方と見なすことができて、ES 5の構造関数はES 6の構造方法に対応します
class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

コンストラクション関数のprototypeプロパティは、ES 6の「クラス」の上に引き続き存在します.実際、クラスのすべてのメソッドはクラスのprototypeプロパティに定義されています.
class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}

//  

Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

クラスのメソッドはprototypeオブジェクトの上に定義ため、クラスの新しいメソッドはprototypeオブジェクトの上に追加することができ、Object.assignメソッドは、クラスに複数のメソッドを一度に追加するのに便利です.
class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});

prototypeオブジェクトのconstructorプロパティは、ES 5の動作と一致する「クラス」そのものを直接指します.
Point.prototype.constructor === Point // true

クラスの内部のすべての定義方法は,ES 5の挙動と一致しない枚挙にいとまがない.
class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

クラスのプロパティ名、式を使用できます
/*
   , 
  Square getArea, 
*/
let methodName = 'getArea';
class Square {
  constructor(length) {
    // ...
  }
  [methodName]() {
    // ...
  }
}
console.log(Square.prototype)

厳格モード


クラスとモジュールの内部は、デフォルトでは厳格なモードなので、use strictを使用して実行モードを指定する必要はありません.クラスまたはモジュールにコードが書かれている限り、厳格なモードしか使用できません.

constructorメソッド

  • constructorメソッドはクラスのデフォルトメソッドであり、newコマンドでオブジェクトインスタンスを生成すると自動的に呼び出されます.クラスにはconstructorメソッドが必要です.明示的に定義されていない場合、空のconstructorメソッドはデフォルトで
  • 追加されます.
  • constructorメソッドは、デフォルトではインスタンスオブジェクト(this)を返し、別のオブジェクト
  • を返すことを完全に指定できます.
    class Foo {
      constructor() {
        return Object.create(null);
      }
    }
    
    new Foo() instanceof Foo

    クラスはnew呼び出しを使用する必要があります.そうしないと、エラーが発生します.これは通常の構造関数との主な違いであり、後者はnewを使わずに実行することができる.
    class Foo {
      constructor() {
        return Object.create(null);
      }
    }
    
    Foo()
    // TypeError: Class constructor Foo cannot be invoked without 'new'

    クラスのインスタンスオブジェクト


    インスタンスのプロパティは、明示的に定義されていない限り(thisオブジェクトに定義されている)、プロトタイプに定義されています(classに定義されています).
    // 
    class Point {
    
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    
      toString() {
        return '(' + this.x + ', ' + this.y + ')';
      }
    
    }
    
    var point = new Point(2, 3);
    
    point.toString() // (2, 3)
    
    point.hasOwnProperty('x') // true
    point.hasOwnProperty('y') // true
    point.hasOwnProperty('toString') // false
    point.__proto__.hasOwnProperty('toString') // true

    上記のコードでは、xとyはインスタンスオブジェクトpoint自身の属性(this変数に定義されているため)であるため、hasOwnPropertyメソッドはtrueを返し、toStringはPointクラスに定義されているため、hasOwnPropertyメソッドはfalseを返し、これらはES 5の動作と一致している
    protoは言語そのものの特性ではなく、これは各メーカーが具体的に実装する際に追加したプライベート属性であり、現在多くの現代ブラウザのJSエンジンでこのプライベート属性が提供されているが、生産でこの属性を使用することを提案せず、環境への依存を避ける.生産環境ではObjectを使用できます.getPrototypeOfメソッドは、インスタンスオブジェクトのプロトタイプを取得し、プロトタイプにメソッド/プロパティを追加します.
    var p1 = new Point(2,3);
    var p2 = new Point(3,2);
    
    p1.__proto__.printName = function () { return 'Oops' };
    
    p1.printName() // "Oops"
    p2.printName() // "Oops"
    
    var p3 = new Point(4,2);
    p3.printName() // "Oops"

    使用例の__proto__プロパティはプロトタイプを書き換えるには、クラスの元の定義を変更し、すべてのインスタンスに影響を与えるため、使用を推奨しない必要があります.

    Class式


    関数と同様に、クラスは式の形式で定義することもできます.
    const MyClass = class Me {
      getClassName() {
        return Me.name;
      }
    };

    上記のコードは、式を使用してクラスを定義します.なお、このクラスの名前はMeではなくMyClassであり、MeはClassの内部コードのみで使用でき、現在のクラスを指す
    let inst = new MyClass();
    inst.getClassName() // Me
    Me.name // ReferenceError: Me is not defined

    変数リフトは存在しません


    クラスに変数のアップグレードは存在しません.これはES 5とは全く違います.
    new Foo(); // ReferenceError
    class Foo {}

    上のコードでは、Fooクラスは前に使用され、後に定義されています.ES 6はクラスの宣言をコードヘッダに昇格させないため、エラーが発生します.

    プライベートメソッド


    1つの方法は、モジュール内部のすべての方法が外部に見られるため、プライベートメソッドをモジュールからいっそ削除することです.
    class Widget {
      foo (baz) {
        bar.call(this, baz);
      }
    
      // ...
    }
    
    function bar(baz) {
      return this.snaf = baz;
    }

    上のコードではfooは共通の方法で、内部でbarが呼び出されています.call(this, baz).これによりbarは実際には現在のモジュールのプライベートメソッドとなっている.
    もう1つの方法は、Symbol値の一意性を利用して、プライベートメソッドの名前をSymbol値と命名することです.
    const bar = Symbol('bar');
    const snaf = Symbol('snaf');
    
    export default class myClass{
    
      //  
      foo(baz) {
        this[bar](baz);
      }
    
      //  
      [bar](baz) {
        return this[snaf] = baz;
      }
    
      // ...
    };

    上記のコードではbarとsnafがSymbol値であり、サードパーティが取得できないため、プライベートメソッドとプライベートプロパティの効果が得られます.

    プライベート属性


    プライベートメソッドと同様に、ES 6はプライベート属性をサポートしません.現在、classにプライベート属性を追加する提案があります.メソッドは、属性名の前に#を使用して表示します.

    nameプロパティ


    本質的には、ES 6のクラスはES 5の構造関数のパッケージにすぎないため、関数の多くの特性はClassによって継承され、name属性が含まれ、name属性は常にclassキーワードの後ろに続くクラス名を返す.
    class Point {}
    Point.name // "Point"

    Classの値取り関数(getter)と保存関数(setter)

  • ES 5と同様に、「クラス」の内部でgetキーとsetキーを使用して、ある属性に対して値関数と値関数を設定し、その属性のアクセス動作
  • をブロックすることができる.
  • メモリ関数と取値関数は、属性のDescriptorオブジェクトに設定された
  • である.
    class CustomHTMLElement {
      constructor(element) {
        this.element = element;
      }
    
      get html() {
        return this.element.innerHTML;
      }
    
      set html(value) {
        this.element.innerHTML = value;
      }
    }
    
    var descriptor = Object.getOwnPropertyDescriptor(
      CustomHTMLElement.prototype, "html"
    );
    
    "get" in descriptor  // true
    "set" in descriptor  // true

    Classの静的アプローチ


    クラスはインスタンスのプロトタイプに相当し、クラスで定義されたすべてのメソッドはインスタンスによって継承されます.1つのメソッドの前にstaticキーを付けると、そのメソッドはインスタンスに継承されず、クラスによって直接呼び出されることを示します.これを「静的メソッド」と呼びます.
    class Foo {
      static classMethod() {
        return 'hello';
      }
    }
    
    Foo.classMethod() // 'hello'
    
    var foo = new Foo();
    foo.classMethod()
    // TypeError: foo.classMethod is not a function

    上記のコードでは、FooクラスのclassMethodメソッドの前にstaticキーワードがあり、このメソッドがFooクラスのインスタンスではなく、Fooクラスで直接呼び出すことができる静的メソッドであることを示しています.インスタンスで静的メソッドを呼び出すと、メソッドが存在しないことを示すエラーが投げ出されます.
    静的メソッドにthisキーワードが含まれている場合、このthisはインスタンスではなくクラスを指します.
    class Foo {
      static bar () {
        this.baz();
      }
      static baz () {
        console.log('hello');
      }
      baz () {
        console.log('world');
      }
    }
    
    Foo.bar() // hello

    上記のコードでは、静的メソッドbarがthisを呼び出す.baz、ここでthisはFooのインスタンスではなくFooクラスを指し、Fooを呼び出すのと同じである.baz.また,この例から,静的手法は非静的手法と再名できることも分かる.
    静的メソッドもsuperオブジェクトから呼び出すことができます.
    class Foo {
      static classMethod() {
        return 'hello';
      }
    }
    
    class Bar extends Foo {
      static classMethod() {
        return super.classMethod() + ', too';
      }
    }
    
    Bar.classMethod() // "hello, too"

    class Foo { static classMethod() {
    return 'hello';

    }}
    class Bar extends Foo { static classMethod() {
    return super.classMethod() + ', too';

    }}
    Bar.classMethod()//"hello, too"

    Classの静的属性


    静的属性とは、Class自体の属性であるClassを指す.インスタンスオブジェクト(this)に定義された属性ではなくpropName
    class Foo {
    }
    
    Foo.prop = 1;
    Foo.prop // 1
  • 上の記述は、Fooクラスの静的属性prop
  • を定義する.
  • 現在、ES 6が明確に規定しているため、Class内部には静的方法しかなく、静的属性
  • がない.

    new.targetプロパティ


    newは、コンストラクション関数からインスタンスオブジェクトを生成するコマンドです.ES 6はnewコマンドにnewを導入する.targetプロパティ.このプロパティは一般的にコンストラクション関数で使用され、newコマンドが作用するコンストラクション関数を返します.コンストラクション関数がnewコマンドで呼び出されない場合、new.targetはundefinedを返します.したがって、このプロパティは、コンストラクション関数がどのように呼び出されるかを決定するために使用できます.
    function Person(name) {
      if (new.target !== undefined) {
        this.name = name;
      } else {
        throw new Error('  new  ');
      }
    }
    
    //  
    function Person(name) {
      if (new.target === Person) {
        this.name = name;
      } else {
        throw new Error('  new  ');
      }
    }
    
    var person = new Person(' '); //  
    var notAPerson = Person.call(person, ' ');  //  

    子クラスが親クラスを継承する場合new.targetはサブクラスを返します
    class Rectangle {
      constructor(length, width) {
        console.log(new.target === Rectangle);
        // ...
      }
    }
    
    class Square extends Rectangle {
      constructor(length) {
        super(length, length);
      }
    }
    
    var obj = new Square(3); //   false

    この特徴を利用して,独立して使用できない,継承しなければ使用できないクラス(抽象クラス)を書くことができる.
    class Shape {
      constructor() {
        if (new.target === Shape) {
          throw new Error(' ');
        }
      }
    }
    
    class Rectangle extends Shape {
      constructor(length, width) {
        super();
        // ...
      }
    }
    
    var x = new Shape();  //  
    var y = new Rectangle(3, 4);  //  
  • 上のコードでは、Shapeクラスはインスタンス化できず、
  • を継承するためにのみ使用できます.
  • 関数の外部ではnewが使用することに注意する.targetは
  • を間違えます