Extend object wrappers for modern primitives - javascript

Starting from ES6 classes can extend special objects like functions, arrays and primitive's wrappers. It's simple: just write a class that extends corresponding type and use it:
class MyNumber extends Number {
constructor() {
super(42)
}
square() {
return this ** 2
}
}
var x = new MyNumber()
console.log(typeof x, x + "", x.square() + "")
But EcmaScript also has some new types like Symbol and BigInt. They still have a non-primitive wrapper types, but you can't use them as constructor and need to explicitly wrap primitive into an object:
var x = BigInt("42")
var y = Object(x)
console.log(typeof x, x + "", x + 1n + "")
console.log(typeof y, y + "", y + 1n + "")
try {
var z = new BigInt("42")
} catch (e) {
console.log(e.message)
}
What if I want to extend such wrapper? Defining a class works fine, but if I try to create an object, it throws on super call:
class MyBigInt1 extends BigInt {
constructor() {
super("42")
}
}
try {
var x = new MyBigInt1()
} catch (e) {
console.log(e.message)
}
class MyBigInt2 extends BigInt {
constructor() {
}
}
try {
var x = new MyBigInt2()
} catch (e) {
console.log(e.message)
}

From the spec:
The Symbol constructor […] is not intended to be subclassed.
And similarly from the BigInt proposal:
The BigInt constructor is not intended to be used with the new operator or to be subclassed.
Instantiating primitive wrappers is already bad enough, don't go extending them.
A hack would be to not call super() (which you cannot prevent from throwing), but create the object yourself (without new) and then set its prototype to your custom one. Just like you already did it in your answer :-)

I've found a possible solution, but still searching for a better way:
class MyBigInt extends BigInt {
constructor() {
var res = Object(BigInt("42"))
Object.setPrototypeOf(res, new.target.prototype)
return res
}
square() {
return this ** 2n
}
}
var x = new MyBigInt()
console.log(typeof x, x + "", x.square() + "")
And same approach with Symbol
class MySymbol extends Symbol {
constructor(description) {
var res = Object(Symbol(description))
Object.setPrototypeOf(res, new.target.prototype)
return res
}
smth() {
return `(${this.description})`
}
}
var x = new MySymbol("qqq")
console.log(typeof x, x.description, x.smth())
var y = { [x]: 42 }
console.log(y[x], y[x.toString()])

Related

Javascript Create new Number wıth own properties

I want create new number object with own properties from Number.
But when assign a number value to my variable, my variable turn to Number(primitive wrapper object).and loss own properties.How can I prevent this?
Example
class Fnumber extends Number {
value = 0;
[Symbol.toPrimitive](hint) {
if (hint === 'object') {
return this;
}
return this.value;
};
//I don't want use Number.prototype.add method because this effect all Number values.
add = function(...v) {
this.value += Array.prototype.slice.call(arguments).reduce((o, v) => o + v)
}
}
var nmbr = new Fnumber();
nmbr.add(4, 2, 4);
console.log(nmbr); //return a Object
console.log(nmbr + 4); //return 14 is number
nmbr = 14;
console.log(nmbr); //return not a Object its Primative number value
console.log(nmbr + 4); //return 14 a number
nmbr.add(4, 2, 4); //raise error.
When doing nmbr = 14 you assign 14 to nmbr, you are not changing the nmbr.value, you are overwriting the object. Instead call add and use nmbr.value when needed.
class Fnumber extends Number {
value = 0;
add(...v) {
this.value += Array.prototype.slice.call(arguments).reduce((o, v) => o + v)
}
}
var nmbr = new Fnumber();
nmbr.add(4, 2, 4);
console.log(nmbr.value);
nmbr.add(5);
console.log(nmbr.value);
nmbr.value = 25; // assign a value to nmbr.value
console.log(nmbr.value);
If you are not planning on reassigning the object, a good practice is to use const instead of var, see the error below.
class Fnumber extends Number {};
const nmbr = new Fnumber();
nmbr = 14;
I found the solution indirectly.
class Fnumber extends Number {
value = 0;
add(...v) {
this.value += Array.prototype.slice.call(arguments).reduce((o, v) => o + v)
}
}
//temğrary variable description
var _nmbr=new Fnumber();
//Define nmbr variable in global scobe
Object.defineProperty(window,"nmbr",{
enumerable: true,
configurable: true,
get() { return _nmbr; },
set(val) { if (typeof val=="number")_nmbr.value=val; }
});
nmbr=4;
console.log(nmbr+2);//output 6
//still nmbr variable is object,because assigned value to
_nmbr.value with above set method
nmbr.add(4, 2, 4);
console.log(nmbr+2);//output 16

