読者です 読者をやめる 読者になる 読者になる

clock-up-blog

go-mi-tech

JavaScript:構造体同士の足し算

JavaScript

構造体的なオブジェクトの足し算みたいな

var a = {left: 10, top: 30};
var b = {left: 100, top: 300};
var c = a + b: // みたいなことやりたい

演算子オーバーロードはひとまず置いておき。

こんなトリッキーなことやりだすと未来の自分を困らせるので。。
Fake operator overloading in JavaScript

とりあえずObjectのメソッドとして実装してみる

@ylqjk さんからご指摘をいただき、Object.prototype を直接書き換えるのではなく Object.defineProperty を使うように修正しました。

// Object.defineProperty が使えない環境に対応
// 引用元:http://qiita.com/alucky0707/items/79b7f625cfd95ee3755d
function extendMethod(object, methodName, method) {
	if(typeof Object.defineProperty !== 'function') {
		object[methodName] = method;
	} else {
		Object.defineProperty(object, methodName, {
			configurable: false,
			enumerable: false,
			value: method,
		});
	}
}

/**
 * オブジェクト同士を加算
 * @return Object 左辺と右辺を加算して作られた新しいオブジェクト
 */
extendMethod(Object.prototype, 'plus', function(arg){
	var ret = {};
	for(key in this){
		ret[key] = this[key];
	}
	for(key in arg){
		if(key in ret){
			if(typeof arg[key] === 'function')continue;
			ret[key] = ret[key] + arg[key];
		}
		else{
			ret[key] = arg[key];
		}
	}
	return ret;
});

使用例

同じキー名の数値があれば数値演算、同じキー名の文字列があれば文字列結合、が発生します。
お互い食い違うキー名の値についてはそのままマージ。

// 普通に足す例 (数値演算)
var a = {left: 10, top: 30};
var b = {left: 100, top: 300};
var c = a.plus(b);
console.log(c);
Object {left: 110, top: 330}
// 計算繋げまくる例
var a = {left: 10, top: 30};
var b = {left: 100, top: 300};
var c = a.plus(b).plus(b).plus(a.plus(a).plus(a))
console.log(c);
Object {left: 240, top: 720}
// キーが食い違う場合の挙動 (マージ)
var a = {left: 10, top: 30};
var b = {left: 100, right: 300};
var c = a.plus(b);
console.log(c);
Object {left: 110, top: 30, right: 300}
// 文字列の結合
var a = {left: 10, msg1: 'Hello', msg2: 'ABC'};
var b = {left: 100, msg1: 'World', msg3: 'XYZ'};
var c = a.plus(b);
console.log(c);
Object {left: 110, msg1: "HelloWorld", msg2: "ABC", msg3: "XYZ"}

おわり

誰が得するんだよってエントリですが、少なくとも俺得なメソッド拡張でした。
メソッド名にはまだ納得がいってない。



ところで俺は JavaScript そんなに好きじゃないし、いまだに C++ が好きだし、なんでそれが好きかというと、演算子オーバーロードが標準で実現できるし、ちょっと変わったことやっても型が雄弁に文脈を語ってくれるので Ruby フレームワークみたいな黒魔術サイドに堕ちなくて済む、と思っている。

Twitterでいただいた指摘

旧コードに対して。

/**
 * オブジェクト同士を加算
 * @return Object 左辺と右辺を加算して作られた新しいオブジェクト
 */
Object.prototype.plus = function(arg){
	var ret = {};
	for(key in this){
		ret[key] = this[key];
	}
	for(key in arg){
		if(key in ret){
			if(typeof arg[key] === 'function')continue;
			ret[key] = ret[key] + arg[key];
		}
		else{
			ret[key] = arg[key];
		}
	}
	return ret;
};


ありがとうございます。勉強になりました。

});