Correct JavaScript Inheritance

Correct javascript inheritance

Simple: Object.create is not supported in all environments, but can be shimmed with new. Apart from that, the two have different aims: Object.create just creates a Object inheriting from some other, while new also invokes a constructor function. Use what is appropriate.

In your case you seem to want that RestModel.prototype inherits from Model.prototype. Object.create (or its shim) is the correct way then, because you do not want to a) create a new instance (instantiate a new Model) and b) don't want to call the Model constructor:

RestModel.prototype = Object.create(Model.prototype);

If you want to call the Model constructor on RestModels, that has nothing to do with prototypes. Use call() or apply() for that:

function RestModel() {
Model.call(this); // apply Model's constructor on the new object
...
}

Proper Javascript inheritance

The standard way to call a superclass constructor is using Function.call:

function Moveable(x, y) {
Sprite.call(this, x, y);
}

As for the prototype, you can do something like this to chain the prototype without creating an instance of the superclass:

function makePrototype(superclass) {
function f() { }
f.prototype = superclass.prototype;
return new f();
}

Moveable.prototype = makePrototype(Sprite);

This uses a dummy constructor to create an object that shares the same prototype as Sprite, and since that's all JavaScript cares about, instances of Moveable are considered instanceof Sprite.

This isn't "short and readable" as you asked for, but the only other choice is to entirely skip prototypes and assign members directly within the constructor.

Edit: As @Raynos points out, you also want to set the constructor property (which is done by default by JavaScript but is lost as soon as you reset Moveable.prototype):

Moveable.prototype.constructor = Moveable;

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;
}

How to inherit correctly in javascript?

apply is not at all like executing the super class constructor. As a construct, it executes any function in the scope of the first provided argument. When you pass this to your Object1 function (which you execute as a function, not as a constructor), you are passing through the scope in which your second function (Object2) is being executed.

To see what's happening, try executing Object1("param") without new:

Object1("test");

When we execute new Object1("test") we get back an object that looks like this:

Object { prop1="p1 test", prop2="p2 test"}

But when we execute Object1("test") we get back undefined ... and on the object equal to this (most likely window in the case of browser JavaScript) we find two new variables ... prop1 and prop2.

What happened?

The key here is that:

Object1("param") !== new Object1("param");

The first version (Object1("param")) executes a function (Object1) in some already existing scope (in browser JavaScript the most common scope is the top-level scope where this === window.) This is the way you are invoking Object1 when you call Object1.apply(this).

The second version (new Object1("param")) executes a function as a constructor - that is, it creates a new object and sets this to be that new object. If we were to use the same apply function to demonstrate this we would write Object1.apply({});

For one of the most definitive answers I have ever seen on the subject of how to do OOP-like programming right in a JavaScript environment, see Bobince's answer to this question.

Proper way to inherit an object constructor's prototype?

Option 1 is definitely the better approach, because it creates a new object that inherits from but is not the same as User.prototype.

Option 2 means that any changes to Admin.prototype will be exactly reflected on User.prototype, which is not desirable; with that approach, they're the exact same object.

Demo:

function User(email, name) {
this.email = email;
this.name = name;
this.online = false;
}

User.prototype.login = function() {
this.online = true;
console.log(this.email, 'has logged in');
}

function Admin(...args) {
User.apply(this, args);
this.role = 'super admin';
}

Admin.prototype = User.prototype; //option 2

Admin.prototype.getAdminInfo = function() {
console.log('getting admin info');
}
const user = new User('useremail');
user.getAdminInfo();

Is this the 'standard' way to define inheritance chains in JavaScript?

The main manner which JavaScript supports inheritance is through prototypal inheritance. Specifically, objects in JavaScript delegate to other objects whenever a property lookup can't be found on the initial object. This delegation continues until the JavaScript engine reaches Object.prototype in which the property is either found or an error is thrown.

The current best practice to create objects with specific objects as their prototype is to use Object.create - you can take a look at more information here.

Here's an example:

var methods = {
method1: function () { console.log( 'something' ); },
method2: function () { return 'cool'; }
};

/*
* Now firstObj will delegate to methods whenever a property lookup can't
* be found on firstObj itself
*/
var firstObj = Object.create( methods );

// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property';

/*
* You can create a chain of delegations! Property lookup first happens on secondObj.
* If its not found there, it looks up the property in firstObj. If its not found there,
* then it looks up the property in methods. Finally, if not found, it tries
* Object.prototype
*/
var secondObj = Object.create ( firstObj );


Related Topics



Leave a reply



Submit