This question already has answers here:
Javascript: operator overloading
(9 answers)
Overloading Arithmetic Operators in JavaScript?
(11 answers)
Closed 7 years ago.
Is it possible to define custom operators between instances of a type in JavaScript?
For example, given that I have a custom vector class, is it possible to use
vect1 == vect2
to check for equality, whilst the underlying code would be something like this?
operator ==(a, b) {
return a.x == b.x && a.y == b.y && a.z == b.z;
}
(This is nonsense of course.)
I agree that the equal function on the vector prototype is the best solution. Note that you can also build other infix-like operators via chaining.
function Vector(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vector.prototype.add = function (v2) {
var v = new Vector(this.x + v2.x,
this.y + v2.y,
this.z + v2.z);
return v;
}
Vector.prototype.equal = function (v2) {
return this.x == v2.x && this.y == v2.y && this.z == v2.z;
}
You can see online sample here.
Update: Here's a more extensive sample of creating a Factory function that supports chaining.
No, JavaScript doesn’t support operator overloading. You will need to write a method that does this:
Vector.prototype.equalTo = function(other) {
if (!(other instanceof Vector)) return false;
return a.x == b.x && a.y == b.y && a.z == b.z;
}
Then you can use that method like:
vect1.equalTo(vect2)
The best you can do if you want to stick with the == operator:
function Vector(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vector.prototype.toString = function () {
return this.x + ";" + this.y + ";" + this.z;
};
var a = new Vector(1, 2, 3);
var b = new Vector(1, 2, 3);
var c = new Vector(4, 5, 6);
alert( String(a) == b ); // true
alert( String(a) == c ); // false
alert( a == b + "" ); // true again (no object wrapper but a bit more ugly)
No, it's not part of the spec (which doesn't mean that there aren't some hacks).
You can change built-in methods of objects in JavaScript, such as valueOf() method. For any two objects to apply the following operators >, <, <=, >=, -, + JavaScript takes the property valueOf() of each object, so it deals with operators kind of like this: obj1.valueOf() == obj2.valueOf() (this does behind the scenes). You can overwrite the valueOf() method depends on your needs. So for example:
var Person = function(age, name){
this.age = age;
this.name = name;
}
Person.prototype.valueOf(){
return this.age;
}
var p1 = new Person(20, "Bob"),
p2 = new Person(30, "Bony");
console.log(p1 > p2); //false
console.log(p1 < p2); //true
console.log(p2 - p1); //10
console.log(p2 + p1); //40
//for == you should the following
console.log(p2 >= p1 && p2 <= p1); // false
So this is not the precise answer for your question, but I think this can be an useful stuff for that kind of issues.
It isn't a direct answer for you question but it's worth to note.
PaperScript is a simple extension of JavaScript that adds support for operator overloading to any object.
It used for for making Vector graphics on top of HTML5 Canvas.
It parse PaperScript to JavaScript on script tag with type="text/paperscript":
<!DOCTYPE html>
<html>
<head>
<!-- Load the Paper.js library -->
<script type="text/javascript" src="js/paper.js"></script>
<!-- Define inlined PaperScript associate it with myCanvas -->
<script type="text/paperscript" canvas="myCanvas">
// Define a point to start with
var point1 = new Point(10, 20);
// Create a second point that is 4 times the first one.
// This is the same as creating a new point with x and y
// of point1 multiplied by 4:
var point2 = point1 * 4;
console.log(point2); // { x: 40, y: 80 }
// Now we calculate the difference between the two.
var point3 = point2 - point1;
console.log(point3); // { x: 30, y: 60 }
// Create yet another point, with a numeric value added to point3:
var point4 = point3 + 30;
console.log(point4); // { x: 60, y: 90 }
// How about a third of that?
var point5 = point4 / 3;
console.log(point5); // { x: 20, y: 30 }
// Multiplying two points with each other multiplies each
// coordinate seperately
var point6 = point5 * new Point(3, 2);
console.log(point6); // { x: 60, y: 60 }
var point7 = new Point(10, 20);
var point8 = point7 + { x: 100, y: 100 };
console.log(point8); // { x: 110, y: 120 }
// Adding size objects to points work too,
// forcing them to be converted to a point first
var point9 = point8 + new Size(50, 100);
console.log(point9); // { x: 160, y: 220 }
// And using the object notation for size works just as well:
var point10 = point9 + { width: 40, height: 80 };
console.log(point10); // { x: 200, y: 300 }
// How about adding a point in array notation instead?
var point5 = point10 + [100, 0];
console.log(point5); // { x: 300, y: 300 }
</script>
</head>
<body>
<canvas id="myCanvas" resize></canvas>
</body>
</html>
Here is a simple emulation which tests for equality using the guard operator:
function operator(node)
{
// Abstract the guard operator
var guard = " && ";
// Abstract the return statement
var action = "return ";
// return a function which compares two vector arguments
return Function("a,b", action + "a.x" + node + "b.x" + guard + "a.y" + node + "b.y" + guard + "a.z" + node + "a.z" );
}
//Pass equals to operator; pass vectors to returned Function
var foo = operator("==")({"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3});
var bar = operator("==")({"x":1,"y":2,"z":3},{"x":4,"y":5,"z":6});
//Result
console.log(["foo",foo,"bar",bar]);
For non-strict mode functions the array index (defined in 15.4) named data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function’s execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object‘s properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.
References
The `arguments` object changes if parameters change
Annotated ES5: The Arguments Object
Javascript check arguments for zero value
Related
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.
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:
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.
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. Similarly, Proxy (added in ES2015) lets you intercept various object operations (including property access), but again won't let you control the result of += on Vector instances.
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 not demonstrate operator overloading, just taking advantage of JavaScript's built-in handling converting to primitives:
valueOf
This example doubles the value of an object's val property in response to being coerced to a primitive, for instance via +:
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:
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
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 +:
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:
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
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:
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]
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.
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.
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.
Actually, there is one variant of JavaScript that does support operator overloading. ExtendScript, the scripting language used by Adobe applications such as Photoshop and Illustrator, does have operator overloading. In it, you can write:
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.
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.
But the paperscript files need to be specifically specified and processed as such.
We can use React-like Hooks to evaluate arrow function with different values from valueOf method on each iteration.
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/vector uses the same idea for Vector3.
I wrote a library that exploits a bunch of evil hacks to do it in raw JS. It allows expressions like these.
Complex numbers:
>> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))
<- {r: -2, i: 1}
Automatic differentiation:
Let f(x) = x^3 - 5x:
>> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);
Now map it over some values:
>> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)
<- [ 7, -2, -5, -2, 7 ]
i.e. f'(x) = 3x^2 - 5.
Polynomials:
>> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')
<- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"
For your particular problem, you would define a Vector2 function (or maybe something shorter) using the library, then write x = Vector2()(x + y);
https://gist.github.com/pyrocto/5a068100abd5ff6dfbe69a73bbc510d7
Whilst not an exact answer to the question, it is possible to implement some of the python __magic__ methods using ES6 Symbols
A [Symbol.toPrimitive]() method doesn't let you imply a call Vector.add(), but will let you use syntax such as 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;
}
}
}
https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/
Interesting is also experimental library operator-overloading-js . It does overloading in a defined context (callback function) only.
I am trying to return the value of the closest object to my position in a function.
I tried to put the entities in a array and than I tried return closest object to my position with a for loop, but it did not work. How can I do this?
function getMyEntity() {
return Game.currentGame.world.localPlayer.entity.getFromTick();
}
function getOtherEntity() {
var MyPlayerEntity = getMyEntity();
var entities = Game.currentGame.world.entities;
for (var uid in entities) {
// how i get closest entity to my player entity here?
var gameObject = entities[uid].fromTick;
console.log(entities[uid].fromTick.position, MyPlayerEntity.position)
if (gameObject.entityClass == "Prop" && gameObject.uid !== MyPlayerEntity.uid) {
return gameObject;
}
}
}
function aimAssist() {
var MyPlayerEntity = getMyEntity();
var OtherEntity = getOtherEntity();
if (OtherEntity == undefined) return
var aimPosition = {
x: OtherEntity.position.x - MyPlayerEntity.position.x,
y: OtherEntity.position.y - MyPlayerEntity.position.y
}
return aimPosition;
}
I'll give you a bad advice, for now it'll work, as your game grows it will be bad because of O(n^2) complexity. Read a bit about quadtrees and see if you can do that. Meanwhile you can compare the euclidean distance, do not need to take the square root:
Object.keys(entities)
.map(function(d,i){
var dx = entities[d].fromTick.position.x - MyPlayerEntity.position.x,
dy = entities[d].fromTick.position.y - MyPlayerEntity.position.y,
result = {D:(dx * dx) + (dy + dy), obj:entities[d] , valueOf: function(){return this.D};
return result;
}).sort(function(a,b){
return a-b;
})[0].obj; //this returns the closest one
So your original function becomes this:
function getOtherEntity() {
var MyPlayerEntity = getMyEntity();
var entities = Game.currentGame.world.entities;
return Object.keys(entities)
.map(function(d,i){
var dx = entities[d].fromTick.position.x - MyPlayerEntity.position.x,
dy = entities[d].fromTick.position.y - MyPlayerEntity.position.y,
result = {D:(dx * dx) + (dy + dy), obj:entities[d] , valueOf: function(){return this.D};
return result;
}).sort(function(a,b){
return a-b;
})[0].obj; //this returns the closest one
}
I'd create an array of objects which contain the Object-ID and distance, and sort it by distance.
The first array-item is the closest to the player.
The array may look like
[{uid: 1234, distance: 12}, {uid: 1235, distance: 16}, ...]
You can sort arrays with arrayName.sort(sortingFunction)
assuming you can get x and y coordinates from gameObject.position and MyPlayerEntity.position you could use a bit of Pitagoras: c^2 = a^2 +b^2, with c being the distance
let a = gameObject.position.x - MyPlayerEntity.position.x;
let b = gameObject.position.y - MyPlayerEntity.position.y;
let distanceSquared = a*a + b*b;
Since you don't seem to need the exact distance and sqrt() is expensive, you can use the value in distanceSquared and other variables declared outside of the loop to keep track
let closestDistance;
let closest;
to make the proper comparisons
if(distanceSquared < closestDistance){
closestDistance = distanceSquared;
closest = gameObject;
}
After you loop through the array, the closest entity reference should be:
return closest;
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.
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:
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.
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. Similarly, Proxy (added in ES2015) lets you intercept various object operations (including property access), but again won't let you control the result of += on Vector instances.
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 not demonstrate operator overloading, just taking advantage of JavaScript's built-in handling converting to primitives:
valueOf
This example doubles the value of an object's val property in response to being coerced to a primitive, for instance via +:
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:
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
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 +:
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:
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
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:
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]
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.
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.
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.
Actually, there is one variant of JavaScript that does support operator overloading. ExtendScript, the scripting language used by Adobe applications such as Photoshop and Illustrator, does have operator overloading. In it, you can write:
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.
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.
But the paperscript files need to be specifically specified and processed as such.
We can use React-like Hooks to evaluate arrow function with different values from valueOf method on each iteration.
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/vector uses the same idea for Vector3.
I wrote a library that exploits a bunch of evil hacks to do it in raw JS. It allows expressions like these.
Complex numbers:
>> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))
<- {r: -2, i: 1}
Automatic differentiation:
Let f(x) = x^3 - 5x:
>> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);
Now map it over some values:
>> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)
<- [ 7, -2, -5, -2, 7 ]
i.e. f'(x) = 3x^2 - 5.
Polynomials:
>> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')
<- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"
For your particular problem, you would define a Vector2 function (or maybe something shorter) using the library, then write x = Vector2()(x + y);
https://gist.github.com/pyrocto/5a068100abd5ff6dfbe69a73bbc510d7
Whilst not an exact answer to the question, it is possible to implement some of the python __magic__ methods using ES6 Symbols
A [Symbol.toPrimitive]() method doesn't let you imply a call Vector.add(), but will let you use syntax such as 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;
}
}
}
https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/
Interesting is also experimental library operator-overloading-js . It does overloading in a defined context (callback function) only.
When I add two variables that I initialize as numbers, JS considers them as a string and concatenates them. In a calculation as follows,
var p1 = new window.TRIGEO.Point(150, 150);
var p2 = new window.TRIGEO.Point(500, 350);
var p3 = new window.TRIGEO.Point(50, 500);
var medicentre = new Point((p1.x+p2.x+p3.x)/3,(p1.y+p2.y+p3.y)/3);
(where Point has x and y as members),medicentre is huge =>( 5016683.33 , 50116833.33 ). I do not want this when the answer is actually =>( 233.33 , 333.33 ).
Is there any way to override this behaviour without making the formula too long, cause I have another one, which is at least three lines long. Is this possible without using parseInt()?
EDIT:
Point object is the following and that's it!
function Point(x, y) {
this.x = x;
this.y = y;
}
TRIGEO is the name of the library I'm writing to visualize all the important points and line segments of a triangle. Sorry for the confusion, I probably should've edited.
whilst Parsint as already answered is technically correct, if you want a shorter formula you can trick the casting by making your values positive (as long as you know they aren't going to have non-numberic values
var num1 = '1111.11';
var num2 = 2222.22;
var n1plus2 = (+num1)+num2
// n1p2 : 3333.33
or both strings:
var num1 = '1111.11';
var num2 = '2222.22';
var n1p2 = (+num1)+(+num2);
// n1p2 : 3333.33
JavaScript doesn't consider them as string. You probably execute an operation in the Point class and convert them.
function A(x, y) {
this.x = x;
this.y = y;
}
var a = new A(1, 1),
b = new A(2, 2),
c = new A(a.x + b.x, a.y + b.y);
alert(c.x + " - " + c.y)
This will output 3 - 3 as expected.
I created a function which takes the inner of the tag S in a XML string:
'<C><P /><Z><S>[Grounds here]</S><D /><O /></Z></C>'
, containing grounds data, obviously related to a game. Then I consume every ground data in this inner until I reach the number z with i in my for loop and return the data of the first/one remaining ground as a object.
The problem: the function returns undefined instead of a object.
This is the function:
/**
* Get a string between 2 strings.
*/
String.prototype.between = function(left, right) {
var sub = this.substr(this.lastIndexOf(left) + left.length);
return sub.substr(0, sub.indexOf(right));
}
/**
* #param {Number} z The inner position of the ground I want to read, e.g, 1, the first.
*/
function readGround(z) {
// string containing all existent grounds
var groundsData = xml.substr(STG.indexOf('<S>') + 3, STG.lastIndexOf('</S>'));
// Iterate the grounds while z isn't reached
for(var i = 1; i < z; i++) {
// Get the ground inner
var outer = groundsData.substr(groundsData.indexOf('<S') + 3, groundsData.indexOf('/>'));
// Check if i reached z
if(i === z) {
// Get grounds properties
var a = [
outer.between('L="', '"'),
outer.between('H="', '"'),
outer.between('X="', '"'),
outer.between('Y="', '"')
];
return {
L: a[0], H: a[1],
X: a[2], Y: a[3]
};
// Else skip this ground
} else groundsData = groundsData.substr(groundsData.indexOf('/>'), groundsData.length);
}
}
Your loop makes i go from 1 to num-1. But within the loop you have a condition if(i==num). This condition is never true, so the programme never reaches the return statement. If the programme flow inside a function means that no return statement is ever reached, then the function simply returns undefined. (This isn't javascript-specific - similar rules apply in many languages.)
Instead, you can move the return statement outside of the loop.
function readGround(num) {
var grounds = stg.substr(stg.indexOf('<S>') + 3, stg.lastIndexOf('</S>')),
gr;
for (var i = 1; i < num; i++) {
grounds = grounds.substr(grounds.indexOf('/>') + 2, grounds.length);
}
gr = grounds.substr(grounds.indexOf('<S') + 3, grounds.indexOf('/>'));
var a = [stringBetween(gr, 'L="', '"'), stringBetween(gr, 'H="', '"'), stringBetween(gr, 'X="', '"'), stringBetween(gr, 'Y="', '"')];
return {
L: a[0],
H: a[1],
X: a[2],
Y: a[3]
};
}
(A few other things also had to be tweaked to make your code work, such as that a should read from the section of text in gr rather than from the longer string grounds)
jsfiddle