Can I create a class method usable by each property of an created object?

Can I create a method usable by each property of an object?
class myClass {
constructor() {
this.a = 1;
this.b = 2;
this.c = 3;
}
raise(x) {
this += x; //I know this doesn't work. I want the raise function to use
//the value of its caller and increase it by x
};
}
What I want to achieve is being able to call the raise method on any object property via
obj1 = new myClass();
obj1.a.raise(1); //a = 2
obj1.b.raise(3); //b = 5
obj1.c.raise(100); //c = 103
So I want to be able to use this syntax: object.property.method()
I tried creating raise on the class constructor, the class prototype or the Object prototype. I also couldn't find any mention of this elsewhere.
It would be impossible for obj.a to return a number, while also being able to call obj.a.raise to modify it.
If you're open to a small tweak, you could retrieve the value via a method called something like .get, while the underlying value is stored and reassigned in the _a property:
class myClass {
constructor() {
this._a = 1;
this.a = { raise: num => this._a += num, get: () => this._a };
}
}
const obj = new myClass();
obj.a.raise(1); //a = 2
console.log(obj.a.get());
Using ES6 you can do it easily. You may not need complex solution!
Sample is given below.
2nd Solution, to use composition.
class PriceList {
constructor(price, amount) {
this._price = price;
this._amount = amount;
}
get price() {
return this._price;
}
set price(p) {
this._price += p; // validation could be checked here such as only allowing non numerical values
}
get amount() {
return this._amount;
}
set amount(p) {
this._amount += p; // validation could be checked here such as only allowing non numerical values
}
}
const priceList = new PriceList(1, 10);
console.log(priceList); // PriceList { _price: 1, _amount: 10 }
priceList.amount = 10;
priceList.price = 100;
console.log(priceList); // PriceList { _price: 101, _amount: 20 }
class Value {
constructor(v) {
this._val = v;
}
get val() {
return this._val;
}
set(v) {
this._val = v;
}
raise(m) {
this._val += m;
}
}
class PriceList2 {
constructor(price, amount) {
this.amount = new Value(amount);
this.price = new Value(price);
}
}
const priceList2 = new PriceList2(1, 10);
console.log(priceList2);
priceList2.amount.raise(10);
priceList2.price.raise(100);
console.log(priceList2.amount.val); // 20
console.log(priceList2.price.val); // 101
priceList2.amount.val = 1
priceList2.amount.raise(10);
console.log(priceList2.amount.val); //30
.as-console-row {color: blue!important}
For literally all answers (except this one), you need to run a function to get the value, here it's more simplified.
class myClass {
constructor() {
this.a = new Number(1);
this.b = new Number(2);
this.c = new Number(3);
for (let p in this) {
this[p].raise = x => {
let fn = this[p].raise;
this[p] = new Number(x + this[p]);
this[p].raise = fn;
}
}
}
}
let m = new myClass();
m.a.raise(15);
m.a.raise(15);
// A = 1 + 15 + 15 = 31
m.b.raise(10);
m.b.raise(20);
// B = 2 + 10 + 20 = 32
m.c.raise(17);
m.c.raise(13);
// C = 3 + 17 + 13 = 33
console.log(m.a + 1, m.b + 1, m.c + 1) // 32 33 34
// logging just m.a, m.b, m.c will get you an object with a raise function
// only in stack overflow (or maybe other fake consoles), but the browser
// console will definitely work and give you a Number object

