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
Converting Between Strings and Arraybuffers
Is It Not Possible to Stringify an Error Using Json.Stringify
Typeerror: $(...).Modal Is Not a Function with Bootstrap Modal
Clientheight/Clientwidth Returning Different Values on Different Browsers
How to Hover in Reactjs? - Onmouseleave Not Registered During Fast Hover Over
Use :Hover to Modify the CSS of Another Class
CSS Transitions Do Not Work When Assigned Trough JavaScript
Synchronized Scrolling Using Jquery
Mydiv.Style.Display Returns Blank When Set in Master Stylesheet
Adding a Parameter to the Url with JavaScript
.Trim() in JavaScript Not Working in Ie
Change the Selected Value of a Drop-Down List with Jquery
Dynamically Load a JavaScript File
How to Properly Reuse Connection to Mongodb Across Nodejs Application and Modules
How to Parse a CSV String With JavaScript, Which Contains Comma in Data