JavaScript

最近良くやるJavaScriptでのクラスの書き方

More than 5 years have passed since last update.

クラスというかコンストラクタですが。

var Point = (function() {

// コンストラクタ
var ctor = function Point(x, y) {
// new なしで呼び出すとエラー
if (!(this instanceof ctor))
throw new TypeError('Constructor cannot be called as a function.');
this.x = x;
this.y = y;
};

var proto = ctor.prototype = {};
proto.constructor = ctor;

// 別名コンストラクタ
ctor.from = (function(c) {
c.prototype = proto;
return c;
}(function from(object) {
if (!(this instanceof ctor))
throw new TypeError('Constructor cannot be called as a function');
this.x = object.x;
this.y = object.y;
}));

// メソッド
// Point.prototype.moveっていちいち書きたくない。
proto.move = function move(dx, dy) {
this.x += dx;
this.y += dy;
};

return ctor;
}());

以下の点に気をつけていたらこんなかんじに。



  • Point.name"Point"であるべき。


    • デバッグしやすい。大変重要。




  • Point()はエラーを投げるべき。


    • 勝手にnew Point()だと解釈するのは良くないと思う。




  • new Point(0, 0)の他にnew Point.from({x: 0, y: 0})のような別の名前のコンストラクタもあると便利


    • (new Point.from({x: 0, y: 0}) instanceof Point) === true

    • Dartからパクった

    • 引数の型とか数を見て処理を分けるよりも、名前自体が変わっている方が作り易い

    • コードの意味もわかりやすいと思うし




  • Point.prototype.move.name"move"であるべき。

継承する場合はもうちょっといろいろ。

// Object.createがない環境のための用意。

var create = Object.create || function create(proto) {
var ctor = function() {};
ctor.prototype = proto;
return new ctor();
};

// Pointクラスを継承するPoint3Dクラス。
var Point3D = (function() {
// プロトタイプチェーンに組み込みたいコンストラクタはここに書く。
// もし他のコンストラクタを継承したくなっても、ここだけ書き換えればいい。
var superCtor = Point;
var superProto = superCtor.prototype;

var ctor = function Point3D(x, y, z) {
if (!(this instanceof ctor))
throw new TypeError('Constructor cannot be called as a function.');
superCtor.call(this, x, y);
this.z = z;
};

var proto = ctor.prototype = create(superProto);
proto.constructor = ctor;

ctor.from = (function(c) {
c.prototype = proto;
return c;
}(function from(object) {
if (!(this instanceof ctor))
throw new TypeError('Constructor cannot be called as a function');
// ファクトリメソッドじゃなくて別名コンストラクタにすると
// こんな感じに再利用出来る。
superCtor.from.call(this, object);
this.z = object.z;
}));

proto.move3D = function move3D(dx, dy, dz) {
// Point.prototype.move.call(...)とか書きたくないからsuperProto変数を用意する。
superProto.move.call(this, dx, dy);
this.z += dz;
};

return ctor;
}());