ES 6 Classクラス
11594 ワード
ES 6(ES 2015)から、JSはクラス(Class)の概念を提出し、JSの中のクラスはJSの既存の、原型に基づく継承の一種の文法包装(文法糖)にすぎず、それは私たちに合理的で簡明な文法で継承を実現させることができる.
ES 6のクラスは実際には1つの関数であり、関数の定義方式には関数宣言と関数式の2つの方式があるように、クラスの定義にも2つの方式があり、それぞれ:クラス宣言 クラス式
クラス宣言はクラスを定義する方法で、classキーを使用してクラス名を付けると、クラスを定義できます.次のようになります.
クラス宣言と関数宣言の違いは、関数宣言には変数の昇格現象があり、クラス宣言はありません.すなわち、クラスは、
この規定の原因はクラスの継承に関係しており、子クラスが親クラスの後に定義されることを保証しなければならない.
上記のコードは、class
クラス式は、関数式のようにクラス名があってもなくてもよいクラスを定義する別の方法です.定義されたクラス名の場合、そのクラス名はクラスの内部のみにアクセスできます.
上記の方式2でクラスを定義するとともにクラス名が与えられるが,この場合,
クラス式を使用すると、すぐに実行されるClassを書くことができます.次のようになります.
クラスのメンバーは、1対のカッコ内の
クラス内のコードは、デフォルトの「use strict」である厳格なモードで強制的に実行されます.将来のすべてのコードがモジュール内で実行されることを考慮すると、ES 6は実際に言語全体を厳格なモードにアップグレードした.
サブクラスはconstructorメソッドで
上記のコードでは、
クラスのメソッドを定義する場合、メソッド名の前に
クラスのすべてのメソッドは、クラスの
したがって,クラスのインスタンスでメソッドを呼び出すことは,実際にはプロトタイプを呼び出すメソッドである.
上記のコードでは、
また,クラスの内部のすべての定義方法は,枚挙にいとまがない(non−enumerable).
静的メソッドはインスタンスに継承されず、クラス名によって直接呼び出されます.ただし、親の静的メソッドは、クラスに継承されます.
静的方法は、
ES 5と同様に、クラス内で
上記のコードでは、
上記のコードでは、メモリ値および値取り方法は、ES 5と一致する
クラスのメソッド名にアスタリスク(
上のコードでは、FooクラスのSymbol.iteratorメソッドの前にアスタリスクがあり、メソッドがGenerator関数であることを示します.Symbol.iteratorメソッドはFooクラスのデフォルトの遍歴器を返し、
クラスの定義
ES 6のクラスは実際には1つの関数であり、関数の定義方式には関数宣言と関数式の2つの方式があるように、クラスの定義にも2つの方式があり、それぞれ:
クラス宣言
クラス宣言はクラスを定義する方法で、classキーを使用してクラス名を付けると、クラスを定義できます.次のようになります.
class Foo {
constructor() {
// ..
}
}
変数リフトは存在しません(hoist)
クラス宣言と関数宣言の違いは、関数宣言には変数の昇格現象があり、クラス宣言はありません.すなわち、クラスは、
ReferenceError
の例外を投げ出す前に宣言してから使用する必要があります.var foo = new Foo(); // Uncaught ReferenceError: Foo is not defined(...)
class Foo {
// ...
}
この規定の原因はクラスの継承に関係しており、子クラスが親クラスの後に定義されることを保証しなければならない.
let Foo = class {};
class Bar extends Foo {
}
上記のコードは、class
Bar
がFoo
を継承する場合、Foo
が定義されているため、エラーは発生しません.ただし、Class昇格が存在する場合、上記のコードはエラーとなります.Class Bar
はコードヘッダに昇格し、式式Foo
は昇格しないため、Class Bar
がFoo
を継承する場合、Foo
はまだ定義されていません.クラス式
クラス式は、関数式のようにクラス名があってもなくてもよいクラスを定義する別の方法です.定義されたクラス名の場合、そのクラス名はクラスの内部のみにアクセスできます.
//
const MyClass = class {};
// :
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
上記の方式2でクラスを定義するとともにクラス名が与えられるが,この場合,
Me
クラス名はClassの内部コードでのみ利用可能であり,現在のクラスを指す.MyClassのname属性値は与えられたクラス名である.let my = new MyClass();
my.getClassName(); // Me
Me.name; // Uncaught ReferenceError: Me is not defined(…)
MyClass.name; // Me
クラス式を使用すると、すぐに実行されるClassを書くことができます.次のようになります.
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('Zhang San');
person.sayName(); // Zhang San
クラスとメソッド定義
クラスのメンバーは、1対のカッコ内の
{}
を定義する必要があります.カッコ内のコードのカッコ自体がクラス体を構成します.クラスメンバーには、クラスコンストラクタとクラスメソッド(静的メソッドとインスタンスメソッドを含む)が含まれます.厳格モード
クラス内のコードは、デフォルトの「use strict」である厳格なモードで強制的に実行されます.将来のすべてのコードがモジュール内で実行されることを考慮すると、ES 6は実際に言語全体を厳格なモードにアップグレードした.
コンストラクタほうしき
constructor
メソッドは、静的メソッドでもインスタンスメソッドでもなく、インスタンス化時にのみ呼び出される特殊なクラスメソッドです.クラスにはconstructor
というメソッドしかありません.そうしないと、SyntaxError
異常が放出されます.constructor
メソッドが定義されていない場合、このメソッドはデフォルトで追加されます.すなわち、定義が表示されているかどうかにかかわらず、どのクラスにもconstructor
メソッドがあります.サブクラスはconstructorメソッドで
super
メソッドを呼び出す必要があります.そうしないと、インスタンスを新規作成するときにエラーが発生します.子クラスは独自のthis
オブジェクトを持たず、親クラスのthis
オブジェクトを継承して加工するため、super
メソッドを呼び出さなければ、子クラスはthis
オブジェクトを得ることができない.class Point {}
class ColorPoint extends Point {
constructor() {}
}
let cp = new ColorPoint(); // ReferenceError
上記のコードでは、
ColorPoint
は親Point
を継承していますが、その構造関数はsuper
メソッドを呼び出さず、新しいインスタンス・タイムズが間違っています.プロトタイプメソッド
クラスのメソッドを定義する場合、メソッド名の前に
function
のキーワードを付ける必要はありません.また、メソッド間をカンマで区切る必要はなく、加算するとエラーが表示されます.class Bar {
constructor() {}
doStuff() {}
toString() {}
toValue() {}
}
クラスのすべてのメソッドは、クラスの
prototype
プロパティに定義されています.上記の書き方は、次のようになります.Bar.prototype = {
doStuff() {},
toString() {},
toValue() {}
};
したがって,クラスのインスタンスでメソッドを呼び出すことは,実際にはプロトタイプを呼び出すメソッドである.
class B {}
let b = new B();
b.constructor === B.prototype.constructor; // true
上記のコードでは、
b
はBクラスの例であり、そのconstructor
メソッドはBクラスプロトタイプのconstructor
メソッドである.クラスのメソッドはいずれもprototype
に定義されているため、クラスの新しいメソッドはprototype
オブジェクトに追加できます.Object.assign
メソッドは、クラスに複数のメソッドを一度に追加するのに便利である.class Point {
constructor() {
// ...
}
}
Object.assign(Point.prototype, {
toString() {},
toValue() {}
});
また,クラスの内部のすべての定義方法は,枚挙にいとまがない(non−enumerable).
class Point {
constructor(x, y) {
// ...
}
toString() {
return '(' + x + ', ' + y + ')';
}
}
Object.keys(Point.prototype); // []
Object.getOwnPropertyNames(Point.prototype); // ["constructor", "toString"]
Object.getOwnPropertyDescriptor(Point, 'toString');
// Object {writable: true, enumerable: false, configurable: true}
スタティツクメソッド
static
キーワードは、クラスの静的メソッドを定義するために使用されます.静的メソッドとは、クラスをインスタンス化する必要がなく、クラス名を使用して直接アクセスできるメソッドです.静的メソッドは、ツール関数としてよく使用されます.class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx*dx + dy*dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));
静的メソッドはインスタンスに継承されず、クラス名によって直接呼び出されます.ただし、親の静的メソッドは、クラスに継承されます.
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod(); // "hello"
静的方法は、
super
キーワードで呼び出すこともできる.class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod(); // "hello too"
extendsキーワード
extends
キーワードは、クラス間の継承を実現するために使用される.子が親を継承すると、親のすべての属性とメソッドが継承されます.extends
の後には1つの親しか付いていません.superキーワード
super
キーワードは、その親のコンストラクタまたはメソッドを呼び出すために使用することができる.class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(this.name + ' roars.');
}
}
クラスのGetterとSetterメソッド
ES 5と同様に、クラス内で
get
およびset
のキーワードを使用して、ある属性に値の取り方および付与方法を設定することができる.class Foo {
constructor() {}
get prop() {
return 'getter';
}
set prop(val) {
console.log('setter: ' + val);
}
}
let foo = new Foo();
foo.prop = 1;
// setter: 1
foo.prop;
// "getter"
上記のコードでは、
prop
属性に対応する付与方法と取値方法があるため、付与動作と読み出し動作がカスタマイズされている.値の格納方法と値の取得方法は、プロパティのdescriptorオブジェクトに設定されます.var descriptor = Object.getOwnPropertyDescriptor(Foo.prototype, 'prop');
"get" in descriptor // true
"set" in descriptor // true
上記のコードでは、メモリ値および値取り方法は、ES 5と一致する
prop
属性の記述オブジェクトに定義されている.クラスのGeneratorメソッド
クラスのメソッド名にアスタリスク(
*
)が付けられている場合、このメソッドはGenerator関数であることを示します.class Foo {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo('hello', 'world')) {
console.log(x);
}
// hello
// world
上のコードでは、FooクラスのSymbol.iteratorメソッドの前にアスタリスクがあり、メソッドがGenerator関数であることを示します.Symbol.iteratorメソッドはFooクラスのデフォルトの遍歴器を返し、
for...of
サイクルでこの遍歴器が自動的に呼び出されます.