How to add chain functions from one class to another?

I am trying to develop a game engine for personal use in JS. I want to have my engine be able to use elements of separate classes. One such problem I am trying to solve is chaining methods from one class (say a math class that chains functions) to my main function.
Here is an example of what I want it to look like:
let game = new Engine()
game.operator(5).sum(3).divide(2)
Here's what it might be in my code, although this is where I am not sure what to do.
class Engine {
constructor() {
//Set up engine here
/* This is where I am unsure how to link the following operator class to this one.
* Do I put it here in constructor or ...
*/
}
/* ... do I put it here? (Or not in this class at all?)
*
* This is what I tried doing as a function
*
* operator(input) {
* let op = new Operator(input);
* }
*/
}
class Operator {
/*The following class is different than the one I am using, but follows similar syntax:
* Basic usage: operator(5).sum(3, 4) => Should output 12
* How the class works is mostly irrelevant, just know it chains methods.
*/
constructor(input) {
this.input = input;
this.answer = this.input;
}
sum() {
let a = arguments[0]
for(var i = 1; i < arguments.length; i++) {
a += arguments[i];
}
this.answer += a;
return this;
}
divide() {
let a = arguments[0];
for(var i = 1; i < arguments.length; i++) {
a *= arguments[i];
}
this.answer /= a;
return this;
}
}
How can I get one class to be able to chain methods from different class?
A pattern for chaining is to have the instance keep a chain state, and to provide a 'value' method that returns the chain state. To chain between two classes, I guess I'd include a special value method that returns an instance of the other class. (To keep the reader oriented, name it something that indicates the type change)...
class ObjectA {
constructor(string) {
this.chainValue = string
this.string = string
}
transformA() {
this.chainValue = this.chainValue.toUpperCase()
return this
}
transformB() {
this.chainValue = this.chainValue + "bar"
return this
}
// the regular value method
get value() {
return this.chainValue
}
// like the value method, but named to explicitly return MyNumber
get numberValue() {
return new MyNumber(this.value.length)
}
}
class MyNumber {
constructor(int) {
this.chainValue = int
this.int = int
}
add(n) {
this.chainValue += n
return this
}
get value() {
return this.chainValue
}
}
let a = new ObjectA("foo")
console.log(
a
.transformB() // append "bar"
.transformA() // convert to upper case
.numberValue // return a number (arbitrarily, the length of the chain state)
.add(12) // add 12
.value // expect 18
)
You can use Proxy for that purpose.
class Engine {
operator() {
// code
console.log('call operator')
}
}
class Operator {
sum() {
// code
console.log('call sum')
}
divide() {
console.log('call divide')
}
}
class SuperOperator {
negate() {
console.log('call negate')
}
}
const operator = new Operator();
const superOperator = new SuperOperator();
const engine = new Engine();
const objectsToChainFrom = [
engine,
operator,
superOperator,
];
// create helper function for proxy
function wrapper(originalMethod, ctx) {
return function () {
originalMethod.apply(ctx, arguments);
// return proxy;
return this;
}
}
var proxy1 = new Proxy(objectsToChainFrom, {
get(target, methodToCall, receiver) {
const objectWithMethod = target.find(el => el[methodToCall]);
return wrapper(objectWithMethod[methodToCall], objectWithMethod)
}
});
proxy1
.sum()
.operator()
.divide()
.negate()

multiple constructor in javascript

