Javascript: перегрузка оператора
Я работаю с JavaScript уже несколько дней и добрался до точки, где хочу перегрузить операторы для своих определенных объектов.
После ожидания поиска в Google, похоже, вы не можете официально это сделать, но есть несколько человек, которые заявляют о некотором длинном способе выполнения этого действия.
В основном я создал класс 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? Поскольку это выглядит просто уродливо.
Ответы
Ответ 1
Как вы обнаружили, JavaScript не поддерживает перегрузку операторов. Самое близкое, что вы можете придумать, это реализовать toString
(который будет вызываться, когда экземпляр должен быть приведен к строке), и valueOf
(который будет вызываться для приведения его к числу, например, при использовании +
для сложения или в во многих случаях при использовании его для конкатенации, потому что +
пытается сделать сложение перед конкатенацией), что довольно ограничено. Ни один из них не позволяет вам создать объект Vector2
в результате.
Vector2
менее, для людей, которые приходят на этот вопрос и которым нужна строка или число в результате (вместо Vector2
), здесь приведены примеры valueOf
и toString
. Эти примеры не демонстрируют перегрузку операторов, а только используют встроенную обработку JavaScript, преобразующую в примитивы:
valueOf
В этом примере удваивается значение свойства объекта val
в ответ на приведение к примитиву, например, через +
:
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)
Ответ 2
Как 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]
Ответ 3
FYI paper.js решает эту проблему, создавая PaperScript, автономный, облачный javascript с перегрузкой операторов векторами, который затем обрабатывает обратно в javascript.
Но файлы документов должны быть специально указаны и обработаны как таковые.
Ответ 4
Можно сделать векторную математику с двумя числами, упакованными в одно. Позвольте мне сначала показать пример, прежде чем я объясню, как это работает:
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");
Я использую тот факт, что если вы сдвинете биты на два числа X раз, а затем сложите или вычтете их, прежде чем сдвинуть их обратно, вы получите тот же результат, как если бы вы не переместили их с самого начала. Аналогично скалярное умножение и деление работает симметрично для сдвинутых значений.
Число JavaScript имеет 52-битную целочисленную точность (64-разрядные числа с плавающей запятой), поэтому я упакую один номер в более высокий доступный 26-битный, а другой - в младший. Код сделан немного более грязным, потому что я хотел поддерживать числа со знаком.
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 должны находиться в диапазоне +-33 миллионов, так как они должны умещаться в пределах 26 бит каждый.
Ответ 5
Моим любимым способом работы с векторами является создание массива, который содержит векторные клавиши, и цикл через этот массив, когда мне нужно использовать оба компонента моих векторов. Я не тестировал производительность этого подхода, но я уверен, что он более эффективен, чем популярные библиотеки, поскольку он не создает никаких объектов при выполнении вычислений и не выполняет никаких функций. Кроме того, это более читаемо.
function Vector(x, y) {
this.x = x;
this.y = y;
}
const comps = ['x', 'y'];
var d = new Vector(0, 0);
var v = new Vector(10, 0);
var t = 1;
for(let c of comps) {
d[c] += v[c] * t;
}
console.log(d); //{ x: 10, y: 0 }
Ответ 6
На самом деле, есть один вариант JavaScript, который поддерживает перегрузку операторов. ExtendScript, язык сценариев, используемый приложениями Adobe, такими как Photoshop и Illustrator, действительно перегружен оператором. В нем вы можете написать:
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;
Более подробно это описано в "Руководстве по инструментам JavaScript Adobe Extendscript" (текущая ссылка здесь). Синтаксис, по-видимому, основан на (теперь давно заброшенном) проекте стандарта ECMAScript.