If I define two classes as follows...
class A {
getParentInstance(...args) {
return new super.constructor(...args);
}
}
class B extends A {}
console.log((new B).getParentInstance().constructor.name);
Object is logged to the console, instead of my desired A. This is due to the super in A.prototype.getParentInstance referencing the superclass of A specifically which is Object. This is opposed to the alternative, which would be super being relative to the current level in the prototype chain -- for B that would be A.
My question is: Is there a way to define methods in such a way to use the relative super at each level of the prototype chain when inherited? Effectively resulting in...
(new B).getParentInstance().constructor.name === 'A'
You could try something like this
class A {
static getParentConstructor() {
return Object;
}
}
class B extends A {
static getParentConstructor() {
return A;
}
}
var b = new B();
var a = new (b.constructor.getParentConstructor())();
A little Object.getPrototypeOf() magic seems to be the trick:
class A {
getParentInstance(...args) {
const thisProto = this.constructor.prototype;
const RelativeSuperConstructor = Object.getPrototypeOf(thisProto).constructor;
return new RelativeSuperConstructor(...args);
}
}
class B extends A {}
console.log((new B).getParentInstance().constructor.name);
results in the correct "super" being grabbed, and thus logs 'A' as desired.
Related
In python there's something like __call__ for this. Consider the following example:
class MyClass {
__call__() { return 'called!' }
}
const myType = new MyClass();
myType(); // called!
The question is what should I replace __call__ with?
I was doing some research, and most of the answers recommend __proto__, but it doesn't seem to work.
It is not possible out-of-the-box, but you can extend Function, and use the Function constructor to forward a call to __call__. If you have multiple classes that need this feature, extend Function only once into -- let's say -- a Callable class, and then inherit your other classes from that:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
__call__() { return 'called!' }
}
let inst = new Class();
console.log(inst());
Background
In JavaScript an object is callable when, and only if, it has the [[Call]] internal slot. But there is (currently) no way to give any given object this slot via JavaScript code. One must start with a function object and extend that.
Adding a constructor, inheritance
The above solution allows the constructor to define properties in the usual way: the constructed object is an instance of the class:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
constructor(data) {
super();
this.x = data;
}
__call__() { return 'called!' }
}
let inst = new Class(42);
console.log(inst instanceof Class); // true
console.log(inst.x); // 42
console.log(inst());
You can use constructor.
class Example {
constructor() {
// gets called on class initialization
}
}
Inside the constructor you can also call other methods if you want.
However this won't create an invoke function like using PHP's __invoke if that's what you meant. If that's what you're looking for then I don't know.
I have an Octree class. A key feature of an Octree is that it can create its own children.
class Octree {
...
createChildren(){
...
/* for each of the 8 new children*/
this.children.push(new Octree(/*someargs*/))
...
}
}
Now I want to inherit off of the Octree class, however, I also want the children to become the inherited class. For example class LODWorldTree extends Octree, to additionally contain some renderer data for a game. However, if I call LODWorldTree.createChildren(), then LODWorldTree.children will be an array of Octrees instead of LODWorldTrees.
What is the best way to fix this problem? While writing this it occured to I could store Octree.myClass = /*some inherited class*/, and manually set this variable for all classes that inherit from Octree. Is there a better way to do something like this? Maybe with this.prototype?
You can utilize the fact that each object has a reference to it's own constructor via the prototype:
class A {
constructor() {
this.val = 1;
this.children = [];
this.typeName = `I'm A`;
}
addSelfChild() {
this.children.push(new this.constructor(this.val + 1));
}
}
let a = new A(1);
a.addSelfChild();
a.addSelfChild();
console.dir(a);
class B extends A {
constructor(val) {
super(val);
this.typeName = `I'm B`;
}
}
let b = new B(1);
b.addSelfChild();
b.addSelfChild();
console.dir(b);
Try to use constructor attribute:
this.children.push(new this.constructor(/*someargs*/));
this.constructor is the reference for constructor for current object, so invoking it will produce new instance of the same class
I'm trying to apply this to the constructor of an es6 class to get some kind of "constructor merging" like this:
class D {
constructor(name){
this.name=name
this.methodD=function(){}
}
}
class C extends D {
constructor(name,name2){
super(name)
this.name2=name2
}
}
function Custom(name,name2){
if (this instanceof Custom){
Function.prototype.bind.call(C.prototype.constructor,this,...arguments)
}
}
Custom.prototype.method=function(){}
const cc=new Custom('name','name2')
I'm expecting cc to be constructed using the same constructor as C, so getting cc.name='name';cc.methodD();
Thank you in advance for your help.
You could try to create new instance of C inside of Custom and redefine prototype, using es6 Object.setPrototypeOf().
Please note that in this case Custom will only return object which will be constructed like C and its prototype will not contain methods of C.prototype and D.prototype, only their own methods, like methodD of D class.
Also note, that according to MDN article, this method could affect performance of browser.
class D {
constructor(name){
this.name=name
this.methodD=function(){}
}
}
class C extends D {
constructor(name,name2){
super(name)
this.name2=name2
}
}
function Custom(name,name2){
let cls = new C(...arguments);
Object.setPrototypeOf(cls, Custom.prototype);
return cls;
}
Custom.prototype.method=function(){}
const cc=new Custom('name','name2')
console.log(cc.name)
console.log(cc.name2)
console.log(cc.methodD)
console.log(cc instanceof Custom)
I have a fairly simple example of a Container that stashes a value away and allows you to operate on it in isolation.
For my own interest I've translated the basic structure of this object from .prototype to class syntax. But the example uses a funky method for creating new instances of this object and I can't figure out how to replicate it in class syntax (see code below)
const Container = function(x) {
this.val = x
}
Container.prototype.map = function(f) {
return Container.of(f(this.val))
}
Container.of = function(x) { return new Container(x) } // Problem spot
This translates into (class syntax):
class Container {
constructor(x) {
this.val = x
}
map(f) {
return Container.of(f(this.val))
}
of = (x) => { // ????????
return new Container(x)
}
}
I believe that the problem is that the "of" method is simply bound to the single original instance of "Container" as a helper to make it easier to not have to write "new" every time you want to spin up an instance of this class. But I can't figure out how to replicate binding like that with the class syntax.
Is it just impossible to instantiate an own-class from one of the classe's own methods?
just declare the function as static.
class Container {
constructor(x) {
this.val = x
}
map(f) {
return Container.of(f(this.val))
}
static of(x) { // ????????
return new Container(x)
}
}
I try to extend a class with another, who has its constructor overrode, but when i instance this class, it doesn't have its own methods, but has its own properties.
Here's an example which doesn't work properly:
class A {
constructor () {
return {
pi: 3.14
}
}
}
class B extends A {
constructor () {
super();
this.c = 10;
}
d () {}
}
let b = new B();
console.log(b);
Here, b is :
Object {
c:10,
pi:3.14
}
So why the 'd' method is missing ?
EDIT:
Here is a concrete case:
I need to extend a class with HTMLElement, which i can instance and use like html element without registering with document.registerElement.
My code is:
class Element{
constructor(){
return document.createElement('div');
}
}
class Editor extends Element{
constructor(){
super();
}
}
and i want to use my class like this:
let editor = new Editor();
document.querySelector('body').appendChild(editor);
ECMAScript6 class methods are methods of an object's prototype object. You will find the method not in the object itself, but in obj.__proto__.
https://reinteractive.com/posts/235-es6-classes-and-javascript-prototypes
Here you can see and maybe understand what is going on internally with prototyped objects. And yes, the ES6 syntax is just a syntax change, not a new technology.