I came to a code that contains these lines
var data = function() {
function Metadata() { /*some initialization here*/ }
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
return Metadata;
}
I struggle to understand what is actually going on, and how to use the returning object. If I understand it correctly, data
will now be an object that should be initialized like this
var d = new data()
But I don't understand the following lines and why Object.create()
is used instead of the new
keyword:
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
What do they do? Are they necessary? And what is the difference between Object.create
and new
?
I came to a code that contains these lines
var data = function() {
function Metadata() { /*some initialization here*/ }
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
return Metadata;
}
I struggle to understand what is actually going on, and how to use the returning object. If I understand it correctly, data
will now be an object that should be initialized like this
var d = new data()
But I don't understand the following lines and why Object.create()
is used instead of the new
keyword:
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
What do they do? Are they necessary? And what is the difference between Object.create
and new
?
4 Answers
Reset to default 5 +100JavaScript is a prototype-based language.
It means that it don't use the class keyword as in other languages. Instead JavaScript use functions as classes.
In your example the data variable can be assimilated to a class:
var Data = function() { ... }
To create an instance of this class we use the new keyword assigning the result of type object to a variable.
var data = new Data()
Since ECMA Script 6 we can use the instantiation method Object.create()
that create an uninitiated object with the specified prototype object and properties. It takes in argument the object which should be the prototype of the newly-created object. (It also copy the constructor)
So the following lines are a way to make Metadata extending the Backend object and keeps its own constructor:
// Metadata extends Backend
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;
But this code is not exactly equivalent to Metadata.prototype = new Backend();
. See this example:
//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
/*
* Then the results are different (see code snippet at the end of this post)
*/
//result1 = 'undefined'
var data1 = new Metadata1();
var result1 = data1.publicProperty;
//result2 = 'SomeValue'
var data2 = new Metadata2();
var result2 = data2.publicProperty;
In fact both are very similar, the main difference is that new
keyword actually runs constructor code, whereas Object.create
will not execute code.
One other difference is that with Object.create
you can create an object that doesn't inherit from anything (Object.create(null)
).
Whereas if you do Metadata.prototype = null
the newly created object will inherit from Object.prototype
Note: In some older browser (IE8 and below) you can use this equivalent code to Object.create
:
Object.create = function(o){
function F(){}
F.prototype=o;
return new F();
}
Here is the working code snippet that shows the differences between the two approach
//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
//result: 'undefined'
var data1 = new Metadata1();
$("#result1").text("result1: " + (typeof data1.publicProperty=='undefined' ? 'undefined' : data1.publicProperty));
//result: 'SomeValue'
var data2 = new Metadata2();
$("#result2").text("result2: " + (typeof data2.publicProperty=='undefined' ? 'undefined' : data2.publicProperty));
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result1"></div>
<div id="result2"></div>
Those two lines are the prototypical way of having Metadata
extend Backend
.
Classes in JavaScript are defined as functions. If you define the prototype
on a function, you can use the new
keyword (or Object.create
) to create an instance of the class. The functions/properties that are on the prototype will be on the new instance of the class you create.
In the above code, Metadata.prototype
is being set to an instance of Backend
, so Metadata.prototype
will inherit call the methods/properties on Backend.prototype
.
You can find more at Inheritance and the prototype chain.
In JavaScript there are no classes, and instead we have constructors that can be called with the new
keyword to create new objects, so we will get the same behavior as classes and instanciation.
And these two lines are used to express inheritance, and to make Metadata
extends Backend
, in the line:
Metadata.prototype = Object.create(Backend.prototype);
We define the prototype of Metadata
, object which should be the prototype of the newly-created object.
While in this line:
Metadata.prototype.constructor = Metadata;
We define the constructor of Metadata
which will be used to create new instances of Metadata
.
And inheritance can be tested like this:
var meta = new Metadata();
console.log('Is meta an instance of Metadata? ' + (meta instanceof Metadata)); // true
console.log('Is meta an instance of Backend? ' + (meta instanceof Backend)); // true
And you can find more about it with another example in the Object.create() documentaion here.
Object.create
is an ES6 method, it creates an object with the given object as prototype.
Metadata.prototype = Object.create(Backend.prototype);
So the above line means Metadata
inherits all properties and methods from Backend
. It's somehow similar to the following line before ES6:
Metadata.prototype = new Backend();
However, Metadata
also inherits constructor
property from Backend
:
var metaData = new Metadata();
metaData.constructor; // function Backend()
This look weird because metaData
is actually constructed with Metadata
, so we fix it like this:
Metadata.prototype.constructor = Metadata;
var metaData = new Metadata();
metaData.constructor; // function Metadata()
Notice that this doesn't mess with instanceof
operator:
metaData instanceof Metadata // true
metaData instanceof Backend // true