JavaScript Inheritance

Inheritance and Super in JavaScript

What is the constructor and super() keyword. I believe it is for inheritance?

That's right. The above sets up class B and then has class A subclass it. The constructor is the function called when you create a new instance of the class, as in the let c = new A("Testing", "37"); line in the code. Unlike some other languages, in JavaScript there can only be one constructor for a class.

super is used in subclasses to refer to the superclass. In a constructor, you call super as though it were a function, and that calls the superclass's constructor function, giving it a chance to do its initialization of the new object that was created by new. So for instance, in A's constructor, super() calls B's constructor.

You can also use super as the source of a property accessor expression to access properties (including methods) on the superclass. That's what's happening in A's printName method, where it uses super.printName() to call B's printName method. (Which will fail, because B doesn't have a printName method; B's method is called printn.)

I'd be remiss if I didn't point out that although this looks a lot like the class-based OOP in, say, Java or C#, it isn't. It's syntactic sugar (the good kind of sugar) for setting up JavaScript's normal prototypical inheritance using constructor functions. It hugely simplifies setting up prototypical inheritance hierarchies using constructor functions. I'd also be remiss if I didn't point out that using constructor functions to do prototypical inheritance is not necessary, you can do prototypical inheritance without using constructor functions via Object.create.

There's a lot more to explore. MDN is probably a good starting point.

I can't get this code to work.

The C in Console.log shouldn't be capitalized, so change

Console.log(c.printn());

to

console.log(c.printn());

Other than that, if you're using a JavaScript engine that supports class (such as the one in Google Chrome or Mozilla Firefox), that code works fine although note again that A seems to expect B to have a printName method, but it doesn't, and the code at the end is calling printn which only B has (which is fine, it just means A's code isn't really involved).

class B {    constructor(name) {        this.name = name;    }
printn() { return this.name; }}
class A extends B { constructor(name, age) { super(name); this._age = age; }
get age() { return this._age; }
printName(){ return super.printName(); }}
let c = new A("Testing", "37");console.log(c.printn());

ES6 Class Multiple inheritance

An object can only have one prototype. Inheriting from two classes can be done by creating a parent object as a combination of two parent prototypes.

The syntax for subclassing makes it possible to do that in the declaration, since the right-hand side of the extends clause can be any expression. Thus, you can write a function that combines prototypes according to whatever criteria you like, and call that function in the class declaration.

How to inherit from a class in javascript?

I have changed how I do this now, I try to avoid using constructor functions and their prototype property, but my old answer from 2010 is still at the bottom. I now prefer Object.create(). Object.create is available in all modern browsers.

I should note that Object.create is usually much slower than using new with a function constructor.

//The prototype is just an object when you use `Object.create()`
var Base = {};

//This is how you create an instance:
var baseInstance = Object.create(Base);

//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));

//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True

jsfiddle

One of the big benefits of using Object.create is being able to pass in a defineProperties argument, which gives you significant control over how properties on the class can be accessed and enumerated over, and I also use functions to create instances, these serve as constructors in a way, as you can do initialization at the end instead of just returning the instance.

var Base = {};

function createBase() {
return Object.create(Base, {
doSomething: {
value: function () {
console.log("Doing something");
},
},
});
}

var Sub = createBase();

function createSub() {
return Object.create(Sub, {
doSomethingElse: {
value: function () {
console.log("Doing something else");
},
},
});
}

var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true

jsfiddle

This is my original answer from 2010:

function Base ( ) {
this.color = "blue";
}

function Sub ( ) {

}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
console.log( this.color );
}

var instance = new Sub ( );
instance.showColor( ); //"blue"

Performing inheritance in JavaScript

The JavaScript object oriented paradigm is prototype based. There are no "classes", just objects.

You can implement inheritance in different ways. The two more popular alternatives are the "pseudo-classical" and the "prototypal" forms. For example:

Pseudo-classical inheritance

I think this is the most popular way. You create constructor functions that you use with the new operator, and you add members through the constructor function prototype.

// Define the Person constructor function
function Person() {}

Person.prototype.sayHello = function(){
alert ('hello');
};

// Define the Student constructor function
function Student() {}

// Inherit from Person
Student.prototype = new Person();

// Correct the constructor pointer, because it points to Person
Student.prototype.constructor = Student;

