Search code examples
javascriptecmascript-6prototypeextends

How do javascripts extends work?


🞰🞰🞰 Update 🞰🞰🞰: Post by @Bergi on this thread explains the need to use Reflect.construct instead of just calling the parent constructor. This shifts the question to "What's the technical difference between Constructor.call() and Reflect.construct()". Thanks for all the comments.

For quite a while I have been extending all sorts of types by setting the prototype of my new prototype to the one that is to be extended. E.g. If I wanted to extend the class "Person" i'd create my extending prototype by calling NewType.prototype=Object.create(Person.prototype); for something like:

const B = function(){
    return A.call(this);
};

B.prototype = Object.create(A.prototype);

B.prototype.extra_method = function(){
    console.log('hello');
}

Recently I have noticed tho that this method is not as capable as the ES6 class extends. For example if I wanted to extend a builtin type, I can only really do that with the class extends keyword. Why is that?

Example A (does not work):

const ArrayExt = function(){
    this.extra_variable = 'hello!';
    return Array.call(this,...arguments); 
}

ArrayExt.prototype = Object.create(Array.prototype);

ArrayExt.prototype.extra_method = function(){
    console.log('Method called!');
}

var inst = new ArrayExt(1,2,3);

inst.push(4);
inst.length == 4 //seems to work right?

inst.extra_method == undefined //not included
inst.extra_variable == undefined //not included (why?)


Example B (works):

const ArrayExt = class extends Array{
    constructor(){
        super(...arguments);
        this.extra_variable = 'Hello!';
    }

    extra_method(){
        console.log('I actually am here now!');
    }
};

var inst = new ArrayExt(1,2,3);

inst.push(4);
inst.length == 4 //works
inst.extra_variable == 'Hello!' //works
inst.extra_method() //'I actually am here now!' works

Solution

  • What's the technical difference between Constructor.call() and Reflect.construct()

    It's a different usage of the function object. It's the same as the difference between Example() and new Example().

    The two expressions always did different things, resolving to two different internal methods (named [[call]] and [[construct]]) that an object could have. ES6 just introduced new types of functions where only one of them works (arrows, methods: call; class constructors: construct). Functions created from function syntax always did enable both (even when it was not desirable).