js get chain of constructor names - javascript

I generate a chain of constructor names based on inheritance:
class Person {
}
class User extends Person {
}
$userClassIdentifier = generateIdentifier(User.constructor)
// userClassIdentifier = 'Person.User'
function generateIdentifier(constructor) {
if (constructor.prototype && constructor.prototype.__proto__
&& constructor.prototype.__proto__.constructor && constructor.prototype.__proto__.constructor.name) {
return `${constructor.prototype.__proto__.constructor.name}.${constructor.name}`
}
return constructor.name
}
this works for one level of inheritance. But it seems very hacky/ugly. Is there a nicer way to do it and to make it work for an unknown number of levels deep?

If I have SUV extending Car extending Vehicle I expect 'Vehicle.Car.SUV'
I'd use getPrototypeOf and a loop, starting with the constructor you want to start with, and ending when that constructor's prototype is Function.prototype or null:
function getLineage(ctor) {
let name = ctor.name;
while ((ctor = Object.getPrototypeOf(ctor)) != Function.prototype && ctor) {
name = ctor.name + "." + name;
}
return name;
}
class Vehicle { }
class Car extends Vehicle { }
class SUV extends Car { }
console.log(getLineage(SUV));
One suggestion I'd make, though, would be to use a different separator rather than ., as . seems to suggest that (say) SUV is a property of Car, which of course it isn't. :-)

Related

Way to test which "sibling class" an object is in Javascript?

Say I have a base class "Pet" and child classes "Dog" and "Cat". Is there a generic way I can take an arbitrary object I know is a "Pet" and test to see if it is "Dog" or "Cat"?
You can either ask the object for its constructor.name property, or if you have a reference to the class, you can use the instanceof operator:
class Pet {
constructor(name) {
this.name = name;
}
}
class Cat extends Pet {
sayHello() { console.log(`${this.name} miaows`) }
}
class Dog extends Pet {
sayHello() { console.log(`${this.name} barks`) }
}
const goofy = new Dog('Pluto');
const garfield = new Cat('Garfield');
console.log(goofy.constructor.name);
console.log(garfield.constructor.name);
console.log(goofy instanceof Dog);
console.log(garfield instanceof Cat);
Please note that Pet shouldn't know about the existence of Dog or Cat (and also neither of the latter two should know anything about the existence of the other).

Javascript - should i use inheritance model where i require a specific interface?

I'm in a bit of a dilemma. I am using ES6 classes for building a simple class hierarchy. One base class with a method, 3 subclasses that override that method. At some some point, i am traversing a list of instances where i call this method. Technically i don't need the class hierarchy if i can on the other hand simply guarantee that all my objects (any kind of objects) provide a method with that name. Which is the better approach?
class Base {
constructor() {
}
method() {
console.log('base')
}
}
class Subclass1 extends Base{
constructor() {
super()
}
method() {
console.log('sc1')
}
}
class Subclass2 extends Base{
constructor() {
super()
}
method() {
console.log('sc2')
}
}
class Subclass3 extends Base {
constructor() {
super()
}
method() {
console.log('sc3')
}
}
classInstances = [new Subclass1(), new Subclass2(), new Subclass3()];
classInstances.forEach(instance => {
instance.method();
})
// or using any kind of objects...
obj1 = {
method() {
console.log('obj1');
}
}
obj2 = {
method() {
console.log('obj2');
}
}
obj3 = {
method() {
console.log('obj3');
}
}
objectInstances = [obj1, obj2, obj3];
objectInstances.forEach(instance => {
instance.method();
})
Yes, just program to the interface.
You are not using Base.prototype.method anywhere, it's pretty pointless - so drop it. And then Base is just an empty class, pretty much equivalent to Object, so pointless as well. You are not actually using any inheritance features here, do you don't need a class hierarchy either.
A test for x instanceof Base is the only use case left, but then again typeof x.method == "function" is much easier and more flexible.
Javascript uses 'prototypal inheritance', rather than the classical inheritance of other object orientated languages. It's a reasonable approach to test whether an object is based on a given prototype, but given that javascript objects can be freely 'patched' to override the prototype, or even mutate the prototype, you need to be careful about what you assume about an object.
If you expect a given method to exist, then it might be more reliable to test specifically for that method, but sometimes that doesn't read so well in code.
One problem you'll encounter is creating 'base' classes that define a type but which has no default behaviour. For example, here we could create a base type for Animal or Speakable, but the speak method would need to be a no-op or throw an error if ever called. That might of course be useful during development for catching when you fail to define an expected behaviour.
Also, Javascript doesn't allow multiple inheritance, so you can't implement 'interfaces' as you might in other languages, although you can create composite prototypes that achieve a similar result.
class Thing {
constructor(name) {
this.name = name
}
}
class Dog extends Thing {
speak() {
return 'woof';
}
}
class Cat extends Thing {
speak() {
return 'meow';
}
}
class House extends Thing {
}
const things = [new Dog('Rover'), new Cat('Tibbles'), new House('Foo Hall')];
things.forEach(thing => {
if (typeof thing.speak === 'function') {
console.log(`${thing.name} says '${thing.speak()}'`);
} else {
console.log(`${thing.name} cannot speak`);
}
});

