備忘録的プログラミングリファレンス

クラス class

 オブジェクト指向プログラミング言語においてクラスとはオブジェクトを生成するための設計図みたいなものです。クラスを元にそっくりなオブジェクトデータが生成されます。

クラスの定義
class Class_City {
	constructor( name, population ) {	// constructorの定義
		this.name = name;	// プロパティの定義
		this.population = population;
	}

	get_Name() {			// methodの定義
		return this.name;
	}

	example_Method() {			// methodの定義
		...
		return this.name;
	}
}
...
let city_tok = new Class_City('tokyo', 1396); // インスタンスの生成

 クラスから生成されるのはインスタンスといいますが、JavaScript ではオブジェクトという記載が多いです。
 すべてをオブジェクトで解説することによる混乱を防ぐためにクラスから生成されるのはインスタンスと解説します。

 ここでは主にクラス( class )について解説します。オブジェクトについてはオブジェクトまたは連想配列型( Object )ページを参照してください。

ページ内 Index

クラスについて

 JavaScript におけるクラス定義とそのインスタンスの生成についてです。

 オブジェクトは、それ自身固有のデータ群と関数群を持つデータという抽象(イメージ)です。

 オブジェクト型のデータ構成を定義することをクラスの定義といい、クラスからオブジェクト型のデータを宣言(メモリを確保)することを、インスタンスを生成または作成するといいます。

Object 型の変数宣言
let example_object = {
	name:"sam",
	age:10s
};

 上記は、オブジェクト型(連想配列型)の変数を1つだけ宣言する例です。ここではクラス( class )を使用して同じようなデータ構成のオブジェクトデータを生成する方法を解説します。

クラス定義について

 クラス定義では以下のようにインスタンスのプロパティ( property )とメソッド( method )を定義します。プロパティはインスタンスのデータのことで、メソッドは関数です。

クラス定義
class Class_City {
	constructor( name, population ) {	// constructorの定義
		this.name = name;	// property の定義
		this.population = population;
	}

	get_Name() {			// methodの定義
		return this.name;
	}

	example_Method() {		// methodの定義
		...
		return this.name;
	}
}

 上記の例には、コンストラクター( constructor )があります。これはクラスを使ってインスタンスを生成するときに必ず呼び出される部分です。
 constructor() はインスタンス生成時に呼び出されるためにプロパティの定義とプロパティの初期化に利用することができます。

インスタンスの生成

 上記のクラス定義からインスタンスを生成するには以下のようにします。

インスタンスの生成
...
let city_tokyo = new Class_City('tokyo', 1396); // クラスを使った変数宣言
/*
生成されたインスタンスは
city_tokyo = {
	name : 'tokyo',
	population : 1396
};
となる
*/

 プログラム上では Object 型のリテラルを代入する変数を宣言することで、インスタンスが生成、作成されます。 コンピュータ上では、変数宣言によってメモリが必要なだけ確保された状態です。

 変数宣言には、var、let、const ステートメントがあります。変数として宣言するには、letを。同じ変数名が出現しないようにするには const を使います。var は間違いが起こりやすいので使うのは控えたほうがよいようです。

プロパティ( property)とメソッド(method)の使用

 以下のようにインスタンスのプロパティ( property)やメソッド(method)を使用することができます。

プロパティやメソッドの使用
console.log(city_tokyo.name);			// cityオブジェクトのプロパティnameを出力
console.log(city_tokyo.population);	// cityオブジェクトのプロパティpopulationを出力

console.log( city_tokyo.get_Name() );	// インスタンスのmethodを実行しプロパティの値を表示。実行結果は'tokyo'

JavaScript と HTML

 JavaScript はブラウザで稼働するスクリプト言語です。HTML で使用することを前提にしています。

 HTML のタグエレメントも DOM という機能を通してオブジェクトとして扱います。
 HTML は、独自にクラス定義から行わなくても、タグエレメント自体をオブジェクトとして利用すればよいケースが多々あります。継承を使うことや、複数のタグエレメントのハンドラーをもつ任意のクラス定義もあるでしょう。さらに、関数に関しても便利な関数が用意されている場合がありますので、それを利用するとよいでしょう。(DOMやビルトインオブジェクトなど)

