multiple constructor in javascript - 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);
}

Related

Extend object wrappers for modern primitives

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()])

How to create a Pair class that's equal in Set when each entry equals?

class Pair {
constructor(x, y) {
this.x = x
this.y = y
}
// TODO ?
}
const set = new Set()
set.add(new Pair(1, 2))
How to make follow statement to return true?
set.has(new Pair(1, 2)) // -> false
(We can override equals and hashCode of Pair class in Java)
The references will only be equal if they point to the same value in memory - new will (by default) create a new object, which cannot be === to a separate one. I suppose one option would be to return an existing x,y Pair instance if it's been constructed earlier:
const pairs = {};
function getMakePair(x, y) {
const str = x + ',' + y;
if (pairs[str]) {
return pairs[str];
}
const pair = new Pair(x, y);
pairs[str] = pair;
return pair;
}
class Pair {
constructor(x, y) {
this.x = x
this.y = y
}
}
const set = new Set()
set.add(getMakePair(1, 2))
console.log(set.has(getMakePair(1, 2)))
Or, if you wanted all action to be in the constructor, you can have the constructor explicitly create / return the instance, though it's a bit less clear IMO, and it's kind of odd to see:
class Pair {
constructor(x, y) {
const str = x + ',' + y;
if (Pair.pairs[str]) {
return Pair.pairs[str];
}
const instance = Object.create(Pair.prototype);
instance.x = x
instance.y = y
Pair.pairs[str] = instance;
return instance;
}
}
Pair.pairs = {};
const set = new Set()
set.add(new Pair(1, 2))
console.log(set.has(new Pair(1, 2)))
Of course, if you use a method like this to cache instances, you shouldn't mutate the .x / .y property of the instance, else the caching mechanism will harder to reason about. If you want that sort of mutable caching functionality, you might consider using .find on an array of instances instead.
Overriding the equality check is infeasible right now: https://stackoverflow.com/a/29759699/7803502
Instead, you can extend the Set class to create your own PairSet.
class Pair {
constructor(x, y) {
this._x = x
this._y = y
}
toString() {
return [
this._x,
this._y,
].join(',')
}
}
class PairSet extends Set {
add(pair) {
return super.add(pair.toString())
}
has(pair) {
return super.has(pair.toString())
}
delete(pair) {
return super.delete(pair.toString())
}
}
(function test() {
const set = new PairSet()
set.add(new Pair(1, 2))
console.log(set.has(new Pair(1, 2))) // true
console.log(set.has(new Pair(2, 1))) // false
})()

Handle nested properties of Object in JS

Hi I'm writing a module in NodeJS in a OOP style.
I have multiples simples objects that contains primitive data and multiple complex objects that contain other objects.
const Simple = function Simple() {
this.x = 0;
this.y = 0;
}
Simple.prototype.getArea = function() {
return this.x * this.y;
}
const Complex = function Complex() {
this.ownProp = 0;
this.nestedProp = new Simple();
this.otherNestedProp = new otherSimple();
}
Complex.prototype.set = function(key, value) {
this[key] = value;
}
Complex.prototype.otherSet = function(value) {
Object.assign(this, value);
}
My problem is that users who will use my API can break things by doing this:
let simple = new Simple();
simple.getArea(); // 0
let complex = new Complex();
complex.nestedProp.getArea(); // 0
complex.set('nestedProp', {x: 5, y: 6});
complex.nestedProp.getArea(); // THROW <----
let complex = new Complex();
complex.nestedProp.getArea(); // 0
complex.set({nestedProp: {x: 5, y: 6});
complex.nestedProp.getArea(); // THROW <----
Is there a lodash function to only assign values of such nested Object.
Or is there a good way to manage this kind of problems?
Note: I could check for instanceof but I have a lot of modules, and I don't want to manage each specific case.
It seems you think passing something like {x: 1, y:2} to Complex.set will magically make x and y end inside of Simple. I think you are confused about how Javascript works, no offense meant.
Here's an implementation that would make things work roughly the way you seem to want.
const Simple = function Simple() {
this.x = 0;
this.y = 0;
}
Simple.prototype.getArea = function() {
return this.x * this.y;
}
Simple.prototype.set = function (x, y) {
this.x = x;
this.y = y;
}
const Complex = function Complex() {
this.nestedProp = new Simple();
}
Complex.prototype.set = function(props) {
this.nestedProp.set(props.x, props.y);
}
let complex = new Complex();
complex.nestedProp.getArea(); // 0
complex.set({x: 5, y: 6});
complex.nestedProp.getArea(); // 30
The properties x and y are passed explicitly from Complex to Simple until they end where they should. You can either pass x and y as separate parameters (see Simple's set) or as properties of an object (see Complex's set).
But if you thought x and y would make it all the way to the end by themselves you need to study basic OOP before writing code; again, no offense meant.

Is there any way to make a function's return accessible via a property?

I'm a JS dev, experimenting with functional programming ideas, and I'm wondering if there's anyway to use chains for synchronous functions in the way the promise chains are written.
For example:
function square (num) {
return num * num;
}
let foo = 2
let a = square(foo) //=> 4
let b = square(square(foo)) //=> 16
Fair enough, but what I'd like to do (often to make code parsing easier) is to chain together those methods by passing that in as the first parameter of a chain. So that something like this would work:
let c = square(foo)
.square()
.square() //=> 256
Is there any way to do this with vanilla javascript, or is this something I'd have to modify the Function.prototype to do?
You might be interested in the Identity functor – it allows you to lift any function to operate on the Identity's value – eg, square and mult below. You get a chainable interface without having to touch native prototypes ^_^
const Identity = x => ({
runIdentity: x,
map: f => Identity(f(x))
})
const square = x => x * x
const mult = x => y => x * y
let result = Identity(2)
.map(square)
.map(square)
.map(square)
.map(mult(1000))
.runIdentity
console.log(result)
// 256000
It is really a bad idea to modify Function.prototype or Number.prototype because you will pollute the default JavaScript objects, say: what if other framework also do the evil and add their own square?
The recommended way is to make an object by your self.
function num(v) {
this.v = v;
this.val = function() { return this.v; };
this.square = function() { this.v = this.v * this.v; return this; };
//and you can add more methods here
this.sqrt = function() { this.v = Math.sqrt(this.v); return this; };
return this;
}
var n = new num(2)
console.log(n.square().square().sqrt().val());
You wouldn't have to modify Function.prototype, but Number.prototype. You're trying to create a new method that acts on a number, not on a function. This does what you're trying to do:
Number.prototype.square = function() {
return this * this;
}
let x = 4;
let y = x.square().square(); // -> 256
You can set square and num as a property of square call`
function square (num) {
if (!this.square) {
this.square = square;
this.num = num || 0;
};
if (num === undefined) {
this.num *= this.num
}
else if (!isNaN(num)) {
this.num *= num;
};
return this;
}
let foo = 2;
let c = new square(foo).square().square();
console.log(c.num);

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