javaScript - multiple inheritance in ES6,

I'm trying to inherit class "EventEmitter" and a pre defined class "Person", here is the code
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduces() {
return `My name is ${this.name}. I am ${this.age} years old.`;
}
};
\\here comes the mixin part
function mix(...mixins) {
class Mix {}
for (let mixin of mixins) {
copyProperties(Mix, mixin);
copyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" && key !== "prototype" && key !== "name") {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
I intend to create a new class 'PersonWithEmitter', and still call the constructor like below:
class PersonWithEmitter extends mix(Person,EventEmitter){
constructor(name,age){
super(name,age)
\\do something else
}
Here comes the issue, when I create a new instance of 'PersonWithEmitter' like this let someOne = new PersonWithEmitter("Tom",21), will not get what I what, In the new class, I want to use this.name and this.age, which is still undefined.
So how can I change my code, So the new class can both have its parent's methods and only class "Person"'s constructor?
Pardon me for my broken English.
In many cases multiple inheritance in JavaScript indicates wrong design decision. It may result in hacky objects that don't behave as they should. The way it is done should always be determined by particular objects. In some cases a shallow copy of own properties is needed, in another the entire prototype chain should be traversed. Composition over inheritance is often a better choice.
The problem in the code above is that class constructors are not called. Mix has empty constructor. This is the reason why PersonWithEmitter doesn't work as expected.
Multiple constructor function calls can generally be stacked like:
function Foo(...args) {
let _this = this;
_this = Bar.apply(_this, args);
_this = Baz.apply(_this, args);
return _this;
}
This won't work if Bar or Baz is ES6 class because it contains a mechanism that prevents it from being called without new. In this case they should be instantiated:
function Foo(...args) {
copyProperties(this, new Bar(...args));
copyProperties(this, new Baz(...args));
}
Static and prototype properties may also be copied to Foo like is shown in code above.
If the case is narrowed down to Node.js EventEmitter, it can be handled like a special case. Its implementation is certain and stable. It is already known that EventEmitter does initialization in constructor, it has a shallow prototype chain and property descriptors. So it likely should be:
class Foo extends Bar {
constructor(...args) {
super(...args)
EventEmitter.call(this);
// or
// EventEmitter.init.call(this);
}
copyProperties(Foo.prototype, EventEmitter.prototype);

ES6: Why do extending classes need to explicitly call super? [duplicate]

Is it possible to extend a class in ES6 without calling the super method to invoke the parent class?
EDIT: The question might be misleading. Is it the standard that we have to call super() or am I missing something?
For example:
class Character {
constructor(){
console.log('invoke character');
}
}
class Hero extends Character{
constructor(){
super(); // exception thrown here when not called
console.log('invoke hero');
}
}
var hero = new Hero();
When I'm not calling super() on the derived class I'm getting a scope problem -> this is not defined
I'm running this with iojs --harmony in v2.3.0
The rules for ES2015 (ES6) classes basically come down to:
In a child class constructor, this cannot be used until super is called.
ES6 class constructors MUST call super if they are subclasses, or they must explicitly return some object to take the place of the one that was not initialized.
This comes down to two important sections of the ES2015 spec.
Section 8.1.1.3.4 defines the logic to decide what this is in the function. The important part for classes is that it is possible for this be in an "uninitialized" state, and when in this state, attempting to use this will throw an exception.
Section 9.2.2, [[Construct]], which defines the behavior of functions called via new or super. When calling a base class constructor, this is initialized at step #8 of [[Construct]], but for all other cases, this is uninitialized. At the end of construction, GetThisBinding is called, so if super has not been called yet (thus initializing this), or an explicit replacement object was not returned, the final line of the constructor call will throw an exception.
The new ES6 class syntax is only an other notation for "old" ES5 "classes" with prototypes. Therefore you cannot instantiate a specific class without setting its prototype (the base class).
Thats like putting cheese on your sandwich without making it. Also you cannot put cheese before making the sandwich, so...
...using this keyword before calling the super class with super() is not allowed, too.
// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
constructor() {
super();
this.supplement = "Cheese";
}
}
// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
super();
}
}
// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
}
}
If you don’t specify a constructor for a base class, the following definition is used:
constructor() {}
For derived classes, the following default constructor is used:
constructor(...args) {
super(...args);
}
EDIT: Found this on developer.mozilla.org:
When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.
Source
There have been multiple answers and comments stating that super MUST be the first line inside constructor. That is simply wrong. #loganfsmyth answer has the required references of the requirements, but it boil down to:
Inheriting (extends) constructor must call super before using this and before returning even if this isn't used
See fragment below (works in Chrome...) to see why it might make sense to have statements (without using this) before calling super.
'use strict';
var id = 1;
function idgen() {
return 'ID:' + id++;
}
class Base {
constructor(id) {
this.id = id;
}
toString() { return JSON.stringify(this); }
}
class Derived1 extends Base {
constructor() {
var anID = idgen() + ':Derived1';
super(anID);
this.derivedProp = this.baseProp * 2;
}
}
alert(new Derived1());
You can omit super() in your subclass, if you omit the constructor altogether in your subclass. A 'hidden' default constructor will be included automatically in your subclass. However, if you do include the constructor in your subclass, super() must be called in that constructor.
class A{
constructor(){
this.name = 'hello';
}
}
class B extends A{
constructor(){
// console.log(this.name); // ReferenceError
super();
console.log(this.name);
}
}
class C extends B{} // see? no super(). no constructor()
var x = new B; // hello
var y = new C; // hello
Read this for more information.
The answer by justyourimage is the easiest way, but his example is a little bloated. Here's the generic version:
class Base {
constructor(){
return this._constructor(...arguments);
}
_constructor(){
// just use this as the constructor, no super() restrictions
}
}
class Ext extends Base {
_constructor(){ // _constructor is automatically called, like the real constructor
this.is = "easy"; // no need to call super();
}
}
Don't extend the real constructor(), just use the fake _constructor() for the instantiation logic.
Note, this solution makes debugging annoying because you have to step into an extra method for every instantiation.
Just registered to post this solution since the answers here don't satisfy me the least since there is actually a simple way around this. Adjust your class-creation pattern to overwrite your logic in a sub-method while using only the super constructor and forward the constructors arguments to it.
As in you do not create an constructor in your subclasses per se but only reference to an method that is overridden in the respective subclass.
That means you set yourself free from the constructor functionality enforced upon you and refrain to a regular method - that can be overridden and doesn't enforce super() upon you letting yourself the choice if, where and how you want to call super (fully optional) e.g.:
super.ObjectConstructor(...)
class Observable {
constructor() {
return this.ObjectConstructor(arguments);
}
ObjectConstructor(defaultValue, options) {
this.obj = { type: "Observable" };
console.log("Observable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class ArrayObservable extends Observable {
ObjectConstructor(defaultValue, options, someMoreOptions) {
this.obj = { type: "ArrayObservable" };
console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class DomainObservable extends ArrayObservable {
ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
this.obj = super.ObjectConstructor(defaultValue, options);
console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");
cheers!
#Bergi mentioned new.target.prototype, but I was looking for a concrete example proving that you can access this (or better, the reference to the object the client code is creating with new, see below) without having to call super() at all.
Talk is cheap, show me the code... So here is an example:
class A { // Parent
constructor() {
this.a = 123;
}
parentMethod() {
console.log("parentMethod()");
}
}
class B extends A { // Child
constructor() {
var obj = Object.create(new.target.prototype)
// You can interact with obj, which is effectively your `this` here, before returning
// it to the caller.
return obj;
}
childMethod(obj) {
console.log('childMethod()');
console.log('this === obj ?', this === obj)
console.log('obj instanceof A ?', obj instanceof A);
console.log('obj instanceof B ?', obj instanceof B);
}
}
b = new B()
b.parentMethod()
b.childMethod(b)
Which will output:
parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true
So you can see that we are effectively creating an object of type B (the child class) which is also an object of type A (its parent class) and within the childMethod() of child B we have this pointing to the object obj which we created in B's constructor with Object.create(new.target.prototype).
And all this without caring about super at all.
This leverages the fact that in JS a constructor can return a completely different object when the client code constructs a new instance with new.
Hope this helps someone.
Try:
class Character {
constructor(){
if(Object.getPrototypeOf(this) === Character.prototype){
console.log('invoke character');
}
}
}
class Hero extends Character{
constructor(){
super(); // throws exception when not called
console.log('invoke hero');
}
}
var hero = new Hero();
console.log('now let\'s invoke Character');
var char = new Character();
Demo
I would recommend to use OODK-JS if you intend to develop following OOP concepts.
OODK(function($, _){
var Character = $.class(function ($, µ, _){
$.public(function __initialize(){
$.log('invoke character');
});
});
var Hero = $.extends(Character).class(function ($, µ, _){
$.public(function __initialize(){
$.super.__initialize();
$.log('invoke hero');
});
});
var hero = $.new(Hero);
});
Simple solution: I think its clear no need for explanation.
class ParentClass() {
constructor(skipConstructor = false) { // default value is false
if(skipConstructor) return;
// code here only gets executed when 'super()' is called with false
}
}
class SubClass extends ParentClass {
constructor() {
super(true) // true for skipping ParentClass's constructor.
// code
}
}

How to extend a class without having to use super in ES6?

Is it possible to extend a class in ES6 without calling the super method to invoke the parent class?
EDIT: The question might be misleading. Is it the standard that we have to call super() or am I missing something?
For example:
class Character {
constructor(){
console.log('invoke character');
}
}
class Hero extends Character{
constructor(){
super(); // exception thrown here when not called
console.log('invoke hero');
}
}
var hero = new Hero();
When I'm not calling super() on the derived class I'm getting a scope problem -> this is not defined
I'm running this with iojs --harmony in v2.3.0
The rules for ES2015 (ES6) classes basically come down to:
In a child class constructor, this cannot be used until super is called.
ES6 class constructors MUST call super if they are subclasses, or they must explicitly return some object to take the place of the one that was not initialized.
This comes down to two important sections of the ES2015 spec.
Section 8.1.1.3.4 defines the logic to decide what this is in the function. The important part for classes is that it is possible for this be in an "uninitialized" state, and when in this state, attempting to use this will throw an exception.
Section 9.2.2, [[Construct]], which defines the behavior of functions called via new or super. When calling a base class constructor, this is initialized at step #8 of [[Construct]], but for all other cases, this is uninitialized. At the end of construction, GetThisBinding is called, so if super has not been called yet (thus initializing this), or an explicit replacement object was not returned, the final line of the constructor call will throw an exception.
The new ES6 class syntax is only an other notation for "old" ES5 "classes" with prototypes. Therefore you cannot instantiate a specific class without setting its prototype (the base class).
Thats like putting cheese on your sandwich without making it. Also you cannot put cheese before making the sandwich, so...
...using this keyword before calling the super class with super() is not allowed, too.
// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
constructor() {
super();
this.supplement = "Cheese";
}
}
// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
super();
}
}
// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
}
}
If you don’t specify a constructor for a base class, the following definition is used:
constructor() {}
For derived classes, the following default constructor is used:
constructor(...args) {
super(...args);
}
EDIT: Found this on developer.mozilla.org:
When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.
Source
There have been multiple answers and comments stating that super MUST be the first line inside constructor. That is simply wrong. #loganfsmyth answer has the required references of the requirements, but it boil down to:
Inheriting (extends) constructor must call super before using this and before returning even if this isn't used
See fragment below (works in Chrome...) to see why it might make sense to have statements (without using this) before calling super.
'use strict';
var id = 1;
function idgen() {
return 'ID:' + id++;
}
class Base {
constructor(id) {
this.id = id;
}
toString() { return JSON.stringify(this); }
}
class Derived1 extends Base {
constructor() {
var anID = idgen() + ':Derived1';
super(anID);
this.derivedProp = this.baseProp * 2;
}
}
alert(new Derived1());
You can omit super() in your subclass, if you omit the constructor altogether in your subclass. A 'hidden' default constructor will be included automatically in your subclass. However, if you do include the constructor in your subclass, super() must be called in that constructor.
class A{
constructor(){
this.name = 'hello';
}
}
class B extends A{
constructor(){
// console.log(this.name); // ReferenceError
super();
console.log(this.name);
}
}
class C extends B{} // see? no super(). no constructor()
var x = new B; // hello
var y = new C; // hello
Read this for more information.
The answer by justyourimage is the easiest way, but his example is a little bloated. Here's the generic version:
class Base {
constructor(){
return this._constructor(...arguments);
}
_constructor(){
// just use this as the constructor, no super() restrictions
}
}
class Ext extends Base {
_constructor(){ // _constructor is automatically called, like the real constructor
this.is = "easy"; // no need to call super();
}
}
Don't extend the real constructor(), just use the fake _constructor() for the instantiation logic.
Note, this solution makes debugging annoying because you have to step into an extra method for every instantiation.
Just registered to post this solution since the answers here don't satisfy me the least since there is actually a simple way around this. Adjust your class-creation pattern to overwrite your logic in a sub-method while using only the super constructor and forward the constructors arguments to it.
As in you do not create an constructor in your subclasses per se but only reference to an method that is overridden in the respective subclass.
That means you set yourself free from the constructor functionality enforced upon you and refrain to a regular method - that can be overridden and doesn't enforce super() upon you letting yourself the choice if, where and how you want to call super (fully optional) e.g.:
super.ObjectConstructor(...)
class Observable {
constructor() {
return this.ObjectConstructor(arguments);
}
ObjectConstructor(defaultValue, options) {
this.obj = { type: "Observable" };
console.log("Observable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class ArrayObservable extends Observable {
ObjectConstructor(defaultValue, options, someMoreOptions) {
this.obj = { type: "ArrayObservable" };
console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class DomainObservable extends ArrayObservable {
ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
this.obj = super.ObjectConstructor(defaultValue, options);
console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");
cheers!
#Bergi mentioned new.target.prototype, but I was looking for a concrete example proving that you can access this (or better, the reference to the object the client code is creating with new, see below) without having to call super() at all.
Talk is cheap, show me the code... So here is an example:
class A { // Parent
constructor() {
this.a = 123;
}
parentMethod() {
console.log("parentMethod()");
}
}
class B extends A { // Child
constructor() {
var obj = Object.create(new.target.prototype)
// You can interact with obj, which is effectively your `this` here, before returning
// it to the caller.
return obj;
}
childMethod(obj) {
console.log('childMethod()');
console.log('this === obj ?', this === obj)
console.log('obj instanceof A ?', obj instanceof A);
console.log('obj instanceof B ?', obj instanceof B);
}
}
b = new B()
b.parentMethod()
b.childMethod(b)
Which will output:
parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true
So you can see that we are effectively creating an object of type B (the child class) which is also an object of type A (its parent class) and within the childMethod() of child B we have this pointing to the object obj which we created in B's constructor with Object.create(new.target.prototype).
And all this without caring about super at all.
This leverages the fact that in JS a constructor can return a completely different object when the client code constructs a new instance with new.
Hope this helps someone.
Try:
class Character {
constructor(){
if(Object.getPrototypeOf(this) === Character.prototype){
console.log('invoke character');
}
}
}
class Hero extends Character{
constructor(){
super(); // throws exception when not called
console.log('invoke hero');
}
}
var hero = new Hero();
console.log('now let\'s invoke Character');
var char = new Character();
Demo
I would recommend to use OODK-JS if you intend to develop following OOP concepts.
OODK(function($, _){
var Character = $.class(function ($, µ, _){
$.public(function __initialize(){
$.log('invoke character');
});
});
var Hero = $.extends(Character).class(function ($, µ, _){
$.public(function __initialize(){
$.super.__initialize();
$.log('invoke hero');
});
});
var hero = $.new(Hero);
});
Simple solution: I think its clear no need for explanation.
class ParentClass() {
constructor(skipConstructor = false) { // default value is false
if(skipConstructor) return;
// code here only gets executed when 'super()' is called with false
}
}
class SubClass extends ParentClass {
constructor() {
super(true) // true for skipping ParentClass's constructor.
// code
}
}

Categories

Resources