序章

JavaScriptはプロトタイプベースの言語であり、JavaScriptのすべてのオブジェクトには、次のような非表示の内部プロパティがあります。 [[Prototype]] オブジェクトのプロパティとメソッドを拡張するために使用できます。 プロトタイプの詳細については、JavaScriptでのプロトタイプと継承の理解チュートリアルをご覧ください。

最近まで、勤勉な開発者はコンストラクター関数を使用して、JavaScriptのオブジェクト指向デザインパターンを模倣していました。 言語仕様ECMAScript2015は、ES6と呼ばれることが多く、JavaScript言語にクラスを導入しました。 JavaScriptのクラスは実際には追加機能を提供せず、よりクリーンでエレガントな構文を提供するという点で、プロトタイプや継承よりも「シンタックスシュガー」を提供すると説明されることがよくあります。 他のプログラミング言語はクラスを使用するため、JavaScriptのクラス構文により、開発者は言語間を簡単に移動できます。

クラスは関数です

JavaScriptクラスは一種の関数です。 クラスはで宣言されます class キーワード。 関数式構文を使用して関数を初期化し、クラス式構文を使用してクラスを初期化します。

// Initializing a function with a function expression
const x = function() {}
// Initializing a class with a class expression
const y = class {}

アクセスできます [[Prototype]] Object.getPrototypeOf()メソッドを使用したオブジェクトの作成。 これを使用して、作成した空の関数をテストしてみましょう。

Object.getPrototypeOf(x);
Output
ƒ () { [native code] }

作成したクラスでもそのメソッドを使用できます。

Object.getPrototypeOf(y);
Output
ƒ () { [native code] }

で宣言されたコード functionclass 両方とも関数を返します [[Prototype]]. プロトタイプを使用すると、任意の関数を使用してコンストラクターインスタンスにすることができます。 new キーワード。

const x = function() {}

// Initialize a constructor from a function
const constructorFromFunction = new x();

console.log(constructorFromFunction);
Output
x {} constructor: ƒ ()

これはクラスにも当てはまります。

const y = class {}

// Initialize a constructor from a class
const constructorFromClass = new y();

console.log(constructorFromClass);
Output
y {} constructor: class

これらのプロトタイプコンストラクターの例は、それ以外は空ですが、構文の下で、両方のメソッドが同じ最終結果を達成していることがわかります。

クラスの定義

プロトタイプと継承のチュートリアルでは、テキストベースのロールプレイングゲームでのキャラクター作成に基づいた例を作成しました。 ここでその例を続けて、構文を関数からクラスに更新しましょう。

コンストラクター関数は、次のプロパティとして割り当てられるいくつかのパラメーターで初期化されます。 this、関数自体を参照します。 識別子の最初の文字は、慣例により大文字になります。

コンストラクター.js
// Initializing a constructor function
function Hero(name, level) {
	this.name = name;
	this.level = level;
}

これを以下に示すclass構文に変換すると、非常によく似た構造になっていることがわかります。

class.js
// Initializing a class definition
class Hero {
	constructor(name, level) {
		this.name = name;
		this.level = level;
	}
}

コンストラクター関数は、初期化子の最初の文字(オプション)を大文字にし、構文に精通していることにより、オブジェクトの青写真になることを意味します。 The class キーワードは、関数の目的をより簡単に伝えます。

初期化の構文の唯一の違いは、 class 代わりにキーワード function、および内部のプロパティの割り当て constructor() 方法。

メソッドの定義

コンストラクター関数の一般的な方法は、メソッドを直接に割り当てることです。 prototype に見られるように、初期化ではなく greet() 以下の方法。

コンストラクター.js
function Hero(name, level) {
	this.name = name;
	this.level = level;
}

// Adding a method to the constructor
Hero.prototype.greet = function() {
	return `${this.name} says hello.`;
}

クラスを使用すると、この構文が簡略化され、メソッドをクラスに直接追加できます。 ES6で導入されたメソッド定義の省略形を使用すると、メソッドの定義はさらに簡潔なプロセスになります。

class.js
class Hero {
	constructor(name, level) {
		this.name = name;
		this.level = level;
	}

	// Adding a method to the constructor
	greet() {
		return `${this.name} says hello.`;
    }
}

これらのプロパティとメソッドの動作を見てみましょう。 の新しいインスタンスを作成します Hero を使用して new キーワード、およびいくつかの値を割り当てます。

