简体   繁体   中英

Multiple prototypical inheritance in Javascript

I have 2 base classes, say ParentClass1 and ParentClass2 . Now I want to do multiple prototypical inheritance to ChildClass .

With a single parent class , I had my code as follows.

 var ParentClass = function() { }; ParentClass.prototype.greetUser = function(name) { console.log('Hi. Hello,', name); }; var ChildClass = function(name) { this.greetUser(name); }; ChildClass.prototype = Object.create(ParentClass.prototype); var obj = new ChildClass('John'); // Hi. Hello, John 


And now when I have to inherit from 2 parent classes , I tried the following code.

 var ParentClass1 = function() { }; ParentClass1.prototype.greetUser = function(name) { console.log('Hi. Hello,', name); }; var ParentClass2 = function() { }; ParentClass2.prototype.askUser = function(name) { console.log('Hey, how are you,', name); }; var ChildClass = function(name) { this.askUser(name); this.greetUser(name); }; ChildClass.prototype = Object.create(ParentClass1.prototype); ChildClass.prototype = Object.create(ParentClass2.prototype); var obj = new ChildClass('John'); // Error. 

But this seems like, this will only accept the last mentioned Object.create() .

So later, It tried switching the second Object.create() to Object.assign() , then it works fine.

ChildClass.prototype = Object.create(ParentClass1.prototype);
ChildClass.prototype = Object.assign(ChildClass.prototype, ParentClass2.prototype);

But my concern is Object.assign() is doing a clone . So is that the right way to do it? Or is there any better alternatives?

Sorry for making this question, lengthy. Thanks in advance.

NOTE: I had a brief look into this and this

It is not very useful to do two assignments to the prototype property, as the last one will overwrite the first. You can do like this, since Object.assign accepts more arguments:

Object.assign(ChildClass.prototype, ParentClass1.prototype, ParentClass2.prototype);

Note that Object.assign performs a shallow copy. That a copy has to be made is sure: you need a prototype object that is different from both other prototypes: the union of both. So inevitably you need to somehow copy the members of the parent prototypes into your target prototype object.

Some caveats:

1. Object.assign makes a shallow copy

Since Object.assign performs a shallow copy, you might get into cases where you interfere with the parent prototype. This might be what you want or do not want.

Example:

 var ParentClass1 = function() { }; ParentClass1.prototype.userList = []; ParentClass1.prototype.addUser = function(name) { this.userList.push(name); }; var ParentClass2 = function() { }; ParentClass2.prototype.askUser = function(name) { console.log('Hey, how are you,', name); }; var ChildClass = function(name) { this.askUser(name); }; Object.assign(ChildClass.prototype, ParentClass1.prototype, ParentClass2.prototype); var p = new ParentClass1('Parent'); var obj = new ChildClass('John'); obj.addUser('Tim'); // Added to child, but console.log(p.userList); // now parent also has Tim... 

2. Object.assign only copies enumerable properties

This means that in some cases you will not get the properties you had hoped for. Say you wanted to inherit also from Array.prototype , then you would want your child object to have a length property, but since it is not enumerable, you will not get it with Object.assign :

 var ParentClass2 = function() { }; ParentClass2.prototype.askUser = function(name) { console.log('Hey, how are you,', name); }; var ChildClass = function(name) { this.askUser(name); }; Object.assign(ChildClass.prototype, Array.prototype, ParentClass2.prototype); var obj = new ChildClass('John'); console.log(obj.length); // undefined console.log(Array.prototype.length); // 0 

3. Object.assign executes getters

Object.assign cannot copy getters. Instead it executes them to retrieve the value for the copy. Executing code on the parent prototype may have effects (by design of that getter) on the state of the parent prototype. This might be undesired behaviour in the context of this copy.

Secondly, the value of the getter can be the result of some calculation and state of the object, returning different values each time it is referenced. But object.assign will only reference it once, and then create a property that always has that single value. See the effect in this example:

 var ParentClass1 = function() { }; // Define a getter on the prototype which returns a // random number between 0 and 999, every time it is referenced: Object.defineProperty(ParentClass1.prototype, 'randomNumber', { get: function() { return Math.round(Math.random() * 1000); }, enumerable: true }); var ParentClass2 = function() {}; ParentClass2.prototype.askUser = function(name) { console.log('Hey, how are you,', name); }; var ChildClass = function(name) { this.askUser(name); }; Object.assign(ChildClass.prototype, ParentClass1.prototype, ParentClass2.prototype); var p = new ParentClass1('Parent'); var obj = new ChildClass('John'); console.log('different:'); console.log(p.randomNumber); console.log(p.randomNumber); console.log(p.randomNumber); console.log('always same:'); console.log(obj.randomNumber); console.log(obj.randomNumber); console.log(obj.randomNumber); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Further reading

The concept of combining multiple prototypes into a new one is often coined "mixin" . Here are some related Q&A:

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