CreateJSのJavaScriptコードをECMAScript 6の構文に書き替える

マウスポインタの位置に応じて立方体が回るつぎのサンプル001は、これまでのJavaScriptコードでCreateJSを用いて書きました。本稿では、これをECMAScript 2015(ECMAScript 6)の構文に書き替えてみます。併せて、CreateJSは最新のバージョン1.0.0を使いました。

####サンプル001■EaselJS 0.8.2: Rotating a Cube around the X and Y axes
[![rotating_3d_cube.png](https://qiita-image-store.s3.amazonaws.com/0/128949/7abba44c-932e-2514-6fdc-c87217a3a228.png)
\>\> jsdo.itのサンプルコードへ](http://jsdo.it/FumioNonaka/nREM)

ECMAScript 6の新しい機能や構文については、たくさんの記事が解説しています。けれど、古い記述でも動くのですから、実際のコードでどこをどう書き改めたらよいのかピンとこない向きもあるでしょう。そこで、サンプルのコードをECMAScript 6に書き改めながら、そうすると何がどうよくなるのかご説明します。

#変数の宣言
具体的なサンプルのコードに入る前に、変数の宣言について確かめておきましょう。ECMAScript 6では変数の宣言には、`var`だけでなく[`let`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/let)と[`const`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/const)が使えるようになりました。ともにブロックレベルのスコープをもちます。`const`は定数を定め、初期値は代入で書き替えられません(「JavaScript『再』入門」の「[変数](https://developer.mozilla.org/ja/docs/Web/JavaScript/A_re-introduction_to_JavaScript#Variables)」参照)。

ECMAScript 6では新しいふたつの変数宣言を用い、値を変えない場合には`const`で定めると、変数の問題を避け、コードも確かめやすくなるでしょう。

#クラスのコンストラクタとメソッド
これまでのJavaScriptコードでは、コンストラクタは`function`キーワードで定め、メソッドを[`Function.prototype`プロパティ](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype)に加えました。つぎが、前掲サンプル001から抜書きしたコードです。

```js
function Point3D(x, y, z) {
	this.x = x;
	this.y = y;
	this.z = z;
}
Point3D.prototype.getProjetedPoint = function(focalLength) {
	var point2D = new createjs.Point();
	var w = focalLength / (focalLength + this.z);
	point2D.x = this.x * w;
	point2D.y = this.y * w;
	return point2D;
};
```

ECMAScript 6は[`class`宣言](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/class)を備えました。メソッドはそのブロックの中に`function`キーワードなしで定めます。コンストラクタメソッドに用いる専用のキーワードが[`constructor()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes/constructor)です。

また、関数には[デフォルト引数](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions_and_function_scope/Default_parameters)が等価演算子`=`で与えられます。引数が渡されないときに用いられる値です。

```js
class Point3D {
	constructor(x = 0, y = 0, z = 0) {
		this.x = x;
		this.y = y;
		this.z = z;		
	}
	getProjetedPoint(focalLength) {
		let point2D = new createjs.Point();
		let w = focalLength / (focalLength + this.z);
		point2D.x = this.x * w;
		point2D.y = this.y * w;
		return point2D;
	}
}
```

#クラスの継承
ECMAScript 5の継承は、[`Object.prototype`プロパティ](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype)に[`Object.create()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Object.create()_を用いた古典的な継承)で親コンストラクタのプロトタイプからつくったオブジェクトを代入して行いました。そして、サブクラスのコンストラクタからスーパークラスのコンストラクタを呼ぶのに使われたのが[`Function.call()`メソッド](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/call)です。

```js
function Shape() {
  this.x = 0;
  this.y = 0;
}
Shape.prototype.move = function(x, y) {
	this.x += x;
	this.y += y;
};
function Rectangle() {
	Shape.call(this);
}
Rectangle.prototype = Object.create(Shape.prototype);
```

ECMAScript 6では、継承には[`extends`キーワード](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes/extends)、親コンストラクタの呼び出しは[`super`キーワード](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/super)が使えます。つぎに抜書きしたサンプル001のコンストラクタFaceには、とくに継承を定めていません。配列のようにプロパティを扱いたかったのに、組み込み済みの`Array`クラスが継承できなかったためです。

```js
function Face(pos0, pos1, pos2, pos3, color) {
	this.length = 4;
	this.color = color;
	this[0] = pos0;
	this[1] = pos1;
	this[2] = pos2;
	this[3] = pos3;
}
```

ECMAScript 6の`class`宣言を使えば、つぎのように`Array`クラスも原則の構文にしたがって継承できるのです。

```js
class Face extends Array {
	constructor(pos0, pos1, pos2, pos3, color) {
		super(pos0, pos1, pos2, pos3);
		this.color = color;
	}
}
```

#静的メソッドと分割代入
これまでのJavaScriptでは、静的なメソッドはコンストラクタ関数に直に定めました。サンプル001から抜書きしたつぎのクラスMathUtilsは、コンストラクタが要らないのでオブジェクトにしました。静的メソッドMathUtils.getRandomIntは、引数ふたつの大小を比べて、順序を整えています。

```js
var MathUtils = {};
MathUtils.getRandomInt = function(min, max) {
	if (min > max) {
		var temp = min;
		min = max;
		max = temp;
	}
	var randomNumber = Math.random() * (max - min) + min;
	return Math.floor(randomNumber);
};
```

ECMAScript 6には、[`static`キーワード](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes/static)が加わりました。`class`の本体のメソッドに`static`を添えれば静的な扱いになります。また、複数の変数に値が同時に代入できる[分割代入](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)の構文も採り入れられました。変数の値の入れ替えが簡単にできます。

```js
class MathUtils {
	static getRandomInt(min, max) {
		if (min > max) {
			[min, max] = [max, min];
		}
		const randomNumber = Math.random() * (max - min) + min;
		return Math.floor(randomNumber);
	}
}
```

なお、静的プロパティの定めはまだ備わっていません。これまでと同じく、クラスに直に加えることになります。ECMAScript 5.1の[`Object.defineProperty(https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)`メソッド]()を使ってもよいでしょう。

```js
Point3D._point = new createjs.Point();
// または
Object.defineProperty(Point3D, '_point', {
	value: new createjs.Point()
});
```

#アロー関数式とArrayクラスのメソッド
ECMAScript 5.1で、`Array`クラスには`for`文は使わず配列要素すべてを扱えるメソッドがいくつか備わりました。引数には関数を渡します。このとき、ECMAScript 6の[アロー関数式`=>`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/arrow_functions)を用いると、さらにすっきりとコードが書けます。名前のない関数(無名関数)で`function`キーワードは省き、代わりにアロー`=>`を使う構文です。

つぎに抜書きしたのは、サンプル001で`for`文により配列の3次元座標を処理している関数rotate()です。

```js
function rotate(eventObject) {
	var count = points.length;
	var RAD_TO_DEG = 1 / createjs.Matrix2D.DEG_TO_RAD;
	points2D.length = 0;
	matrixY.identity().rotate(angle.y * RAD_TO_DEG);
	matrixX.identity().rotate(angle.x * RAD_TO_DEG);
	for (var i = 0; i < count; i++) {
		var point = points[i];
		point.rotatePoint(matrixY, 'y');
		point.rotatePoint(matrixX, 'x');
		points2D[i] = point.getProjetedPoint(focalLength);
	}
	drawFaces(points2D, facesVertices);
}
```

[`Array.map()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map)は、配列要素を順に取り出して処理したうえで、戻り値の要素からなる新たな配列を返します。引数に渡すのは、その処理と戻り値を定めた関数です。関数rotate()は、つぎのように書き替えられます。`for`文のようなカウンタ変数が要りません。

```js
function rotate(eventObject) {
	const RAD_TO_DEG = 1 / createjs.Matrix2D.DEG_TO_RAD;
	matrixY.identity().rotate(angle.y * RAD_TO_DEG);
	matrixX.identity().rotate(angle.x * RAD_TO_DEG);
	points2D = points.map((point) => {
		point.rotatePoint(matrixY, 'y');
		point.rotatePoint(matrixX, 'x');
		return point.getProjetedPoint(focalLength);
	});
	drawFaces(points2D, facesVertices);
}
```

もうひとつサンプル001から、クラスFaceのメソッドgetFacePoints()をご紹介します。インスタンスにインデックスで定めたプロパティを順に取り出して、新たな配列にして返しています。

```js
Face.prototype.getFacePoints = function (points) {
	var faces = this.length;
	var facePoints = [];
	for (var i = 0; i < faces; i++) {
		facePoints[i] = points[this[i]];
	}
	return facePoints;
};
```

ECMAScript 6では、組み込み済みの`Array`クラスが継承できました。すると、クラスFaceのメソッドとして`Array.map()`が呼び出せます。アロー関数式で本体が1行のステートメントのときは、ブロックの波かっこ`{}`が省けます。そして、式の値が自動的に返されるので、`return`も要りません。

```js
class Face extends Array {

	getFacePoints(points) {
		const facePoints = this.map((element) => points[element]);
		return facePoints;
	}
}
```

`Array`クラスの新しいメソッドの使い方については「[JavaScriptのArrayクラスのメソッドで要素の標準偏差を求める](https://qiita.com/FumioNonaka/items/fee07b53fd277b218c97)」をご参照ください。

これらの手直しを加えたコードが、つぎのサンプル002です。

####サンプル002■EaselJS 1.0.0 + ES6: Rotating a Cube around the X and Y axes
[![rotating_3d_cube.png](https://qiita-image-store.s3.amazonaws.com/0/128949/7abba44c-932e-2514-6fdc-c87217a3a228.png)
\>\> jsdo.itのサンプルコードへ](http://jsdo.it/FumioNonaka/IKJm)