マウスポインタの位置に応じて立方体が回るつぎのサンプル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
>> jsdo.itのサンプルコードへ
ECMAScript 6の新しい機能や構文については、たくさんの記事が解説しています。けれど、古い記述でも動くのですから、実際のコードでどこをどう書き改めたらよいのかピンとこない向きもあるでしょう。そこで、サンプルのコードをECMAScript 6に書き改めながら、そうすると何がどうよくなるのかご説明します。
#変数の宣言
具体的なサンプルのコードに入る前に、変数の宣言について確かめておきましょう。ECMAScript 6では変数の宣言には、var
だけでなくlet
とconst
が使えるようになりました。ともにブロックレベルのスコープをもちます。const
は定数を定め、初期値は代入で書き替えられません(「JavaScript『再』入門」の「変数」参照)。
ECMAScript 6では新しいふたつの変数宣言を用い、値を変えない場合にはconst
で定めると、変数の問題を避け、コードも確かめやすくなるでしょう。
#クラスのコンストラクタとメソッド
これまでのJavaScriptコードでは、コンストラクタはfunction
キーワードで定め、メソッドをFunction.prototype
プロパティに加えました。つぎが、前掲サンプル001から抜書きしたコードです。
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
宣言を備えました。メソッドはそのブロックの中にfunction
キーワードなしで定めます。コンストラクタメソッドに用いる専用のキーワードがconstructor()
です。
また、関数にはデフォルト引数が等価演算子=
で与えられます。引数が渡されないときに用いられる値です。
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
プロパティにObject.create()
メソッドで親コンストラクタのプロトタイプからつくったオブジェクトを代入して行いました。そして、サブクラスのコンストラクタからスーパークラスのコンストラクタを呼ぶのに使われたのがFunction.call()
メソッドです。
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
キーワード、親コンストラクタの呼び出しはsuper
キーワードが使えます。つぎに抜書きしたサンプル001のコンストラクタFaceには、とくに継承を定めていません。配列のようにプロパティを扱いたかったのに、組み込み済みのArray
クラスが継承できなかったためです。
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
クラスも原則の構文にしたがって継承できるのです。
class Face extends Array {
constructor(pos0, pos1, pos2, pos3, color) {
super(pos0, pos1, pos2, pos3);
this.color = color;
}
}
#静的メソッドと分割代入
これまでのJavaScriptでは、静的なメソッドはコンストラクタ関数に直に定めました。サンプル001から抜書きしたつぎのクラスMathUtilsは、コンストラクタが要らないのでオブジェクトにしました。静的メソッドMathUtils.getRandomIntは、引数ふたつの大小を比べて、順序を整えています。
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
キーワードが加わりました。class
の本体のメソッドにstatic
を添えれば静的な扱いになります。また、複数の変数に値が同時に代入できる分割代入の構文も採り入れられました。変数の値の入れ替えが簡単にできます。
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)
メソッドを使ってもよいでしょう。
Point3D._point = new createjs.Point();
// または
Object.defineProperty(Point3D, '_point', {
value: new createjs.Point()
});
#アロー関数式とArrayクラスのメソッド
ECMAScript 5.1で、Array
クラスにはfor
文は使わず配列要素すべてを扱えるメソッドがいくつか備わりました。引数には関数を渡します。このとき、ECMAScript 6のアロー関数式=>
を用いると、さらにすっきりとコードが書けます。名前のない関数(無名関数)でfunction
キーワードは省き、代わりにアロー=>
を使う構文です。
つぎに抜書きしたのは、サンプル001でfor
文により配列の3次元座標を処理している関数rotate()です。
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()
メソッドは、配列要素を順に取り出して処理したうえで、戻り値の要素からなる新たな配列を返します。引数に渡すのは、その処理と戻り値を定めた関数です。関数rotate()は、つぎのように書き替えられます。for
文のようなカウンタ変数が要りません。
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()をご紹介します。インスタンスにインデックスで定めたプロパティを順に取り出して、新たな配列にして返しています。
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
も要りません。
class Face extends Array {
getFacePoints(points) {
const facePoints = this.map((element) => points[element]);
return facePoints;
}
}
Array
クラスの新しいメソッドの使い方については「JavaScriptのArrayクラスのメソッドで要素の標準偏差を求める」をご参照ください。
これらの手直しを加えたコードが、つぎのサンプル002です。
####サンプル002■EaselJS 1.0.0 + ES6: Rotating a Cube around the X and Y axes
>> jsdo.itのサンプルコードへ