Я работаю с 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? Поскольку это выглядит просто уродливо.
javascript
operators
operator-overloading
Ли Бриндли
источник
источник
Ответы:
Как вы обнаружили, JavaScript не поддерживает перегрузку операторов. Самое близкое, что вы можете придумать, - это реализовать
toString
(который будет вызываться, когда необходимо привести экземпляр к строке) иvalueOf
(который будет вызываться для приведения его к числу, например, при использовании+
для сложения или во многих случаях, когда используя его для конкатенации, потому что+
пытается выполнить сложение перед конкатенацией), что довольно ограничено. Ни то, ни другое не позволяет создавать в результатеVector2
объект.Vector2
Тем не менее, для людей, которые задаются этим вопросом и хотят получить в качестве результата строку или число (вместо a ), вот примеры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)
Или с 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
источник
Date
классе неявно преобразуют даты в числа с помощьюvalueOf
? Например, вы можете это сделать,date2 > date1
и это будет верно, если онdate2
был создан послеdate1
.>
,<
,>=
, И<=
(но не==
,===
,!=
или!==
) использовать абстрактную Реляционную сравнению операцию, в которой используетсяToPrimitive
с подсказкой «номером». ДляDate
объекта это приводит кgetTime
возвращаемому числу (значение миллисекунд с начала эпохи).Как сказал TJ, вы не можете перегружать операторы в 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]
источник
add
метода OP ... То, что они не хотели делать.+
знаком. Это очень хороший ответ, показывающий, как избежать вызова неестественного имени функции для квазичисловых объектов.+
оператором, - это возможность возвращать aNumber
в качестве замены для одного из операндов. Поэтому любая добавляющая функциональность, которая работает сObject
экземплярами, всегда должна кодировать объект как aNumber
и в конечном итоге декодировать его.FYI paper.js решает эту проблему, создав PaperScript, автономный javascript с ограниченной областью видимости с перегрузкой векторов операторами, которые затем обрабатываются обратно в javascript.
Но файлы документов должны быть специально указаны и обработаны как таковые.
источник
Фактически, есть один вариант 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;
Более подробно это описано в «Руководстве по инструментам Adobe Extendscript JavaScript» (текущая ссылка здесь ). Синтаксис, по-видимому, был основан на (теперь давно заброшенном) проекте стандарта ECMAScript.
источник
Можно выполнять векторные математические вычисления с двумя числами, упакованными в одно. Позвольте мне сначала показать пример, прежде чем я объясню, как это работает:
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 бит, а другое - в более низкие. Код стал немного более запутанным, потому что я хотел поддерживать числа со знаком.
function vec_pack(vec){ return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]); } function vec_unpack(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 битам каждый.
источник
Хотя это и не является точным ответом на вопрос, можно реализовать некоторые методы python __magic__, используя символы ES6.
[Symbol.toPrimitive]()
Метод не позволяет подразумевает вызовVector.add()
, но позволит вам использовать синтаксис , такие какDecimal() + int
.class AnswerToLifeAndUniverseAndEverything { [Symbol.toPrimitive](hint) { if (hint === 'string') { return 'Like, 42, man'; } else if (hint === 'number') { return 42; } else { // when pushed, most classes (except Date) // default to returning a number primitive return 42; } } }
источник
Мы можем использовать React-подобные хуки для оценки стрелочной функции с разными значениями из
valueOf
метода на каждой итерации.const a = Vector2(1, 2) // [1, 2] const b = Vector2(2, 4) // [2, 4] const c = Vector2(() => (2 * a + b) / 2) // [2, 4] // There arrow function will iterate twice // 1 iteration: method valueOf return X component // 2 iteration: method valueOf return Y component
Показать фрагмент кода
const Vector2 = (function() { let index = -1 return function(x, y) { if (typeof x === 'function') { const calc = x index = 0, x = calc() index = 1, y = calc() index = -1 } return Object.assign([x, y], { valueOf() { return index == -1 ? this.toString() : this[index] }, toString() { return `[${this[0]}, ${this[1]}]` }, len() { return Math.sqrt(this[0] ** 2 + this[1] ** 2) } }) } })() const a = Vector2(1, 2) const b = Vector2(2, 4) console.log('a = ' + a) // a = [1, 2] console.log(`b = ${b}`) // b = [2, 4] const c = Vector2(() => (2 * a + b) / 2) // [2, 4] a[0] = 12 const d = Vector2(() => (2 * a + b) / 2) // [13, 4] const normalized = Vector2(() => d / d.len()) // [0.955..., 0.294...] console.log(c, d, normalized)
Библиотека @ js-basics / vector использует ту же идею для Vector3.
источник
Интересна также экспериментальная библиотека operator-overloading-js . Он выполняет перегрузку только в определенном контексте (функция обратного вызова).
источник
Я написал библиотеку, которая использует кучу злых уловок, чтобы сделать это на необработанном JS. Он позволяет такие выражения.
Сложные числа:
>> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))
<- {r: -2, i: 1}
Автоматическая дифференциация:
Пусть
f(x) = x^3 - 5x
:>> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);
Теперь сопоставьте его с некоторыми значениями:
>> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)
<- [ 7, -2, -5, -2, 7 ]
т.е.
f'(x) = 3x^2 - 5.
Полиномы:
>> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')
<- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"
Для вашей конкретной проблемы вы должны определить
Vector2
функцию (или, может быть, что-то более короткое) с помощью библиотеки, а затем написатьx = Vector2()(x + y);
https://gist.github.com/pyrocto/5a068100abd5ff6dfbe69a73bbc510d7
источник