構造体的なオブジェクトの足し算みたいな
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; };
@kobayan_tokyo そのObject.prototype拡張は危険。それやるとfor in文でメソッドが列挙されるので、常にhasOwnPropertyでガードが必要になる。Object.definePropertyのenumerable:falseを使う必要がある。
— 夜クジャク (@ylqjk) August 20, 2014
@kobayan_tokyo 結論から書くとObject.defineProperty使え、Object.prototypeは拡張するな。 http://t.co/JWfipofi3W 辺りが詳しいです。
— 夜クジャク (@ylqjk) August 20, 2014
ありがとうございます。勉強になりました。