// Replace the sayHello method (a polymorphism example)
Student.prototype.sayHello = function () {
alert('hi, I am a student');
}

var student1 = new Student();
student1.sayHello();

Prototypal inheritance

Basically we make a helper function that takes an object as a parameter and returns an empty new object that inherits from the old one, objects inherit from objects.

// Helper function
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}

var person = {
sayHello : function () {
alert('Person object');
},
walk : function () {
alert('walk');
}
};

var student1 = Object.create(person);
student1.sayHello = function () {
alert('hello I am a student');
};

Another interesting form is the parasitic inheritance. In the "derived" constructor you create a "base" object instance. That object is augmented and that new instance is returned:

// Person constructor function
function Person(name) {
this.name = name;
}

function Student(value) {
var that = new Person(value);
that.sayHello = function () {
alert('hello I am a student');
};
return that;
}

JavaScript inheritance not working as I expected

By doing this later in your code

PermanentEmployee.prototype = employee;

you might override this

PermanentEmployee.prototype.getSalary = function()

Try this instead:

function Employee(name) {  this.name = name;}
Employee.prototype.getName = function() { return this.name;} function PermanentEmployee(name, annualSalary) { Employee.call(this, name); // calling parent's "constructor" this.annualSalary = annualSalary;}PermanentEmployee.prototype = Object.create(Employee.prototype); // setting "inheritance"PermanentEmployee.prototype.getSalary = function() { return this.annualSalary;}var pe = new PermanentEmployee("Mark", 5000);
console.log(pe.getName());console.log(pe.getSalary());

Javascript Inheritance Explanation

Last two console logs give you true because

  • speedBoat instance is created from SpeedBoat class => speedBoat = new SpeedBoat()
  • Similarly, boat instance is created from Boat class => boat = new Boat()

The fist one gives you false because

yatch instance is created from Yatch class => yatch = new Yatch() and you're checking if this yatch instance is an instance of Boat class which is NOT TRUE

Why inheritance is not working for you when you do - console.log(yatch instanceof Boat)

  • Inheritance is the concept where Child class (Yatch in this case) is getting all its properties and methods from the parent class Boat. But that's not happening in your case as you're not inheriting it properly.
  • In your Yatch classes' constructor (and all other constructors), you're defining like this:

The below code doesn't work

class Yatch {
constructor() {
new Boat() // this line is creating object/instance of Boat class and this object is not pointing to any variable and hence will be garbage collected (will be removed from the memory)
}
}

How to define inheritance?

You need to use extends keyword in the Child class (Yatch) and tell which should be the parent class (Boat) like this

class Yatch extends Boat{
super() { // this super keyword calls the constructor of parent (Boat class)
new Boat()
}
}

Modified working code - https://codepen.io/sandeep194920-the-flexboxer/pen/bGvaoyW?editors=1111

JavaScript: Object inheriting from Function.prototype

TL;DR

Object.prototype is last in the prototype chain and it doesn't inherit from anything. The Object constructor is the one that inherits from Function.prototype because it's just a function; it's a Function instance.


Long Version

Since your question is a general one, I'll try to describe a few subjects and hopefully you will answer your own question. Here are the subjects I'll try to cover:

  1. Two ways to use the word "prototype".
  2. How classes are created in JavaScript.
  3. How the Function & Object constructors relate.

Note: It can be hard and at times confusing to explain how JavaScript really works. I hope that you'll get something out of it though.


Two ways to use the word "prototype"

The word "prototype" can be a little confusing in JavaScript. That's because there are at least two ways to use this word depending on the context:

1) "The prototype object of another object"

The prototype object of another object is also talked about as the "internal prototype", denoted as [[Prototype]], or __proto__; they all mean the same thing. As an example let's take this array: nums = [9, 8, 7];. We say that nums is an array... but why?

  1. We say it's an array because it is an instance of the Array constructor (constructors are just functions, except we use them with the new keyword).
  2. We also say it's an array because its prototype object (a.k.a. "internal prototype") is the object contained inside of the Array.prototype property.

2) "The prototype property of a constructor function"

Continuing with the nums array example, the Array constructor function has a property named prototype, and we can access it like this: Array.prototype. This property is the "internal prototype" of Array instances, and provides all the methods that we're used to calling on arrays - e.g. forEach, push, pop, join, and so on.

