简体   繁体   中英

Extend already defined class in JavaScript

The traditional way of extending classes in JS is something like this:

// define constructor function
function Fuu(){}
// extend prototype and override prototype's constructor
Fuu.prototype = Object.create(OtherClass.prototype, {
    constructor: {
        value: Fuu, 
        enumerable: false, 
        writable: true, 
        configurable: true
    }
});

Then you add the methods you want to the prototype

Fuu.prototype.method = function() {}

And just like that you have a function extending another. A nice example of inheritance in JS!

My question is how to extend when the sub class already has a prototype with methods and properties. I could try to copy the the methods of the old prototype to the new one using a for in loop but the methods are non enumerable(class is created with a transpiler) and doing something with getOwnPropertyNames doesn't seem right. Any suggestion? can I do something like keeping the prototype and adding a prototype to the prototype?

Edit: example

class Fuu {
    someMethod(){} // non enumerable method in Fuu's prototype
}

// My first option: (extending this way `someMethod` is lost)
Fuu.protoype = Object.create(HTMLElement.prototype, {//...same as before})

// Option 2: copy methods from old to new prototype

// Option 3: prototype of prototype?
// Fuu.prototype.prototype = Object.create(HTMLElement.prototype, {...})

You want something like

            ┌──> Fuu.prototype
instances ──┤
            └──> OtherClass.prototype

But that's not possible, because objects only have one [[Prototype]].

Therefore, you must achieve one of these:

instances ───> Fuu.prototype ───> OtherClass.prototype
instances ───> OtherClass.prototype ───> Fuu.prototype

So you must set the [[Prototype]] of one of those to be the other one. I will assume the first possibility.

There are two main ways to set the [[Prototype]]:

  • Object.create , when creating the object

    The problem is that both Fuu.prototype and OtherClass.prototype have been created already.

    However, you can create a new object with the right [[Prototype]] and assign the properties of the old one.

    Since there may be non-enumerable properties, you must use getOwnPropertyNames . Using defineProperty and getOwnPropertyDescriptor may also be a good idea, in case there are getters or setters.

     var old = Fuu.prototype, props = Object.getOwnPropertyNames(old); Fuu.prototype = Object.create(OtherClass.prototype); for(var i=0; i<props.length; ++i) Object.defineProperty( Fuu.prototype, props[i], Object.getOwnPropertyDescriptor(old, props[i]) ); 
  • setPrototypeOf or __proto__ (ES6), once the object has been created:

     Object.setPrototypeOf(Fuu.prototype, OtherClass.prototype); 
     Fuu.prototype.__proto__ = OtherClass.prototype; 

    However, be aware that

    Mutating the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine. The effects on performance of mutating prototypes [...] may extend to any code that has access to any object whose [[Prototype]] has been mutated. If you care about performance you should avoid mutating the [[Prototype]] of an object.

I think the method you have suggested is probably the bet way to go. Is there a reason why you think it is wrong?

var old = Fuu.prototype;
Fuu.prototype = Object.create(OtherClass.prototype, {
    constructor: {
        value: Fuu, 
        enumerable: false, 
        writable: true, 
        configurable: true
    }
});
var names = Object.getOwnPropertyNames(old);
for (var i = 0; i < names.length; i++) {
    var name = names[i];
    Fuu.prototype[name] = old[name];
}

The only thing I'd be concerned about is your constructor method being overridden by the old version, and for your old prototype's prototype chain being lost; however you can do things to fix this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM