Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to know the 'this' object before calling the constructor?

Before ES6 classes, a function could be used as a constructor:

function MyClass(a, b) {
}

Then, the following code is equivalent to a classic instantiation (like let thisObj = new MyClass("A", "B")):

let thisObj = Object.create(MyClass.prototype)
// Here we know the `this` object before to call the constructor.
// Then, the constructor is called manually:
MyClass.call(thisObj, "A", "B")

… This technique was a way to know the this object before calling the constructor. But Function.prototype.call() doesn't work on an ES6 class constructor.

With ES6 we have Reflect.construct():

let thisObj = Reflect.construct(MyClass, "A", "B");

But it doesn't provide a way to call the constructor after the this object is created.

Is it still possible to do that with ES6 classes?

My use case

I would have needed to keep this feature from ES5 to ES6 for a framework. The framework is responsible for instantiating components (which are ES6 classes). A component can create child components (in a tree of components, there is no inheritance here) from its constructor. Then, a child component can query the framework to get its parent from its own constructor. In this case, we have a technical limitation because the framework still doesn't have the return value of the parent component constructor. This is a regression compared to (a transpilation to) ES5.

like image 617
Paleo Avatar asked Apr 25 '18 13:04

Paleo


People also ask

Can we use this keyword in the constructor and why?

Within an instance method or a constructor, this is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this .

When an object is created what is called first?

Explanation: Constructors are the member functions which are called automatically whenever an object is created. It is a mandatory functions to be called for an object to be created as this helps in initializing the object to a legal initial value for the class.

Can a method call a constructor?

No, you cannot call a constructor from a method. The only place from which you can invoke constructors using “this()” or, “super()” is the first line of another constructor. If you try to invoke constructors explicitly elsewhere, a compile time error will be generated.

What is the correct way of placing this keyword in a constructor first statement?

Calling parameterized constructor from default constructor: A(){ this(5); System.out.println("hello a"); }


1 Answers

It's impossible to do that with ES6 classes. ES6 classes are supposed to be instantiated only with new or Reflect.construct.

Function-calling classes is currently forbidden. That was done to keep options open for the future, to eventually add a way to handle function calls via classes. [source: exploringjs]

See also:

  • Is it possible to inherit old-style class from ECMAScript 6 class in JavaScript?
  • are es6 classes just syntactic sugar for the prototypal pattern in javascript?

Why this pattern is not viable

Class instance isn't necessary this object that appears in constructor, because ES6 class can return a value from constructor, which is considered class instance:

class Foo {
  constructor {
    // `this` is never used
    return {};
  }
}

A component can create sub-components from its constructor. Then, a sub-component can query the framework to get its parent from its own constructor

This pattern is not viable in ES6 classes. The limitation restricts this from appearing before super.

In order to reach particular classes in hierarchy, decorator pattern can be used to decorate a class when it's defined. This allows to modify its prototype on definition or put an intermediate class between a parent and a child. This approach solves a lot of framework-specific tasks like dependency injection and is used in modern frameworks (Angular, etc.).

A convenient way to do this is ECMAScript Next/TypeScript decorator. Here's an example that shows that a decorator allows to dynamically intercept child constructor and augment child prototype:

let BAZ;

class Foo {
  constructor(foo) {
    console.log(foo);    
  }
}

function bazDecorator(Class) {
  return class extends Class {
    constructor(foo) {
      super(BAZ || foo);
    }

    get bar() {
      return BAZ || super.bar;
    }
  }
}

@bazDecorator
class Bar extends Foo {
  constructor(foo) {
    super(foo);

    console.log(this.bar);
  }

  get bar() {
    return 'bar';
  }
}

// original behaviour
new Bar('foo'); // outputs 'foo', 'bar'

// decorated behaviour
BAZ = 'baz';
new Bar('foo'); outputs 'baz', 'baz'

ES.next decorator is basically a helper function. Even without syntactic sugar, it is still applicable in ES6 with slightly different syntax:

const Bar = bazDecorator(
    class Bar extends Foo { ... }
);
like image 149
Estus Flask Avatar answered Oct 22 '22 13:10

Estus Flask