So, along the same lines, the internal prototype of my function foo(), or any other function, is the object that's contained inside of the Function.prototype property; in other words, Function.prototype is any function's "internal prototype" object. Also, we can say that the Function constructor has a prototype property, which eventually is the "internal prototype" of all functions.

Where I'm getting at is that we speak of one thing (the prototype) in two different ways. In the first way we say: "the prototype/internal prototype" of an object, and in the second way we say: "the constructor's prototype" property.


How classes are created in JavaScript

In JavaScript constructor functions are like classes in other programming languages. Well, not quite. Actually, to resemble classes, JavaScript uses a combination of a constructor function and another object called the prototype. Actually every JavaScript function acquires a prototype property automatically because a function can be used as a constructor or simply as a function. When a function isn't used as a constructor, its prototype property isn't used for anything and it's just dangling there as a useless property.

In classical languages, the class contains both the instance variables and the instance methods, however in JavaScript, the constructor function contains the instance variables and its prototype object contains the instance methods.

Instance variables are unique to the particular instance of a constructor function (they contain instance specific data), and instance methods are shared by all instances. In other words, all instances can execute the instance methods but cannot access the variables of each other.

So, all objects in JavaScript are instances of their respective constructor functions. For example, an array such as [1,2,3] is an instance of the function Array() {} constructor. Objects such as {key: 'value'} are instances of the function Object() {} constructor. JavaScript functions such as alert() are instances of the function Function() {} constructor... and so on.

Again, all constructor functions in JavaScript have a prototype property, and this property includes the methods that instances of the constructor will inherit.

Example:

// Person constructor to create people instances
function Person(name, age) {
// Every instance has its own "instance variables", a.k.a. properties.
this.name = name;
this.age = age;
}

// The "instance methods"
Person.prototype = {
greet: function() {
return 'Hello ' + this.name;
},
//...
};

// Joe is an instance of the `Person` constructor, and Joe's "prototype"
// is the `Person.prototype` object. We call Joe's "prototype" the
// "internal prototype".
var joe = new Person('Joe Doe', 44);
joe.name; //=> Joe Doe
joe.greet(); //=> Hello Joe Doe


How the Function & Object constructors relate

The Object Constructor.

The Object constructor is just like the Person constructor above, except it creates object instances instead of person instances.

The Function Constructor.

The Function constructor is just like the Person & Object constructors above, except that it creates Function instances, in other words it creates functions.

All constructors in JavaScript like Person, Object, Array, Function, String, Boolean, and so on, are just functions. Since they are functions, it means that they were created with new Function internally in the language, and all function methods like call() and apply() come from Function.prototype. In other words, Function.prototype is the "prototype/internal prototype" object of all functions, including constructors and the function Function itself.

Conclusion:

Don't confuse a constructor's prototype property, which includes the methods that future instances will use, with the internal prototype of the constructor itself.

However, keep in mind that a constructor's prototype property is the internal [[Prototype]] of that constructor's instances. For example, Function.prototype is the internal [[Prototype]] for the Object constructor, and that makes sense since the Object constructor is just another function (a Function instance).

For a code conclusion take a look at how the Object & Function constructors are created internally in JavaScript:

// Object constructor
// ==============================================
function Object() { /* ... */ }
// Object.keys()
// Object.observe()
// ...

// `Object.__proto__` (internal [[Prototype]])
// -----------------------------------------------
// Since `Object` is a function, it inherits all of Function's
// instance methods (the ones inside of Function.prototype).
//
// In other words the `Object` constructor can use methods
// like `apply()`, `call()`, `bind()`, and more.
//
// So we can say that the Object's prototype is the
// `Function.prototype` object.
Object.__proto__ = Function.prototype;

// `Object.prototype` (instance methods)
// -----------------------------------------------
// The Object's `prototype` property is totally different from
// the `__proto__` property. This `prototype` property includes
// methods that all JavaScript objects inherit. So an object
// literal like `var obj = {}` or an array like `var arr = []`
// or even a function like `alert` can use these methods.
Object.prototype = {
constructor: Object,
hasOwnProperty: function() {},
isPrototypeOf: function() {},
//...
};

// Function constructor
// ==============================================
function Function() { /* ... */ }
// Function.call()
// Function.apply()
// ...