const hero1 = new Hero('Varg', 1);

新しいオブジェクトに関する詳細情報を次のように印刷すると console.log(hero1)、クラスの初期化で何が起こっているかについての詳細を見ることができます。

Output
Hero {name: "Varg", level: 1} __proto__: ▶ constructor: class Hero ▶ greet: ƒ greet()

出力で、 constructor()greet() 関数がに適用されました __proto__、 また [[Prototype]]hero1、および直接の方法としてではありません hero1 物体。 これはコンストラクター関数を作成するときは明らかですが、クラスを作成するときは明らかではありません。 クラスを使用すると、より単純で簡潔な構文が可能になりますが、プロセスの明確さがいくらか犠牲になります。

クラスの拡張

コンストラクター関数とクラスの利点は、親に基づいて新しいオブジェクトブループリントに拡張できることです。 これにより、類似しているが追加またはより具体的な機能が必要なオブジェクトのコードが繰り返されるのを防ぎます。

call()メソッドを使用して、親から新しいコンストラクター関数を作成できます。 以下の例では、というより具体的な文字クラスを作成します Mage、およびのプロパティを割り当てます Hero を使用してそれに call()、および追加のプロパティを追加します。

コンストラクター.js
// Creating a new constructor from the parent
function Mage(name, level, spell) {
	// Chain constructor with call
	Hero.call(this, name, level);

	this.spell = spell;
}

この時点で、次の新しいインスタンスを作成できます。 Mage と同じプロパティを使用する Hero また、新しいものを追加しました。

const hero2 = new Mage('Lejon', 2, 'Magic Missile');

送信 hero2 コンソールに、新しいものを作成したことがわかります Mage コンストラクターに基づいています。

Output
Mage {name: "Lejon", level: 2, spell: "Magic Missile"} __proto__: ▶ constructor: ƒ Mage(name, level, spell)

ES6クラスでは、superキーワードが代わりに使用されます call 親関数にアクセスします。 我々は使用するだろう extends 親クラスを参照します。

class.js
// Creating a new class from the parent
class Mage extends Hero {
	constructor(name, level, spell) {
		// Chain constructor with super
		super(name, level);

		// Add a new property
		this.spell = spell;
	}
}

これで、新しいものを作成できます Mage 同じ方法でインスタンス。

const hero2 = new Mage('Lejon', 2, 'Magic Missile');

印刷します hero2 コンソールに移動し、出力を表示します。

Output
Mage {name: "Lejon", level: 2, spell: "Magic Missile"} __proto__: Hero ▶ constructor: class Mage

クラス構築では、出力はほぼ同じですが、 [[Prototype]] この場合、親にリンクされています Hero.

以下は、初期化、メソッドの追加、およびコンストラクター関数とクラスの継承のプロセス全体を並べて比較したものです。

コンストラクター.js
function Hero(name, level) {
	this.name = name;
	this.level = level;
}

// Adding a method to the constructor
Hero.prototype.greet = function() {
	return `${this.name} says hello.`;
}

// Creating a new constructor from the parent
function Mage(name, level, spell) {
	// Chain constructor with call
	Hero.call(this, name, level);

	this.spell = spell;
}
class.js
// Initializing a class
class Hero {
	constructor(name, level) {
		this.name = name;
		this.level = level;
	}

	// Adding a method to the constructor
	greet() {
		return `${this.name} says hello.`;
    }
}

// Creating a new class from the parent
class Mage extends Hero {
	constructor(name, level, spell) {
		// Chain constructor with super
		super(name, level);

		// Add a new property
		this.spell = spell;
	}
}

構文はまったく異なりますが、基本的な結果は両方のメソッド間でほぼ同じです。 クラスは、オブジェクトブループリントを作成するためのより簡潔な方法を提供し、コンストラクター関数は、内部で何が起こっているかをより正確に記述します。

結論

このチュートリアルでは、JavaScriptコンストラクター関数とES6クラスの類似点と相違点について学びました。 クラスとコンストラクターはどちらも、オブジェクト指向の継承モデルを、プロトタイプベースの継承言語であるJavaScriptに模倣します。

プロトタイプの継承を理解することは、効果的なJavaScript開発者であるために最も重要です。 React などの一般的なJavaScriptライブラリは、クラスを頻繁に使用するため、クラスに精通していると非常に役立ちます。 class 構文。