P タグエレメントの取得
let obj_p = document.getElementsByTagName('p');	// P タグエレメントを取得
alert( obj_p[0] );				// オブジェクトとして扱われている
alert( obj_p[0].innerHTML );	// タグエレメントのプロパティ

 JavaScript では HTML タグエレメントをオブジェクト(DOM)として扱います。DOM について詳しくはDOMページを参照してください。

プロパティとメソッド

 クラスにはプロパティとメソッドを定義することができます。
 プロパティはオブジェクトを表す変数で、メソッドは主に変数を操作するための関数です。

 以下の例はクラス宣言をして、そのクラスを元にオブジェクトの配列を作成しています。

以下の確認ボタンをクリックして、開発用コンソールで実行結果を確認してみてください。

 クラスから生成されたオブジェクトで、name プロパティや get_Name() メソッドが機能します。

プロパティについて

 プロパティは変数名(識別子)とその値の組み合わせです。キーと値と言ったりもします。プロパティの順番は決まっていません。

 プロパティは以下のようにオブジェクトの後に.または[ ]を使って指定します。

プロパティの値を取得
console.log( obj_student.name );
// または
console.log( obj_student['name'] );

 このことは連想配列に準じています。

 プロパティにはプロパティ自体を操作するための Object.defineProperty()Object.getOwnPropertyDescriptor() といったメソッドがあります。
 ディスクリプタの操作によって、プロパティの変更、列挙型、削除といった既定の性質を変更することができます。

プロトタイプ

 プロトタイプはクラスを宣言するとクラス以下に自動で作成されます。

以下の確認ボタンをクリックして、開発用コンソールで実行結果を確認してみてください。

 クラスを元にインスタンスを生成するとそのプロパティやメソッドがコピーされ、インスタンスごとに独立しているようにみえます。
 実際にはクラスのプロトタイプを経由しながら各インスタンスのプロパティの参照やメソッドの実行が行われます。
 そのために、this が参照しているインスタンスが想定と違うといった現象が起きます。

 クラスやインスタンスは元になるクラスを継承しています。その継承はプロトタイプによって繋がっています。このことをプロトタイプチェーンといいます。

 プロトタイプの存在は意識する必要がないですが、エラーが起きたい場合にその解決の糸口になるかもしれません。

static(静的)メソッド

 JavaScript で静的メソッドが利用できるようになっています。
 静的メソッドはインスタンスの生成なしに利用することができるメソッドです。

 例えば、以下のような Object クラスの keys() メソッドが静的メソッドです。

静的メソッド
let array_keys = Object.keys( obj_example );

 keys() メソッドは Object クラスに定義されていますが、クラス名に続いて .keys() で呼び出すことができます。

 静的メソッドはstaticステートメントで定義されたメソッドです。

class Class_City {
	static get_Population( obj_ ){
		return( obj_.population );
	}

	constructor( name, population ) {	// constructorの定義
		this.name = name;	// property の定義
		this.population = population;
	}

	get_Name() {			// methodの定義
		return this.name;
	}
}

 上記の例のように static に続いて定義されたメソッドが静的メソッドして機能します。
 static はこのクラス内だけで機能するという意味です。このような静的メソッドの定義は、インスタンスから呼び出すことができないという特性があります。

Example

 例えば、クラスによって複数のインスタンスを作成するのですが ID といったインスタンスごとの一意のデータが必要になることがあります。
 そのような場合に ID のためのシーケンスをクラスに記憶させておいて静的メソッドで ID を作成することができます。

以下の確認ボタンをクリックして、開発用コンソールで実行結果を確認してみてください。

 クラス自体に変数を宣言してデータを記憶させておくことができます。
 そして、クラス定義の外でシーケンスを初期化(例えば、Class_City.sequence_id = 0;)をする必要がありますが、クラスがデータを記憶しています。

get ステートメントと set ステートメント

 JavaScript にはあらかじめ、プロパティ値の取得と設定ができるメソッドとして定義できる get と set という特殊なステートメントが用意されています。これらはアクセッサプロパティと呼ばれます。
  get ステートメントはプロパティ値を取得し、 set ステートメントは値を設定します。これらのステートメントはメソッドの前に付けることで、そのメソッドは引数を指定しなくてもデータの取得と設定を専用に行うことができます。

