Javascript:运算符重载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19620667/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript: operator overloading
提问by Lee Brindley
I've been working with JavaScript for a few days now and have got to a point where I want to overload operators for my defined objects.
我已经使用 JavaScript 工作了几天,现在已经到了我想为我定义的对象重载运算符的地步。
After a stint on google searching for this it seems you can't officially do this, yet there are a few people out there claiming some long-winded way of performing this action.
在谷歌上搜索了一段时间之后,似乎您无法正式执行此操作,但是有一些人声称执行此操作的方式有些冗长。
Basically I've made a Vector2 class and want to be able to do the following:
基本上我已经创建了一个 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.
Instead I'm having to do this:
相反,我必须这样做:
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.
Is there an approach I can take to overload operators in my Vector2 class? As this just looks plain ugly.
有没有一种方法可以让我的 Vector2 类中的运算符重载?因为这看起来很丑。
回答by T.J. Crowder
As you've found, JavaScript doesn't support operator overloading. The closest you can come is to implement toString
(which will get called when the instance needs to be coerced to being a string) and valueOf
(which will get called to coerce it to a number, for instance when using +
for addition, or in many cases when using it for concatenation because +
tries to do addition before concatenation), which is pretty limited. Neither lets you create a Vector2
object as a result.
正如您所发现的,JavaScript 不支持运算符重载。最接近的是实现toString
(当需要将实例强制为字符串时valueOf
将调用它)和(将被调用以将其强制为数字,例如在+
用于加法时,或在许多情况下使用它进行连接是因为+
尝试在连接之前进行加法),这是非常有限的。两者都不允许您因此创建Vector2
对象。
For people coming to this question who want a string or number as a result (instead of a Vector2
), though, here are examples of valueOf
and toString
. These examples do notdemonstrate operator overloading, just taking advantage of JavaScript's built-in handling converting to primitives:
但是,对于想要字符串或数字作为结果(而不是Vector2
)的人来回答这个问题,这里是valueOf
和 的示例toString
。这些示例不演示运算符重载,只是利用 JavaScript 的内置处理转换为原语:
valueOf
valueOf
This example doubles the value of an object's val
property in response to being coerced to a primitive, for instance via +
:
此示例将对象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)
Or with ES2015's class
:
或者使用 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)
Or just with objects, no constructors:
或者只使用对象,没有构造函数:
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
toString
This example converts the value of an object's val
property to upper case in response to being coerced to a primitive, for instance via +
:
本示例将对象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
Or with ES2015's class
:
或者使用 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
Or just with objects, no constructors:
或者只使用对象,没有构造函数:
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
回答by user2259659
As T.J. said, you cannot overload operators in JavaScript. However you can take advantage of the valueOf
function to write a hack which looks better than using functions like add
every time, but imposes the constraints on the vector that the x and y are between 0 and MAX_VALUE. Here is the code:
正如 TJ 所说,你不能在 JavaScript 中重载运算符。但是,您可以利用该valueOf
函数编写一个看起来比add
每次都使用函数更好的 hack ,但是对 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);
};
Then you can write equations like this:
然后你可以写出这样的方程:
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]
回答by Joshua Penman
FYI paper.js solves this issue by creating PaperScript, a self-contained, scoped javascript with operator overloading of vectors, which it then processing back into javascript.
仅供参考 paper.js 通过创建 PaperScript 解决了这个问题,PaperScript 是一个自包含的、作用域的 javascript,具有向量的运算符重载,然后它会处理回 javascript。
But the paperscript files need to be specifically specified and processed as such.
但是paperscript文件需要专门指定和处理。
回答by J. Peterson
Actually, there is one variant of JavaScript that doessupport operator overloading. ExtendScript, the scripting language used by Adobe applications such as Photoshop and Illustrator, does have operator overloading. In it, you can write:
实际上,有一种 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;
This is described in more detail in the "Adobe Extendscript JavaScript tools guide" (current link here). The syntax was apparently based on a (now long abandoned) draft of the ECMAScript standard.
这在“Adobe Extendscript JavaScript 工具指南”(此处为当前链接)中有更详细的描述。语法显然是基于 ECMAScript 标准的(现已长期废弃)草案。
回答by Stuffe
It's possible to do vector math with two numbers packed into one. Let me first show an example before I explain how it works:
可以将两个数字合二为一进行矢量数学运算。在解释它是如何工作的之前,让我先展示一个例子:
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");
I am using the fact that if you bit shift two numbers X times and then add or subtract them before shifting them back, you will get the same result as if you hadn't shifted them to begin with. Similarly scalar multiplication and division works symmetrically for shifted values.
我使用的事实是,如果您将两个数字移位 X 次,然后在将它们移回之前将它们加或减,您将获得与开始时没有移位它们相同的结果。类似地,标量乘法和除法对于移位值对称地工作。
A JavaScript number has 52 bits of integer precision (64 bit floats), so I will pack one number into he higher available 26 bits, and one into the lower. The code is made a bit more messy because I wanted to support signed numbers.
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;
}
}
The only downside I can see with this is that the x and y has to be in the range +-33 million, since they have to fit within 26 bits each.
我能看到的唯一缺点是 x 和 y 必须在 +-3300 万的范围内,因为它们每个都必须在 26 位以内。
回答by xmedeko
Interesting is also experimentallibrary operator-overloading-js. It does overloading in a defined context (callback function) only.
有趣的还有实验性库operator-overloading-js。它仅在定义的上下文(回调函数)中进行重载。
回答by FTOH
We can use React-like Hooks to evaluate arrow function with different values from valueOf
method on each iteration.
我们可以使用类似 React 的 HooksvalueOf
在每次迭代中使用来自方法的不同值来评估箭头函数。
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)
Library @js-basics/vectoruses the same idea for Vector3.
库@js-basics/vector对 Vector3 使用相同的想法。
回答by James McGuigan
Whilst not an exact answer to the question, it is possible to implement some of the python __magic__ methods using ES6 Symbols
虽然不是问题的确切答案,但可以使用 ES6 符号实现一些 python __magic__ 方法
A [Symbol.toPrimitive]()
method doesn't let you imply a call Vector.add()
, but will let you use syntax such as Decimal() + int
.
一个[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;
}
}
}
回答by High
First of all this is not operator overloading
首先,这不是运算符重载
Create your Vector3
object like this:
Vector3
像这样创建你的对象:
function Vector3(x, y, z) {
this.x = x
this.y = y
this.z = z
}
Extend Vector3
function like this:
Vector3
像这样扩展功能:
Vector3.operator = {
add: function (a, b) {
return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z)
},
sub: function (a, b) {
return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z)
},
mul: function (a, b) {
return new Vector3(a.x * b, a.y * b, a.z * b)
},
div: function (a, b) {
return new Vector3(a.x / b, a.y / b, a.z / b)
}
}
Finally use your tool:
最后使用你的工具:
var a = new Vector3(1, 2, 3)
var b = new Vector3(4, 5, 6)
// step 1: get
var { add, sub, mul, div } = Vector3.operator
// step 2: use
var r1 = add(a, b) // ---------- a + b
var r2 = mul(add(a, b), 4) //--- (a + b) * 4
var r3 = div(b, 4) //----------- b / 4
console.log(r1)
console.log(r2)
console.log(r3)
Look how easily convert formula to method calls:
看看如何轻松地将公式转换为方法调用:
a + (b - a) * t // lerp furmola
(a + ((b - a) * t)) // step 1: add all parentheses depend on order of operations
add(a + mul(sub(b - a) * t)) // step 2: before every parentheses write method name depend on operation in it.
add(a, mul(sub(b, a), t)) // step 3: replace operators(+-*/) with ','