how to call method of abstract class in javascript - javascript

I have an abstract class that implements some methods on its prototype and I want to create an instance directly of this class without subclassing it.
I can instantiate that class by creating a Proxy and trapping construct and it seems to work. Properties of the new instance are set correctly but I have a hard time calling its methods.
function AbstractNumbers(...args) {
if (new.target === AbstractNumbers) {
throw new Error('Cannot instantiate abstract class');
}
this.numbers = args;
}
AbstractNumbers.prototype.showNumbers = function() { console.log(this.numbers); }
const AbstractNumbersProxy = new Proxy(AbstractNumbers, {
construct(target, args) {
// change 3rd argument to bypass new.target test
return Reflect.construct(target, args, function() {});
}
});
const n = new AbstractNumbersProxy(1, 2, 3);
// set prototype back to AbstractNumbers
Object.setPrototypeOf(n, AbstractNumbers);
// n.__proto__ shows the correct prototype
console.log(n.__proto__);
// property n.numbers is set correctly
console.log(n.numbers);
// calling its prototype method fail
n.showNumbers();
How can I properly instantiate that abstract class so that I am able to call its methods?

In
// set prototype back to AbstractNumbers
Object.setPrototypeOf(n, AbstractNumbers);
you've set the prototype back to the constructor function instead of its prototype property. Try
Object.setPrototypeOf(n, AbstractNumbers.prototype);
instead:
function AbstractNumbers(...args) {
if (new.target === AbstractNumbers) {
throw new Error('Cannot instantiate abstract class');
}
this.numbers = args;
}
AbstractNumbers.prototype.showNumbers = function() { console.log(this.numbers); }
const AbstractNumbersProxy = new Proxy(AbstractNumbers, {
construct(target, args) {
// change 3rd argument to bypass new.target test
return Reflect.construct(target, args, function() {});
}
});
const n = new AbstractNumbersProxy(1, 2, 3);
// set prototype back to AbstractNumbers
Object.setPrototypeOf(n, AbstractNumbers.prototype);
// n.__proto__ shows the correct prototype
console.log(n.__proto__);
// property n.numbers is set correctly
console.log(n.numbers);
// calling its prototype method fail
n.showNumbers();
Please do not ask me to investigate what you are doing.

Related

Should we declare methods inside or outside the constructor in a class definition in JavaScript?