以下の確認ボタンをクリックして、開発用コンソールで実行結果を確認してみてください。

 get と set は上記のように実際のプロパティを隠蔽することができます。
 JavaScript ではプロパティを直接変更することができるという欠点をもっています。これを補うために、get や set を使用してメソッドをプロパティのように扱い、実際のプロパティ名を隠蔽します。
 上記の例のようにプロパティには_populationのような別名にし、get や set ではpopulationという想像しやすい名称にします。
 このことでユーザーはcities[0].populationというように実際の変数名をユーザーから隠蔽することができます。

 get のみを定義し set を定義しないこともできます。そのことで読み込むことができても書き込むことができないといった使い方ができます。

get のみ
class Class_City {
	constructor( name, population ) {	// constructorの定義
		this._name = name;	// property の定義
		this._population = population;
	}

	get population() {			// get メソッドの定義
		return this._population;
	}
}

const tokyo_city = new Class_City( 'Tokyo', 1396 );

console.log( tokyo_city.population );
// tokyo_city.population = 100 といった代入はできない

 上記のように get のみを定義することで値を取得するのみで代入を防ぐことができます。

 アクセッサプロパティは特殊で、上記の例のように get、 set は関数として定義をするのですが、オブジェクトのプロパティを扱うObject 名.Property 名という文で値の取得と代入ができます。

クラスの継承(extends)

 JavaScriptにおいてクラスの継承は、extendsステートメントを使います。

クラスの継承
class Example_Class {
	constructor( name, population ) {	// constructorの定義
		this._name = name;	// property の定義
		this._population = population;
	}
}

class Extends_Class extends Example_Class {
	get_CityName_Population(){
		alert( this._name + "の人口は" + this._population + "万人です。" );
	}
}

 継承とは、あるクラスを元に機能を追加したクラスを定義する方法です。
 上記の例では、Example_Class では都市名とその人口を記憶するためのものですが、そのクラスを継承した Extends_Class によって.. の人口は .. 万人です。というコメントを出力することができます。
 このように継承によってより充実したクラスや元のクラスを変更しないで新たな似たクラスを定義することができます。

super (コンストラクターの継承)

 extends ステートメントでクラスが継承できるのですが、コンストラクターは super ステートメントを使って継承しないと上書きされてしまいます。
 そのために継承クラスで super を利用しないでコンストラクターを定義してしまうと、親クラスのパラメーターが参照できずに method などが使えない場合が発生します。

以下の確認ボタンをクリックして、開発用コンソールで実行結果を確認してみてください。

 super() は継承元の親クラスのコンストラクターを呼び出すものです。そのため、引数は親のコンストラクターの引数に合わせます。

インターフェイス、ミックスイン

 クラスの継承は1対1の関係であることが好ましいです。多重継承はプロパティやメソッドが多重に衝突する危険性を含みます。そのために1つの親からは1つの子しか存在しないということの方が好ましいのです。
 実際には、1つの親を元に複数のクラスを定義する、または複数の親クラスから継承するしたほうが都合がいい場合があります。それを可能にするのがインターフェイス、ミックスインという機能です。

 しかしながら、JavaScript ではインターフェイスという構文はありません。クラスの継承を利用します。

インターフェイス

 インターフェイスとは抽象クラスとも呼ばれ、クラスによって継承されることを前提としたクラスまたインスタンスの生成の元になるクラスのことです。

 JavaScript におけるインターフェイスとは、1つの親クラスから複数の子クラスが継承する方法です。

 以下の例は地域ごとの名称と人口を記録するクラスです。このクラスを元に都道府県ごとの名称と人口を記憶するクラス、市町村ごとの名称と人口を記憶するクラスを定義します。

1つのクラスを複数のクラスで継承(インターフェイス)
class Area {
	constructor( name, population ) {	// constructorの定義
		this._name = name;	// property の定義
		this._population = population;
	}
}

class Prefecture extends Area {
	...
}

class City extends Area {
	...
}

 JavaScript では1対1というクラス継承でなければないことを規制していません。そのために、このような1つのクラスを複数のクラスで継承することができます。

 しかし、複数のクラスを1つのクラスで継承することは許していません。これを行うにはミックスインを使用します。

ミックスイン

 ミックスインという複数のクラスを1つのクラスで継承する機能があります。
 ミックスインはクラスではなく関数を利用した複数のクラスを利用できるようにしたものです。

 以下は地域名とその人口を記憶するクラスを分けています。人口クラスでは男性と女性を分けて記憶するようにしています。

以下の確認ボタンをクリックして、開発用コンソールで実行結果を確認してみてください。