Function-binding with super keyword in javascript - javascript

I would like to call "super" from a bound function.
Here is my use case: I have many child classes from different parents. I'd like to bind the same function to all of them (instead of copy pasting it). That function needs to call the "super" version of that same function.
Example:
class Parent {
func() {
console.log("string1");
}
}
function boundFunc() {
super.func();
console.log(this.string2);
}
class Child extends Parent {
constructor() {
super();
this.string2 = "string2"
this.func = boundFunc.bind(this);
}
}
const child = new Child();
child.func();
I would like to obtain result:
string1
string2
I get this result instead (unsurprisingly, I'd day):
"SyntaxError: 'super' keyword unexpected here".
I've tried to pass the super function as an argument to bind. Like this:
function bindedFunc(thisArg, oriFunc) {
oriFunc();
console.log(this.string2);
}
class Child extends Parent {
constructor() {
super();
this.string2 = "string2"
this.func = bindedFunc.bind(this, super.func);
}
}
Result (oriFunc happens to be undefined):
TypeError: oriFunc is not a function
Any solution? Thank you

Instead of super, you can use Object.getPrototypeOf twice: once to navigate from the instance to its internal prototype (which is Child.prototype), and once to navigate from that to its internal prototype (which is Parent.prototype):
class Parent {
func() {
console.log("string1");
}
}
function boundFunc() {
Object.getPrototypeOf(Object.getPrototypeOf(this)).func();
console.log(this.string2);
}
class Child extends Parent {
constructor() {
super();
this.string2 = "string2"
this.func = boundFunc.bind(this);
}
}
const child = new Child();
child.func();

Related

Instantiate subclass without constructing

I'm trying to write a subclass using the es6 class syntax. The subclass has some complicated logic to perform before calling the superclass constructor, so I tried factoring it out into a function. However, this seems to be impossible since this is not defined until after super() is called.
I've tried simply calling super() twice, once at the start of the constructor and once at the end, but it feels wrong and wastes the work that the superclass constructor does the first time.
class Parent {
constructor(x) {
console.log('some expensive thing with ' + x);
}
}
class Child extends Parent {
constructor() {
let x = this.f();
super(x);
}
f() {
// complicated logic
return 3;
}
}
let c = new Child();
Running the code as written results in ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor at new Child. Removing this and attempting to call f() results in ReferenceError: f is not defined at new Child.
Is there any way to factor the subclass constructor logic somewhere else, even if it's okay if this isn't bound?
I would use an initialization function separate from the constructor as that give you more control over when/if parent initialization happens.
class Parent {
constructor(x) {
this.init(x);
console.log("parent constructor does other stuff");
}
init(x) {
console.log("parent init runs")
}
}
class Child extends Parent {
constructor(x) {
super(x);
}
init(x) {
console.log("child init runs");
super.init(x); // This call is optional if you don't want to run the parent's init code
}
}
let c = new Child();
Using a static method could be a solution.
class Parent {
constructor(x) {
console.log('some expensive thing with ' + x);
}
}
class Child extends Parent {
constructor() {
let x = Child.f();
super(x);
}
static f() {
// complicated logic
return 3;
}
}
let c = new Child();

Call child method class in parent with parent instance

I don't want my parent class to be too long so I separate some methods from it and create child class.
However I don't want to use child class as a instance I want it to be used only by parent class.
class Parent {
parentMethod() {
this.foo(); // execute from parent
}
}
class Child extends Parent {
foo() {
console.log('foo!');
}
}
const parent = new Parent();
parent.parentMethod(); // execute parent method which execute child method
this cause:
Uncaught TypeError: this.foo is not a function
I don't want my parent class to be too long so I separate some methods from it
Ok.
So I create child class, however I don't want to use child class as a instance.
No, subclassing is the wrong approach here. Especially if you don't want to instantiate the subclass, it's not even a solution to your problem.
To separate units of code, factor them out into separate functions. Those don't need to be linked to the caller through inheritance, and don't need to be methods of a class at all. Just write
class MyClass {
myMethod() {
foo();
}
}
function foo() {
console.log('foo!');
}
const instance = new MyClass();
instance.myMethod();
Or compose your object of multiple smaller helpers:
class Outer {
constructor() {
this.helper = new Inner();
}
myMethod() {
this.helper.foo();
}
}
class Inner {
foo() {
console.log('foo!');
}
}
const instance = new Outer();
instance.myMethod();
If you want to use the Parent class in the subclass, Child class, then you need to do the following:
class Parent {
foo() {
console.log('foo!');
}
}
class Child extends Parent {
constructor() {
super();
}
}
let c = new Child(); //instantiate Child class
c.foo(); // here you are calling super.foo(); which is the Parent classes method for foo.
//foo!
The super keyword is used to access and call functions on an object's
parent.
How to use super
Or, alternatively, if you'd rather create a method on the child class that wraps the parent method of foo, rather than accessing it by instantiating the parent class via calling super in the child constructor:
class Parent {
foo() {
console.log('foo!');
}
}
class Child extends Parent {
method() {
this.foo();
}
}
let c = new Child();
c.method();

How to directly call setter of an object's parent class?

TLDR; How to directly call setter of an object's parent class without invoking the child's setter outside of both the parent and child class?
I know that if the solution exists, it may be very hacky/magic-like, but I don't mind. Here's the scenario:
Parent is class from a 3rd party library so I can't change this code at all.
Child is a class from my codebase, but I'd like to keep the magic code outside of it, as the Prop class may be use with different "Child" classes.
Prop is the class where the magic code may reside if necessary.
I need to access the Parent's setter of x via a Child object without invoking the setter of x of the Child.
Is it even possible?
class Parent {
constructor() {
this._x = 255;
}
set x(v) {
console.log("Calling Parent setter");
this._x = v;
}
get x() {
console.log("Calling Parent getter");
return this._x;
}
}
class Child extends Parent {
constructor() {
super();
this.prop = new Prop(this);
}
set x(v) {
console.log("AVOID! Calling Child setter");
super.x = v;
// Shennanigans I don't want to run
}
get x() {
console.log("Calling Child getter");
return super.x;
}
}
class Prop {
constructor(child) {
this.child = child;
}
setX() {
const parent = this.child; // Not sure what to do here.
const old = parent.x;
parent.x = 0;
console.log(`parent.x changed from ${old} to ${parent.x}`);
}
}
const child = new Child();
child.prop.setX();
Reflect.set is here to your rescue! It does allow to pass the receiver separately:
setX() {
Reflect.set(Parent.prototype, "x", 0, this.child); // invokes the Parent.protype.x setter
}
Alternatives would be Object.getOwnPropertyDescriptor(Parent.prototype, "x").set.call(this.child, 0) or just this.child._x = 0 (if you don't need to run the setter code).
So while it is possible, I would recommend to reconsider your design. Maybe inheritance is the wrong approach here, and you should use composition instead of extends Parent:
class Child {
constructor() {
this.val = new Parent();
}
set x(v) {
… // Shenanigans
this.val.x = v;
}
get x() {
return this.val.x;
}
// (without the Prop helper class for simplicity)
setX(v) {
// without shenanigans
this.val.x = v;
}
}

Typescript: Overriding parent class properties in child

Given the following case:
class Parent {
propStr = "Hello";
propNum = 42;
constructor(propShared) {
console.log(this.propStr); // Hello
console.log(this.propNum); // 42
console.log(propShared); // w/e
}
}
class Child extends Parent {
propStr = "Hi"; // overridden
propNum = 1337; // overridden
constructor(propShared) {
super(propShared);
}
}
let c = new Child("Foobar");
How do I make sure that the parent properties are properly overridden so that the console.log prints the child's properties?
You are logging the properties from within the parent constructor, where they are not yet overwritten by the child constructor code that runs after super(). You should only do initialisation within the constructor, not execute any side effects:
class Parent {
propStr = "Hello";
propNum = 42;
log() {
console.log(this.propStr);
console.log(this.propNum);
}
}
class Child extends Parent {
propStr = "Hi"; // overridden
propNum = 1337; // overridden
}
const c = new Child("Foobar");
c.log()
If you want the initialisation to depend on things chosen elsewhere, including child classes, make them parameters. You can still give them reasonable defaults:
class Parent {
constructor(propStr = "Hello", propNum = 42, propShared) {
this.propStr = propStr;
this.propNum = propNum;
this.propShared = propShared;
}
log() {
console.log(this.propStr);
console.log(this.propNum);
console.log(this.propShared);
}
}
class Child extends Parent {
constructor(propShared) {
super("Hi", 1337, propShared);
}
}
const c = new Child("Foobar");
c.log();
Since you mentioned typescript, you can use a feature of typescript called Parameter Properties:
class Parent {
constructor(public propStr = "Hello", public propNum = 42, public propShared: any) {
console.log(this.propStr); // Hello
console.log(this.propNum); // 42
console.log(this.propShared); // w/e
}
}
class Child extends Parent {
constructor(propShared: any) {
super("Hi", 1337, propShared);
}
}
let c = new Child("Foobar");
The output will be exactly what you were expecting.
You can also use getter. Then parent constructor see value declared in child.
class Parent {
get propStr: string {
return "Hello"
}
}
class Child extends Parent {
get propStr: string {
return "Hi"
}
}

Cannot understand class to function mapping in javascript

class Parent {
print() {
console.log('hey i am parent');
}
}
class Child extends Parent {
constructor() {
super();
}
print() {
console.log('hey i am child');
}
}
x = new Parent();
console.log(Object.getPrototypeOf(x))
x.print();
Though the [[prototype]] of x is an empty object but still It can access the print() function which is defined in class Parent.
I cannot understand why Object.getPrototypeOf(x) is an empty object.
It's in there, just non-enumerable. Try this:
Object.getOwnPropertyNames(Object.getPrototypeOf(x));
// ["constructor", "print"]
class Parent {
print() {
console.log('hey i am parent');
}
}
class Child extends Parent {
constructor() {
super();
}
print() {
console.log('hey i am child');
}
}
x = new Parent();
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(x)));
x.print();
What makes you think it's empty? It does have constructor and print properties, they are however not enumerable and not displayed by default on the console. (And of course it does have a [[prototype]] link to Object.prototype, but how/whether that is displayed depends on your console as well).
To inspect them, have a look at Get functions (methods) of a class.

Categories