JavaScriptのObject.definePropertyを使ってみよう
フロントエンドエンジニアのQです。読者の皆様には全く興味がないであろう煩わしい前置きは抜きにして、早速JavaScriptのObject.definePropertyを使ってコードを改善する過程を紹介します。
サンプルとして以下の2次元ベクトルのオブジェクトのコードの一部を使います。とりあえずベクトルの長さを求めるメソッドが定義してあります。
1 |
[code lang="javascript"] function Vector2D() { this.x = 0; this.y = 0; } Vector2D.prototype.getLength = function () { return Math.sqrt(this.x * this.x + this.y * this.y); }; [/code] |
ベクトルの長さはベクトルの成分が変化しない限りは同じ値になりますが、上のgetLength()では呼び出されるたびに計算し直しています。これをgetter/setterを使い、ベクトルの成分が変化した時のみベクトルの長さを再計算して、getLength()では計算済みの値を返すようにしてみます。 (今回は単純なプロパティへのアクセスをgetter/setter呼び出しにすることによるオーバーヘッド等は考えないことにします。そういうことにさせて下さい・・・!)
1 |
[code lang="javascript"] function Vector2D() { this._x = 0; this._y = 0; this._length = 0; } Object.defineProperty(Vector2D.prototype, "x", { get: function () { return this._x; }, set: function (value) { this._x = value; this._length = Math.sqrt(this.x * this.x + this.y * this.y); } }); Object.defineProperty(Vector2D.prototype, "y", { get: function () { return this._y; }, set: function (value) { this._y = value; this._length = Math.sqrt(this.x * this.x + this.y * this.y); } }); Vector2D.prototype.getLength = function () { return this._length; }; [/code] |
getter/setterを使って、ベクトルの成分が変化した時のみベクトルの長さを再計算するようにできました。
以上!
・・・でもちょっと待ってください!この実装ではベクトルの成分が頻繁に変化する場合は、最初のコードよりも逆に計算が増えてしまいます。ベクトルの長さの再計算のタイミングを、ベクトルの成分が変化した時ではなく、ベクトルの成分が変化しベクトルの長さの値が必要になった時にしてみましょう。
1 |
[code lang="javascript"] function Vector2D() { this._x = 0; this._y = 0; this._length = 0; this._changed = false; } Object.defineProperty(Vector2D.prototype, "x", { get: function () { return this._x; }, set: function (value) { this._x = value; this._changed = true; } }); Object.defineProperty(Vector2D.prototype, "y", { get: function () { return this._y; }, set: function (value) { this._y = value; this._changed = true; } }); Vector2D.prototype.getLength = function () { if (this._changed) { this._length = Math.sqrt(this.x * this.x + this.y * this.y); this._changed = false; } return this._length; }; [/code] |
今度こそgetter/setterを使って、インターフェースを変えずちょこっとパフォーマンスを向上させることができました。
以上!
ちなみに人によりますが、私は v.getLength() より v.length のようにアクセスしたがる派なので、以下の書き方が好みです。
1 |
[code lang="javascript"] Object.defineProperty(Vector2D.prototype, "length", { get: function () { if (this._changed) { this._length = Math.sqrt(this.x * this.x + this.y * this.y); this._changed = false; } return this._length; } }); [/code] |
・・・
以上!