JavaScriptを数日間使って、定義済みのオブジェクトの演算子をオーバーロードしたいところまで来ました。
グーグルでこれを検索した後、正式にこれを行うことはできないようですが、このアクションを実行するための長い方法を主張している人々がいます。
基本的に、Vector2クラスを作成し、次のことを実行できるようにしたいと考えています。
var x = new Vector2(10,10);
var y = new Vector2(10,10);
x += y; //This does not result in x being a vector with 20,20 as its x & y values.
代わりに私はこれをしなければなりません:
var x = new Vector2(10,10);
var y = new Vector2(10,10);
x = x.add(y); //This results in x being a vector with 20,20 as its x & y values.
Vector2クラスで演算子をオーバーロードするために取れるアプローチはありますか?これは単純に見苦しいように見えます。
お気づきのように、JavaScriptは演算子のオーバーロードをサポートしていません。最も近い方法は、toString
(インスタンスを文字列に強制する必要があるときに呼び出される)とvalueOf
(番号に強制するために呼び出される)を実装することです。たとえば、+
を追加に使用する場合、または+
が連結の前に追加を試行するため、多くの場合、連結に使用する場合、これはかなり制限されています。どちらも、結果としてVector2
オブジェクトを作成できません。
(Vector2
の代わりに)結果として文字列または数値を必要とするこの質問に来ている人のために、valueOf
とtoString
の例を次に示します。これらの例しないは、プリミティブに変換するJavaScriptの組み込み処理を利用するだけで、演算子のオーバーロードを示しています。
valueOf
この例では、たとえば+
を介して、プリミティブに強制されることに応じて、オブジェクトのval
プロパティの値を2倍にします。
function Thing(val) {
this.val = val;
}
Thing.prototype.valueOf = function() {
// Here I'm just doubling it; you'd actually do your longAdd thing
return this.val * 2;
};
var a = new Thing(1);
var b = new Thing(2);
console.log(a + b); // 6 (1 * 2 + 2 * 2)
または、ES2015のclass
で:
class Thing {
constructor(val) {
this.val = val;
}
valueOf() {
return this.val * 2;
}
}
const a = new Thing(1);
const b = new Thing(2);
console.log(a + b); // 6 (1 * 2 + 2 * 2)
または、オブジェクトのみで、コンストラクタはありません:
var thingPrototype = {
valueOf: function() {
return this.val * 2;
}
};
var a = Object.create(thingPrototype);
a.val = 1;
var b = Object.create(thingPrototype);
b.val = 2;
console.log(a + b); // 6 (1 * 2 + 2 * 2)
toString
この例では、たとえば+
を介してプリミティブに強制されることに応じて、オブジェクトのval
プロパティの値を大文字に変換します。
function Thing(val) {
this.val = val;
}
Thing.prototype.toString = function() {
return this.val.toUpperCase();
};
var a = new Thing("a");
var b = new Thing("b");
console.log(a + b); // AB
または、ES2015のclass
で:
class Thing {
constructor(val) {
this.val = val;
}
toString() {
return this.val.toUpperCase();
}
}
const a = new Thing("a");
const b = new Thing("b");
console.log(a + b); // AB
または、オブジェクトのみで、コンストラクタはありません:
var thingPrototype = {
toString: function() {
return this.val.toUpperCase();
}
};
var a = Object.create(thingPrototype);
a.val = "a";
var b = Object.create(thingPrototype);
b.val = "b";
console.log(a + b); // AB
T.J.としてJavaScriptで演算子をオーバーロードすることはできません。ただし、valueOf
関数を利用して、毎回add
などの関数を使用するよりも良く見えるが、xとyが0〜 MAX_VALUE。コードは次のとおりです。
var MAX_VALUE = 1000000;
var Vector = function(a, b) {
var self = this;
//initialize the vector based on parameters
if (typeof(b) == "undefined") {
//if the b value is not passed in, assume a is the hash of a vector
self.y = a % MAX_VALUE;
self.x = (a - self.y) / MAX_VALUE;
} else {
//if b value is passed in, assume the x and the y coordinates are the constructors
self.x = a;
self.y = b;
}
//return a hash of the vector
this.valueOf = function() {
return self.x * MAX_VALUE + self.y;
};
};
var V = function(a, b) {
return new Vector(a, b);
};
次に、次のような方程式を記述できます。
var a = V(1, 2); //a -> [1, 2]
var b = V(2, 4); //b -> [2, 4]
var c = V((2 * a + b) / 2); //c -> [2, 4]
FYI paper.jsは、ベクトルの演算子オーバーロードを備えた自己完結型のスコープ付きjavascriptであるPaperScriptを作成することでこの問題を解決し、それを処理してjavascriptに戻します。
ただし、paperscriptファイルは、具体的に指定され、そのように処理される必要があります。
2つの数値を1つにまとめてベクトル演算を行うことができます。どのように機能するかを説明する前に、最初に例を示します。
let a = vec_pack([2,4]);
let b = vec_pack([1,2]);
let c = a+b; // Vector addition
let d = c-b; // Vector subtraction
let e = d*2; // Scalar multiplication
let f = e/2; // Scalar division
console.log(vec_unpack(c)); // [3, 6]
console.log(vec_unpack(d)); // [2, 4]
console.log(vec_unpack(e)); // [4, 8]
console.log(vec_unpack(f)); // [2, 4]
if(a === f) console.log("Equality works");
if(a > b) console.log("Y value takes priority");
2つの数値をX回ビットシフトし、次にそれらを加算または減算してからシフトバックすると、最初にシフトしていない場合と同じ結果が得られるという事実を使用しています。同様に、スカラーの乗算と除算は、シフトされた値に対して対称的に機能します。
JavaScriptの数値には52ビットの整数精度(64ビットの浮動小数点数)があるため、1つの数値を利用可能な上位26ビットに、もう1つを下位に詰めます。符号付きの数値をサポートしたかったため、コードは少し複雑になりました。
vec_unpack = function(number){
switch(((number & 33554432) !== 0) * 1 + (number < 0) * 2){
case(0):
return [(number % 33554432),Math.trunc(number / 67108864)];
break;
case(1):
return [(number % 33554432)-33554432,Math.trunc(number / 67108864)+1];
break;
case(2):
return [(((number+33554432) % 33554432) + 33554432) % 33554432,Math.round(number / 67108864)];
break;
case(3):
return [(number % 33554432),Math.trunc(number / 67108864)];
break;
}
}
私がこれで見ることができる唯一の欠点は、xとyがそれぞれ26ビット以内に収まらなければならないので、+-3300万の範囲になければならないことです。
実際、does演算子オーバーロードをサポートするJavaScriptのバリアントが1つあります。 PhotoshopやIllustratorなどのAdobeアプリケーションで使用されるスクリプト言語であるExtendScriptには、演算子のオーバーロードがあります。その中で、あなたは書くことができます:
Vector2.prototype["+"] = function( b )
{
return new Vector2( this.x + b.x, this.y + b.y );
}
var a = new Vector2(1,1);
var b = new Vector2(2,2);
var c = a + b;
これについては、「Adobe Extendscript JavaScriptツールガイド」(現在の リンクはこちら )で詳しく説明されています。構文は明らかに、(長い間放棄されていた)ECMAScript標準のドラフトに基づいていたようです。