I am confused should I declare methods inside or outside the constructor method in a class definition?
How does it help to declare to methods inside the constructor function in a class definition?
What is the disadvantage of declaring methods outside the constructor function?
class animal {
constructor(name, type, color){
this.name = name;
this.type = type;
this.color = color;
this.sound = () => {console.log(this.name,this.type,this.color)}
}
}
class animal {
constructor(name, type, color){
this.name = name;
this.type = type;
this.color = color;
}
sound = () => {console.log(this.name,this.type,this.color)}
}
Creating objects in either way, results in the similar objects. What am I missing?
The definition of functions inside the constructor was mostly used to store and access private variables in calls like this:
function MyClass() {
let privateVar = { something: "usefull" };
this.getPrivateVar = function getPrivateVar() { return privateVar; };
}
new MyClass().getPrivateVar(); // { something: "usefull" }
Just because calls produce a similar result, doesn't mean that they have an equal behavior. They can act completly differently depending on how they get called or how they where defined.
This counts especially for arrow functions as they don't have a this binding. They access the closest this providing environment, when defined.
class MyClass {}
MyClass.prototype.arrow = () => this;
console.log(new MyClass().arrow() == globalThis); // true
The answers from How does JavaScript .prototype work? explain pretty well how prototype is working and for classes every property and function defined inside it, will be stored on constructor.prototype, with some exceptions like the static and #private properties.
Private properties are only available and accessible inside the class and not in an inherited or an extended class and can't be accessed by a classic prototype.
class MyClass {
#private = "example";
doSomethingWithMyPrivate() { return this.#private }
}
MyClass.prototype.getThePrivate = function() { return this.#private };
new MyClass().doSomethingWithMyPrivate(); // example
// Both produce the same SyntaxError:
// Private field '#private' must be declared in an enclosing class
new MyClass().#private;
new MyClass().getThePrivate();
Static properties will be placed on the constructor directly.
class MyClass {
static doSomething() { console.log("Okay!") }
}
MyClass.doSomething(); // Okay!
new MyClass().doSomething(); // TypeError: doSomething is not a function
Every instance of a class will access the prototype references or the inheriting objects prototype until any object has a match with the property-name, if not it returns undefined or throw a more specific error depending on the situation.
From this answer:
Javascript has a mechanism when looking up properties on Objects which is called 'prototypal inheritance', here is what is basically does:
First is checked if the property is located on the Object itself. If so this property is returned.
If the property is not located on the object itself it will 'climb up the protochain'. It basically looks at the object referred to by the proto property. There it checks if the property is available on the object referred to by proto
If the property isn't located on the proto object it will climb up the proto chain all the way up to Object object.
If it cannot find the property nowhere on the object and its prototype chain it will return undefined.
So if you define the functions inside the constructor, each instance gets a new function assigned (they won't have the same reference). Which means, depending on the class (new animal).sound == (new animal).sound, the statement will return true (your 2nd example) or false (your 1st example) and the prototypal inheritance will take place or not.
// the old way
function Foo() {
if (this instanceof Foo) {
this.fx = () => {};
} else throw new TypeError(`Constructor Foo cannot be invoked without 'new'`);
}
Foo.prototype.pfx = function() {};
console.log((new Foo).fx, (new Foo).fx == (new Foo).fx); // arrow ƒ, false
console.log((new Foo).pfx, (new Foo).pfx == (new Foo).pfx); // ƒ, true
// the class way
class Bar {
constructor() { // similar too "ƒ Foo() {}" with the addition of super ƒ
this.fx = () => {};
}
// this is almost like Bar.prototype.pfx = ...
pfx() {}
}
// works like before
Bar.prototype.moo = function moo() {};
console.log((new Bar).fx, (new Bar).fx == (new Bar).fx); // arrow ƒ, false
console.log((new Bar).pfx, (new Bar).pfx == (new Bar).pfx); // ƒ, true
console.log((new Bar).moo, (new Bar).moo == (new Bar).moo); // ƒ, true
If you define properties as mentioned in your example #1, you can "override" other methods and this may cause trouble with private properties as mentioned above.
class Foo {
constructor() {
// just for the example
this.fx = Foo.prototype.fx;
}
fx() { console.log("I am broken"); };
}
class Bar extends Foo {
fx() { console.log("I fix something"); }
}
class Moo extends Bar {
constructor() {
super();
delete this.fx;
}
}
/*
* If instance has no own property called fx the default
* behavior will take place:
* Test the construtor.prototype if it has one and execute this.
* If not, look at the inheriting classes in descending order
* (Moo > Bar > Foo > Object)
* But bar.fx() => has an own property called fx which takes place
*/
let bar = new Bar();
bar.fx(); // I am broken
// none of the inheriting classes has a toJSON ƒ,
// so Object.prototype.toJSON will be called.
console.log(bar.toJSON == Object.prototype.toJSON) // true
// the Moo constructor deletes the own property after the Foo constructor was evaluated, so Bar.prototype.fx takes place
new Moo().fx() // I fix something

How to wrap object being constructed with Proxy inside constructor?

I understand that Proxy can be used to alter object-level behaviors, such as bracket notation get and set. All the examples I can find show constructing an object and then wrapping it with a Proxy call. Is there a way to define a class Foo, using ES6 class constructor notation, such that the returned object from the constructor is already wrapped in Proxy, rather than the caller of the constructor having to also call Proxy separately?
Thanks in advance.
If I understand your question properly, what you want to do, is in the constructor return a new proxy like this:
class MyClass {
constructor() {
return new Proxy(this, {
// Proxy settings here
})
}
}
Here in this example, we create a new class, then call a few properties. The proxy will then just print out the properties that were called for simplicity.
class MyClass {
constructor() {
return new Proxy(this, {
get: (target, key) => {
console.log('I am the key: ' + key)
return Reflect.get(target, key)
}
})
}
}
let c = new MyClass
c.awesome
c.billy
c.superTroopers
if (c instanceof MyClass) {
console.log('I am an instance of MyClass')
} else {
console.log('I am not an instance of MyClass')
}
As far as I know: no, but you can set the prototype afterwards. Something like this:
class Thing {
constructor() {
// ...
}
}
Thing.prototype = new Proxy(Thing.prototype, {
get(target, name) {
// ...
}
});

Is it possible to inherit from built-in objects?

I've tried different patterns for implementing inheritance in Javascript, but none of them seem to work with built-in objects such as Set. For instance, using the method described in MDN:
function Test() {
Set.apply(this, arguments);
}
Test.prototype = Object.create(Set.prototype, {});
Test.prototype.constructor = Test;
var test = new Test(["a", "b"]);
yields the following error:
Uncaught TypeError: Constructor Set requires 'new'
at Test.Set (native)
at new Test (<anonymous>:2:9)
at <anonymous>:1:9
This makes sense, since my derived object does not contain the native Set implementation. Is there a pattern that would support these operations, other than making a complete wrapper?
You need to use extends and have a call to super, something like:
class mySet extends Set {
constructor (iterable, name) {
super(iterable);
this.name = name;
}
// Read a Set property
howMany () {
return this.size;
}
// Call a Set method
showEm () {
this.forEach(v=>console.log(v));
}
// Add your own methods
// ...
}
var aSet = new mySet([0,1,2,3], 'aSet');
console.log(aSet.name); // aSet
console.log(aSet.howMany()); // 4
aSet.showEm(); // 0 1 2 3
// Call set method directly
console.log(aSet.has(3)); // true
I guess is asking for Set constructor.
Try this,
function Set(){
// init stuff
}
function Test() {
return new Set.apply(this, arguments);
}
Test.prototype = Object.create(Set.prototype, {});
Test.prototype.constructor = Test;
var test = new Test(["a", "b"]);

How to extend an object literal class with Ecmascript 6 classes

I have a base class defined as an object literal, with methods defined inside constructor's scope. Like this:
var NodeMappingAbstract = function NodeMappingAbstract() {
this.mapToContent = function (obj) {
throw new Error('Not implemented');
};
this.getInfo = function (data) {
throw new Error('Not implemented');
};
this.normalizeUri = function normalizeUri(uri) {
return uriNormalizer.normalize(uri);
};
};
How do I extend theNodeMappingAbstract with ES6 class syntax? I attempted using extends and super():
class SomeMapping extends NodeMappingAbstract{
constructor(){
super();
}
mapToContent(rawElement) {
console.warn('mapContent called');
return rawElement;
}
}
But when I instantiate it and call mapToContent like this:
let m = new SomeMapping();
m.mapToContent();
I get Error: Not implemented, which means the implementation of the NodeMappingAbstract is used.
The problem here is that you are attaching the methods directly on an instance of NodeMappingAbstract (this), and not the prototype of the function.
When JavaScript performs a lookup for an object property it first inspects the objects immediate own properties, which are what are made available when you assign something to this. Only if it does not find that property immediately available in this way will it turn to the prototype. So, although you are extending the NodeMappingAbstract and defining methods on the prototype (via the class sugar), you are not actually overriding the properties that are assigned to this on instantiation inside NodeMappingAbstract.
So, move the method declarations outside of the function body and onto its prototype:
var NodeMappingAbstract = function NodeMappingAbstract () {};
NodeMappingAbstract.prototype.mapToContent = function (obj) {
throw new Error('Not implemented');
};
NodeMappingAbstract.prototype.getInfo = function (data) {
throw new Error('Not implemented');
};
NodeMappingAbstract.prototype.normalizeUri = function normalizeUri (uri) {
return uriNormalizer.normalize(uri);
};
You should put your methods on the prototype of your ES5 class (as you always should).
If you define them in the constructor, and create them as instance properties (which shadow anything inherited from the prototype), you will have to do the same when overwriting them:
class SomeMapping extends NodeMappingAbstract{
constructor(){
super();
this.mapToContent = function(rawElement) {
console.warn('mapContent called');
return rawElement;
};
}
}

How to define private constructors in javascript?

I have defined pure objects in JS which expose certain static methods which should be used to construct them instead of the constructor. How can I make a constructor for my class private in Javascript?
var Score = (function () {
// The private constructor
var Score = function (score, hasPassed) {
this.score = score;
this.hasPassed = hasPassed;
};
// The preferred smart constructor
Score.mkNewScore = function (score) {
return new Score(score, score >= 33);
};
return Score;
})();
Update: The solution should still allow me to test for x instanceof Score. Otherwise, the solution by #user2864740 of exposing only the static constructor works.
One can use a variable (initializing) inside a closure which can throw an error if the constructor was called directly instead of via a class method:
var Score = (function () {
var initializing = false;
var Score = function (score, hasPassed) {
if (!initializing) {
throw new Error('The constructor is private, please use mkNewScore.');
}
initializing = false;
this.score = score;
this.hasPassed = hasPassed;
};
Score.mkNewScore = function (score) {
intializing = true;
return new Score(score, score >= 33);
};
return Score;
})();
Is there a solution which will allow me to say x instanceof Score?
Yes. Conceptually, #user2864740 is right, but for instanceof to work we need to expose (return) a function instead of a plain object. If that function has the same .prototype as our internal, private constructor, the instanceof operator does what is expected:
var Score = (function () {
// the module API
function PublicScore() {
throw new Error('The constructor is private, please use Score.makeNewScore.');
}
// The private constructor
var Score = function (score, hasPassed) {
this.score = score;
this.hasPassed = hasPassed;
};
// Now use either
Score.prototype = PublicScore.prototype; // to make .constructor == PublicScore,
PublicScore.prototype = Score.prototype; // to leak the hidden constructor
PublicScore.prototype = Score.prototype = {…} // to inherit .constructor == Object, or
PublicScore.prototype = Score.prototype = {constructor:null,…} // for total confusion :-)
// The preferred smart constructor
PublicScore.mkNewScore = function (score) {
return new Score(score, score >= 33);
};
return PublicScore;
}());
> Score.mkNewScore(50) instanceof Score
true
> new Score
Error (…)
Simply don't expose the constructor function. The core issue with the original code is the "static method" is defined as a property of the constructor (which is used as a "class") as opposed a property of the module.
Consider:
return {
mkNewScore: Score.mkNewScore
// .. and other static/module functions
};
The constructor can still be accessed via .constructor, but .. meh. At this point, might as well just let a "clever user" have access.
return {
mkNewScore: function (score) {
var s = new Score(score, score >= 33);
/* Shadow [prototype]. Without sealing the object this can
be trivially thwarted with `del s.constructor` .. meh.
See Bergi's comment for an alternative. */
s.constructor = undefined;
return s;
}
};
In order to create a private constructor in JS, I like to create a private key that is only accessible in the class (function) file and provide a static factory function as the only allowed way to construct said class:
// in PrivateConstructorClass.js
// Use a Symbol as this will always be unique.
// If you don't have Symbol in your runtime,
// use a random string that nobody can reliably guess,
// such as the current time plus some other random values.
const PRIVATE_CONSTRUCTOR_KEY = Symbol()
class PrivateConstructorClass {
constructor(arg1, arg2, argN, constructorKey) {
if (constructorKey !== PRIVATE_CONSTRUCTOR_KEY) {
throw new Error('You must use the PrivateConstructorClass.create() to construct an instance.')
}
this.arg1 = arg1
this.arg2 = arg2
this.argN = argN
}
static create(arg1, arg2, argN) {
return new PrivateConstructorClass(arg1, arg2, argN, PRIVATE_CONSTRUCTOR_KEY)
}
}
// From Another JS File:
try {
const myFailedInstanceA = new PrivateConstructorClass('foo', 123, {
size: 'n'
})
} catch (err) {
console.error('Failed:', err.message)
}
const myFactoryInstance = PrivateConstructorClass.create('foo', 123, {
size: 'n'
})
console.log('Success:', myFactoryInstance)
Another possible simple approach is to use predicate function instead of instanceof. For typescript it can be a type guard and type synonym instead of a class can be exported:
// class is private
class _Score {
constructor() {}
}
export type Score = _Score
export function isScore(s): s is Score {
return s instanceof _Score
}
So to be fair the simplest answer is usually the best. An object literal is always a single instance. Not much reason for anything more complex other than, perhaps allocation of memory on demand.
That being said, here is a classical implementation of a singleton using ES6.
The instance "field" is "private". This really means we hide the instance as a property of the constructor. Somewhere not Constructor.prototype, which will be available to the instance through prototipical inheritance.
The constructor is "private". We really are just throwing an error when the caller is not the static getInstance method.
Also of note. It’s important to understand what the keyword this means in different contexts.
In the constructor, this points to the instance being created.
In the static getInstance method, this points to the left of the dot, Universe constructor function which, is an object like most things in JS and can hold properties.
class Universe {
constructor() {
if (!((new Error).stack.indexOf("Universe.getInstance") > -1)) {
throw new Error("Constructor is private. Use static method getInstance.");
}
this.constructor.instance = this;
this.size = 1;
}
static getInstance() {
if (this.instance) {
return this.instance;
}
return new this;
}
expand() {
this.size *= 2;
return this.size;
}
}
//console.log(Universe.getInstance())
//console.log(Universe.getInstance().expand())
//console.log(Universe.getInstance())
//console.log(new Universe())
const getInstance= () => { console.log('hi');
console.log("From singleton: ", Universe.getInstance()); return new Universe() };
console.log(getInstance())

Categories

Resources