I have a question: I was wondering if it is possible to simulate the
multiple constructors, like in Java (yes, I know that the languages are
completely different)?
Let's say that I have a class called "Point" which would have two
values "x" and "y".
Now, let's say if it were the Java version, I would want two
constructors: one that accept two numbers, the other accepts a string:
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(String coord) {
this.x = coord.charAt(0);
this.y = coord.charAt(1);
}
//...
}
//In JavaScript, so far I have
Point = function() {
var x;
var y;
//...
}
Is it possible to have two declarations for the Point.prototype.init?
Is it even possible to have multiple constructors in JavaScript?
You can do this in javascript by testing the number of arguments, or the type of the arguments.
In this case, you can do it by testing the number of arguments:
function Point(/* x,y | coord */) {
if (arguments.length == 2) {
var x = arguments[0];
var y = arguments[1];
// do something with x and y
} else {
var coord = arguments[0];
// do something with coord
}
}
Yes, you can, although not as your expecting. As Javascript is weakly typed, no-one cares or checks what type the arguments that you provide are.
Java requires two different constructors because it is strongly typed and the argument types have to match the method signature, however this isn't the case with JavaScript.
function Point(arg1, arg2) {
if (typeof arg1 === "number" && typeof arg2 === "number") {
// blah
} else if (typeof arg1 === "string" && arguments.length == 1) {
// blah
} else {
throw new Error("Invalid arguments");
}
};
This is inspired from iOS.
class Point {
constructor() {
this.x = 0; // default value
this.y = 0; // default value
}
static initWithCoor(coor) {
let point = new Point();
point.x = coor.x;
point.y = coor.y;
return point;
}
static initWithXY(x,y) {
let point = new Point();
point.x = x;
point.y = y;
return point;
}
}
Just like that, you could have as many initializers as you want without writing lots of if-else.
let p1 = Point.initWithCoor({ x:10, y:20 });
let p2 = Point.initWithXY(10, 20);
Just make one constructor wrap another:
function Point(x,y) {
//make the point and do what the constructor is designed to do
}
function PointStr(str) {
var xp = arguments[0];
var yp = arguments[1];
return new Point(xp, yp);
}

JavaScript override methods