// [[Prototype]] + instance methods
// -----------------------------------------------
// Since `Function` is a function itself and at the same time
// the constructor for other JavaScript functions, its internal
// [[Prototype]] and the `prototype` property point to the same
// exact object.
Function.__proto__ = Function.prototype = {
apply: function() {},
call: function() {},
bind: function() {},

//...

// Just an object literal, so it inherits the
// Object's instance methods.
__proto__: Object.prototype
};

Further Resources

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#Determining_instance_relationships
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
  • https://es5.github.io/#x15.3.4
  • http://people.mozilla.org/~jorendorff/es5.1-final.html#sec-15.3.2.1; A prototype property is automatically created for every function, to provide for the possibility that the function will be used as a constructor.
  • https://www.quora.com/In-JavaScript-what-is-the-logic-behind-the-data-structure-of-function-prototype-proto-and-constructor?share=1

Prototypical inheritance - writing up

Constructor function introduction

You can use a function as a constructor to create objects, if the constructor function is named Person then the object(s) created with that constructor are instances of Person.

var Person = function(name){
this.name = name;
};
Person.prototype.walk=function(){
this.step().step().step();
};
var bob = new Person("Bob");

Person is the constructor function. When you create an instance using Person you have to use the new keyword:

var bob = new Person("Bob");console.log(bob.name);//=Bob
var ben = new Person("Ben");console.log(ben.name);//=Ben

The property/member name is instance specific, it's different for bob and ben

The member walk is part of Person.prototype and is shared for all instances bob and ben are instances of Person so they share the walk member (bob.walk===ben.walk).

bob.walk();ben.walk();

Because walk() could not be found on bob directly JavaScript will look for it in the Person.prototype as this is the constructor of bob. If it can't be found there it'll look on Object.prototype. This is called the prototype chain. The prototype part of inheritance is done by lengthening this chain; for example bob => Employee.prototype => Person.prototype => Object.prototype (more on inheritance later).

Even though bob, ben and all other created Person instances share walk the function will behave differently per instance because in the walk function it uses this. The value of this will be the invoking object; for now let's say it's the current instance so for bob.walk() "this" will be bob. (more on "this" and the invoking object later).

If ben was waiting for a red light and and bob was at a green light; then you'll invoke walk() on both ben and bob obviously something different would happen to ben and bob.

Shadowing members happens when we do something like ben.walk=22, even though bob and ben share walk the assignment of 22 to ben.walk will not affect bob.walk. This is because that statement will create a member called walk on ben directly and assign it a value of 22. There will be 2 different walk members: ben.walk and Person.prototype.walk.

When asking for bob.walk you'll get the Person.prototype.walk function because walk could not be found on bob. Asking for ben.walk however will get you the value 22 because the member walk has been created on ben and since JavaScript found walk on ben it will not look in the Person.prototype.

When using Object.create with 2 arguments, Object.defineProperty or Object.defineProperties shadowing works a bit different. More info on that here.

More about prototype

An object can inherit from another object through the use of prototype. You can set the prototype of any object with any other object using Object.create. In the constructor function introduction we have seen that if a member can't be found on the object then JavaScript will look in the prototpe chain for it.

In previous part we have seen that re assignment of members that come from an instance's prototype (ben.walk) will shadow that member (create walk on ben rather than changing Person.prototype.walk).

What if we don't re assign but mutate the member? Mutating is (for example) changing sub properties of an Object or invoking functions that will change the object's value. For example:

var o = [];
var a = o;
a.push(11);//mutate a, this will change o
a[1]=22;//mutate a, this will change o

The following code demonstrates the difference between prototype members and instance members by mutating members.

var person = {
name:"default",//immutable so can be used as default
sayName:function(){
console.log("Hello, I am "+this.name);
},
food:[]//not immutable, should be instance specific
// not suitable as prototype member
};
var ben = Object.create(person);
ben.name = "Ben";
var bob = Object.create(person);
console.log(bob.name);//=default, setting ben.name shadowed the member
// so bob.name is actually person.name
ben.food.push("Hamburger");
console.log(bob.food);//=["Hamburger"], mutating a shared member on the
// prototype affects all instances as it changes person.food
console.log(person.food);//=["Hamburger"]

The code above shows that ben and bob share members from person. There is only one person, it is set as bob's and ben's prototype (person is used as the first object in the prototype chain to look up requested members that don't exist on the instance). The problem with the above code is that bob and ben should have their own food member. This is where the constructor function comes in. It is used to create instance specific members. You could also pass arguments to it to set values of these instance specific members.

The next code shows another way to implement the constructor function, syntax is different but the idea is the same:

  1. Define an object that has members that will be same for many instances (person is a blueprint for bob and ben and can be for jilly, marie, clair ...)
  2. Define instance specific members that should be unique for instances (bob and ben).
  3. Create an instance running the code in step 2.

With constructor functions you'll set the prototype in step 2 in the following code we set the prototype in step 3.

In this code I have removed name from prototype as well as food because you are most likely going to shadow this almost immediately when creating an instance anyway. Name is now an instance specific member with a default value set in the constructor function. Becaus the food member is also moved from prototype to instance specific member it will not affect bob.food when adding food to ben.

var person = {
sayName:function(){
console.log("Hello, I am "+this.name);
},
//need to run the constructor function when creating
// an instance to make sure the instance has
// instance specific members
constructor:function(name){
this.name = name || "default";
this.food = [];
return this;
}
};
var ben = Object.create(person).constructor("Ben");
var bob = Object.create(person).constructor("Bob");
console.log(bob.name);//="Bob"
ben.food.push("Hamburger");
console.log(bob.food);//=[]

You may come across similar patterns that are more robust to help with object creation and object definition.

Inheritance

The following code shows how to inherit. The tasks are basically the same as in code before with a little extra

  1. Define instance specific members of an object (functions Hamster and RussionMini).
  2. Set the prototype part of inheritance (RussionMini.prototype = Object.create(Hamster.prototype))
  3. Define members that can be shared among instances.(Hamster.prototype and RussionMini.prototype)
  4. Create an instance running the code in step 1 and for objects that inherit have them run the Parent code as well (Hamster.apply(this,arguments);)

Using a pattern some would call "classical inheritance". If you are confused by the syntax I'll be happy to explain more or provide different patterns.

function Hamster(){
this.food=[];
}
function RussionMini(){
//Hamster.apply(this,arguments) executes every line of code
//in the Hamster body where the value of "this" is
//the to be created RussionMini (once for mini and once for betty)
Hamster.apply(this,arguments);
}
//setting RussionMini's prototype
RussionMini.prototype=Object.create(Hamster.prototype);
//setting the built in member called constructor to point
// to the right function (previous line has it point to Hamster)
RussionMini.prototype.constructor=RussionMini;
mini=new RussionMini();
//this.food (instance specic to mini)
// comes from running the Hamster code
// with Hamster.apply(this,arguments);
mini.food.push("mini's food");
//adding behavior specific to Hamster that will still be
// inherited by RussionMini because RussionMini.prototype's prototype
// is Hamster.prototype
Hamster.prototype.runWheel=function(){console.log("I'm running")};
mini.runWheel();//=I'm running

Object.create to set prototype part of inheritance

Here is the documentation about Object.create, it basically returns the second argument (not supported in the polyfil) with the first argument as the returned object's prototype.

If no second argument was given it'll return an empty object with first argument to be used as the returned object's prototype (the first object to be used in the returned object's prototype chain).

Some would set the prototype of RussionMini to an instance of Hamster (RussionMini.prototype = new Hamster()). This is not desirable because even though it accomplishes the same (RussionMini.prototype's prototype is Hamster.prototype) it also sets Hamster instance members as members of RussionMini.prototype. So RussionMini.prototype.food will exist but is a shared member (remember bob and ben in "More about prototype"?). The food member will be shadowed when creating a RussionMini because Hamster code is run with Hamster.apply(this,arguments); that in turn runs this.food = [] but any Hamster members will still be members of RussionMini.prototype.

Another reason could be that to create a Hamster a lot of complicated calculations need be done on passed arguments that may be not available yet, again you could pass in dummy arguments but it could unnecessarily complicate your code.

Extending and overriding Parent functions

Sometimes children need to extend parent functions.

You want the 'child' (=RussionMini) to do something extra. When RussionMini can call the Hamster code to do something and then do something extra you don't need to copy and paste Hamster code to RussionMini.



Related Topics



Leave a reply



Submit