Let's say you have the below code:
function A() {
function modify() {
x = 300;
y = 400;
}
var c = new C();
}
function B() {
function modify(){
x = 3000;
y = 4000;
}
var c = new C();
}
C = function () {
var x = 10;
var y = 20;
function modify() {
x = 30;
y = 40;
};
modify();
alert("The sum is: " + (x+y));
}
Now the question is, if there is any way in which I can override the method modify from C with the methods that are in A and B. In Java you would use the super-keyword, but how can you achieve something like this in JavaScript?
Edit: It's now six years since the original answer was written and a lot has changed!
If you're using a newer version of JavaScript, possibly compiled with a tool like Babel, you can use real classes.
If you're using the class-like component constructors provided by Angular or React, you'll want to look in the docs for that framework.
If you're using ES5 and making "fake" classes by hand using prototypes, the answer below is still as right as it ever was.
JavaScript inheritance looks a bit different from Java. Here is how the native JavaScript object system looks:
// Create a class
function Vehicle(color){
this.color = color;
}
// Add an instance method
Vehicle.prototype.go = function(){
return "Underway in " + this.color;
}
// Add a second class
function Car(color){
this.color = color;
}
// And declare it is a subclass of the first
Car.prototype = new Vehicle();
// Override the instance method
Car.prototype.go = function(){
return Vehicle.prototype.go.call(this) + " car"
}
// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"
var c = new Car("red");
c.go() // "Underway in red car"
Unfortunately this is a bit ugly and it does not include a very nice way to "super": you have to manually specify which parent classes' method you want to call. As a result, there are a variety of tools to make creating classes nicer. Try looking at Prototype.js, Backbone.js, or a similar library that includes a nicer syntax for doing OOP in js.
Since this is a top hit on Google, I'd like to give an updated answer.
Using ES6 classes makes inheritance and method overriding a lot easier:
'use strict';
class A {
speak() {
console.log("I'm A");
}
}
class B extends A {
speak() {
super.speak();
console.log("I'm B");
}
}
var a = new A();
a.speak();
// Output:
// I'm A
var b = new B();
b.speak();
// Output:
// I'm A
// I'm B
The super keyword refers to the parent class when used in the inheriting class. Also, all methods on the parent class are bound to the instance of the child, so you don't have to write super.method.apply(this);.
As for compatibility: the ES6 compatibility table shows only the most recent versions of the major players support classes (mostly). V8 browsers have had them since January of this year (Chrome and Opera), and Firefox, using the SpiderMonkey JS engine, will see classes next month with their official Firefox 45 release. On the mobile side, Android still does not support this feature, while iOS 9, release five months ago, has partial support.
Fortunately, there is Babel, a JS library for re-compiling Harmony code into ES5 code. Classes, and a lot of other cool features in ES6 can make your Javascript code a lot more readable and maintainable.
Once should avoid emulating classical OO and use prototypical OO instead. A nice utility library for prototypical OO is traits.
Rather then overwriting methods and setting up inheritance chains (one should always favour object composition over object inheritance) you should be bundling re-usable functions into traits and creating objects with those.
Live Example
var modifyA = {
modify: function() {
this.x = 300;
this.y = 400;
}
};
var modifyB = {
modify: function() {
this.x = 3000;
this.y = 4000;
}
};
C = function(trait) {
var o = Object.create(Object.prototype, Trait(trait));
o.modify();
console.log("sum : " + (o.x + o.y));
return o;
}
//C(modifyA);
C(modifyB);
modify() in your example is a private function, that won't be accessible from anywhere but within your A, B or C definition. You would need to declare it as
this.modify = function(){}
C has no reference to its parents, unless you pass it to C. If C is set up to inherit from A or B, it will inherit its public methods (not its private functions like you have modify() defined). Once C inherits methods from its parent, you can override the inherited methods.
the method modify() that you called in the last is called in global context
if you want to override modify() you first have to inherit A or B.
Maybe you're trying to do this:
In this case C inherits A
function A() {
this.modify = function() {
alert("in A");
}
}
function B() {
this.modify = function() {
alert("in B");
}
}
C = function() {
this.modify = function() {
alert("in C");
};
C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}
C.prototype = new A();
Not unless you make all variables "public", i.e. make them members of the Function either directly or through the prototype property.
var C = function( ) {
this.x = 10 , this.y = 20 ;
this.modify = function( ) {
this.x = 30 , this.y = 40 ;
console.log("(!) C >> " + (this.x + this.y) ) ;
} ;
} ;
var A = function( ) {
this.modify = function( ) {
this.x = 300 , this.y = 400 ;
console.log("(!) A >> " + (this.x + this.y) ) ;
} ;
} ;
A.prototype = new C ;
var B = function( ) {
this.modify = function( ) {
this.x = 3000 , this.y = 4000 ;
console.log("(!) B >> " + (this.x + this.y) ) ;
} ;
} ;
new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ;
test it here
You will notice a few changes.
Most importantly the call to the supposed "super-classes" constructor is now implicit within this line:
<name>.prototype = new C ;
Both A and B will now have individually modifiable members x and y which would not be the case if we would have written ... = C instead.
Then, x, y and modify are all "public" members so that assigning a different Function to them
<name>.prototype.modify = function( ) { /* ... */ }
will "override" the original Function by that name.
Lastly, the call to modify cannot be done in the Function declaration because the implicit call to the "super-class" would then be executed again when we set the supposed "super-class" to the prototype property of the supposed "sub-classes".
But well, this is more or less how you would do this kind of thing in JavaScript.
HTH,
FK
function A() {
var c = new C();
c.modify = function(){
c.x = 123;
c.y = 333;
}
c.sum();
}
function B() {
var c = new C();
c.modify = function(){
c.x = 999;
c.y = 333;
}
c.sum();
}
C = function () {
this.x = 10;
this.y = 20;
this.modify = function() {
this.x = 30;
this.y = 40;
};
this.sum = function(){
this.modify();
console.log("The sum is: " + (this.x+this.y));
}
}
A();
B();

Categories

Resources