Related
I'm trying to understand inheritance in Javascript.
I know that every object has a prototype, which is an object it inherits properties from. I know that the .prototype property exists only on functions, and it is the object that will be set asthe prototype of objects created from this function, when it's used as a constructor.
I know that clasically an object's prototype is inaccessible, although some browsers support the __proto__ property. (But since it's not a 'classical' part of the language, I want to understand how to use the language without it).
So if all of this is correct (?), I want to underatand what the standard way is to define inheritance chains.
The only approach I can think of is this:
All objects that I want them to inherit from another object, have to be created through a constructor function. Their 'base object' would be set as the .prototype of their constructor.
And when I want one of them to be a 'base object' of other objects, I'd set it as the .prototype of another constructor. And so on.
This seems odd. Is there a way (in 'normal' JS) to set an object's 'base' directly? Or do I have to use constructors in the way described above, in order to create inheritance chains?
What would be the 'standard' way to create inheritance? Is the approach I describsd the standard approach?
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 );
Inheritance in JavaScript is a little difficult to understand at first because:
JavaScript is a prototypal object-oriented programming language (i.e. objects directly inherit from other objects). This means that there's no distinction between classes and objects. Objects which are used as classes are called prototypes.
Unfortunately, the traditional way to create an instance of a prototype is by using new (which makes people think that the instance inherits from the constructor function, and not the prototype). This is called the constructor pattern, and it's the main reason of confusion in JavaScript.
For this reason Object.create was introduced. It allowed objects to directly inherit from other objects. However, Object.create is slow as compared to using new. I had the same problem that you did and I was looking for an alternative; and I did come up with one.
The Traditional Way of OOP in JavaScript
Consider the following code:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = new Person;
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
This way of writing code suffers from several problems:
There is no encapsulation. The constructor function and the prototype methods are defined all over the place. It look incoherent. Like shaghetti. It doesn't look like one logical unit.
We make Man.prototype inherit from Person.prototype by setting it to new Person. However, in doing so we're initializing the firstname, lastname and gender properties on Man.prototype which is wrong.
The New Way of OOP in JavaScript
With the introduction of Object.create we can now write code like this:
function Person(firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
Person.prototype.getFullname = function () {
return this.firstname + " " + this.lastname;
};
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;
function Man(firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
The only change is that instead of Man.prototype = new Person we write Man.prototype = Object.create(Person.prototype). This solves the second problem of the traditional method. However, the code still looks like spaghetti.
However, Object.create is quite powerful. You could also use it to write object-oriented code without creating constructor functions at all. Some people call this the initializer pattern:
var person = {
init: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
};
var man = Object.create(person, {
init: {
value: function (firstname, lastname) {
person.init.call(this, firstname, lastname, "M");
}
}
});
var bobMarley = Object.create(man);
bobMarley.init("Bob", "Marley");
alert(bobMarley.getFullname());
This solves all the problems of the traditional method. However, it also introduces some new problems of its own:
The way of creating instances of prototypes is not consistent with the way of creating object literals.
You have to create an instance using Object.create and then initialize the new object using init. This is much slower than simply using new.
My Way of OOP is JavaScript
To solve this problem, I wrote my own functions for OOP in JavaScript:
var Person = defclass({
constructor: function (firstname, lastname, gender) {
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
},
getFullname: function () {
return this.firstname + " " + this.lastname;
}
});
var Man = extend(Person, {
constructor: function (firstname, lastname) {
Person.call(this, firstname, lastname, "M");
}
});
var bobMarley = new Man("Bob", "Marley");
alert(bobMarley.getFullname());
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, properties) {
var prototype = Object.create(constructor.prototype);
var keys = Object.keys(properties);
var length = keys.length;
var index = 0;
while (index < length) {
var key = keys[index++];
prototype[key] = properties[key];
}
return defclass(prototype);
}
I defined two functions defclass and extend for OOP in JavaScript. The defclass function creates a “class” from a prototype. This is possible because prototypes and classes are isomorphic.
The extend function is for inheritance. It creates an instance of the prototype of a constructor and copies some properties onto it before returning the “class” of the new prototype.
This is the way I currently create prototype chains in JavaScript. It has the following advantages over other methods:
Every “class” is encapsulated. There are no prototype methods dangling all over the place. It doesn't look like spaghetti.
The extend function uses Object.create for inheritance. Hence no extra properties are added to the new prototype. It's a blank prototype.
You don't have to worry about resetting the constructor property on the prototype. It is automatically done for you.
The defclass and the extend functions are consistent unlike object literals and the Object.create functions in the initializer pattern.
We create instances using new instead of Object.create and init. Hence the resulting code is much faster.
I could be wrong now, but I don't think so. Hope that helps.
You can inherit by 2 ways in javascript - classical and prototypal
Classical
function inherit (C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}
Prototypal
function inherit (o) {
function F() {}
F.prototype = o;
return new F();
}
JavaScript uses a Prototype system, which is fundamentally different than a Class system. This is my first serious encounter with the language. I had fooled around with it previously, but this is the first time I built a system with proper OO, inheritance, polymorphism, etc.
From what I read there seems to be a few common methods to do member function inheritance in Javascript. Assuming you have a parent foo as following
foo = function(){ this.one = 1; }
foo.prototype.func1 = function(){return this.one;}
The MDN Introduction to JavaScript Inheritance suggests the naive approach of invoking the parent's method in the context of the child, as shown below.
bar = function(){ foo.call(this); }
bar.prototype = Object.create(foo.prototype);
bar.prototype.func1 = function(){ return this.one + foo.prototype.func1();}
This has the advantage of being simple to understand, but can become cumbersome as pointed out in this Salsify Blog post. The blog post outlines an alternate method where a super property is defined in the child prototype, and the name of each member function is attached as a property to the method. This method, however, relies on the caller property of a method, which the article points out will soon be deprecated. Rather than duplicate the entire post, I believe a summary of the important points are these
Object.defineProperty(bar.prototype, "super", {
get: function get() {
...
// methodName is set as a property on each method in an omitted code segment
methodName = get.caller.methodName;
...
Object.getPrototypeOf(this.prototype)[methodName]
}
}
Which is to say that you find the method with the same name in your prototype's prototype. I was wondering if this can be done in a simpler manner, without having to attach the method name as a parameter and without the Function.caller.
foo.prototype.super = function(method) {
superMethod = Object.getPrototypeOf(this.constructor.prototype)[method];
return superMethod.call(this, Array.prototype.slice.call(arguments, 1));
}
bar.prototype.func1 = function(){ return this.one + super('func1'); }
I'm making a number of assumptions in the above, I'd like to verify some assumptions.
new bar().constructor.prototype === Object.getPrototypeOf(new bar())
If the above is always true, is one preferable over the other?
The Parent's member function will always live in the child's prototype's prototype (assuming that neither of the prototypes were mutated after object creation)
That Object.getPrototypeOf() is not the "language support for accessing super methods" that the blog refers to as being added in ES6
If Object.getPrototypeOf() isn't that language support, what is?
After seeing the error of using this, which does not change throughout the execution and always refers to the instance of the subclass, I've revisited and am thinking I need something like this
Grandfather = function(){};
Grandfather.prototype.function1 = function(){console.log("I am the Grandfather");};
Father = function(){Grandfather.apply(this);};
Father.prototype = Object.create(Grandfather.prototype);
Father.prototype.function1 = function f(){ f.super(); console.log("I am the Father");};
Father.prototype.function1.super = Grandfather.prototype.function1;
Child = function(){Father.apply(this);}
Child.prototype = Object.create(Father.prototype);
Child.prototype.function1 = function f(){ f.super(); console.log("I am the Child");};
Child.prototype.function1.super = Father.prototype.function1;
c = new Child();
c.function1();
// I am the Grandfather
// I am the Father
// I am the Child
And so the question becomes, how to set the super property on to each function in some automatic way?
One such way to do this is shown below, it has the benefit that functions added to the prototype after objects are instantiated still receive the benefit of being able to call superFunc, whereas an approach that sets a super property at class extension time would not set such a property if functions are added to the prototype later.
The downsides of this approach are that it only works in single threaded environment and that it requires functionality inherited from a common base class. It is not threadsafe since some state is held in a what is effectively a static variable of the function. This is fine for my purposes since browsers still have single threaded JavaScript. The requirement that all classes inherit from some base class containing this method isn't a huge blocker (especially if you do a "bad thing" and insert this into Object's prototype).
Grandfather.prototype.superFunc = function f(funcName){
currentPrototype = Object.getPrototypeOf(f.startingPrototype || Object.getPrototypeOf(this));
f.startingPrototype = currentPrototype;
return currentPrototype[funcName]();
}
Child.prototype.function2 = function(){this.superFunc('function2'); console.log("Still in the Child");};
Father.prototype.function2 = function(){this.superFunc('function2'); console.log("Still in the Father");};
GrandFather.prototype.function2 = function(){console.log("Still in the Grandfather");};
c = new Child();
c.function2();
// Still in the Grandfather
// Still in the Father
// Still in the Child
Question 1
new Bar().constructor.prototype should equal Object.getPrototypeOf(new Bar()), provided you haven't overrided Bar.prototype.constructor or Bar.prototype, or return a different object in the Bar constructor. Here's an example:
function Bar() {}
var foo = new Bar();
foo.constructor.prototype === Object.getPrototypeOf(foo); // true
function Bar2() {}
var foo2 = new Bar2();
Bar2.prototype = {};
foo2.constructor.prototype === Object.getPrototypeOf(foo2); // false
function Bar3() {}
var foo3 = new Bar3();
Bar3.prototype.constructor = function NotBar3() {};
foo3.constructor.prototype === Object.getPrototypeOf(foo3); // false
Question 2
If you're looking to get the actual prototype of an object, use Object.getPrototypeOf, as that's unaffected by any of the changes shown above.
Question 3
No, you will not be able to access Foo from new Bar(). In your example, new Bar() would not inherit from Foo.prototype and as a result, there's no way to access Foo unless you make it inherit from Foo.prototype or assign Foo to a property of new Bar() or Bar.prototype.
Question 4/5
No, that's not what they're referring to. ES6 will introduce a separate class contruct, where super takes on a special meaning (similar to how super works in other languages with classes). Here's an example of how classes work in ES6:
class Foo {
constructor() {
this.one = 1;
}
func1() {
return this.one;
}
}
class Bar extends Foo {
func1() {
return this.one + super();
}
}
When you use super in the way you do it'll break when inheritance is more than 2 levels.
Assuming you'd use it the following way:
//changed super to this.super since super is not shown to exist in global scope
bar.prototype.func1(){ return this.one + this.super('func1'); }
See the following example:
function GrandFather(){
this.i = 0;
};
GrandFather.prototype.test = function(){
console.log('test in GrandFather');
};
function Father(){
GrandFather.call(this);
};
Father.prototype = Object.create(GrandFather.prototype);
Father.prototype.constructor = Father;
Father.prototype.super = GrandFather.prototype;
Father.prototype.test = function(){
console.log('test in Father');
//prevent too many recursions
this.i++;
if(this.i>5){
return;
}
this.super.test.call(this);//because test in child was called
// with Child instance as invoking object this will be Child
// and this.super will be Father.prototype
};
function Child(){
Father.call(this);
}
Child.prototype = Object.create(Father.prototype);
Child.prototype.constructor = Child;
Child.prototype.super = Father.prototype;
Child.prototype.test = function(){
console.log('test in Child');
this.super.test.call(this);//because invoking object is Child
//this.super in Father is Child
};
var c = new Child();
c.test();
It's also common practice to start a constructor function with a capital so it's better to use Foo and Bar for constructor function names.
If you want to go through all the trouble of simulating super in JavaScript then the following way would be slightly more robust: http://ejohn.org/blog/simple-javascript-inheritance/
I've been reading Is JavaScript's "new" keyword considered harmful? and this Adobe Article on using prototypal inheritance rather than 'new'. The Adobe article has a 'new' example:
function Foo() {
this.name = "foo";
}
Foo.prototype.sayHello = function() {
alert("hello from " + this.name);
};
...that it replaces with:
var foo = {
name: "foo",
sayHello: function() {
alert("hello from " + this.name);
}
};
Here the 'sayHello' method isn't attached to the object's prototype. Doesn't this mean 'sayHello' gets unnecessarily duplicated in memory (I know V8 avoids this, but in older JS engines, for example) as it is copied to all objects that inherit from foo?
Shouldn't it be:
var foo = {
name: "foo",
prototype: {
sayHello: function() {
alert("hello from " + this.name);
}
}
};
Or similar?
The method is not attached to the prototype because this very object becomes the prototype that is attached to newly created objects with Object.create. And you don't need a prototype of the prototype.
Remember that Object.create doesn't deep clone objects. Rather, it is equivalent to something like
Object.create = function(proto) {
function F() {}
F.prototype = proto;
return new F();
}
(actual implementation is more complicated but this short version illustrates the idea)
All newly created objects "inherit" the method from the foo which acts as the prototype and there is no duplication here.
No, it won't get duplicated if you create another object using that as a protoype. The almost equivalent code using Object.create is actually slightly different, you are not using the prototype, you are just creating an object. To use prototypal inheritance, do the following. Note that using new still sets up the prototype chain so the title of the article you linked to is not very accurate and you are still sharing the properties on a single object.
var foo = {
name: "foo",
sayHello: function() {
alert("hello from " + this.name);
}
};
var extended = Object.create(foo);
var extended2 = Object.create(foo);
extended.name = "first";
extended2.name = "second";
extended.sayHello(); // hello from first
extended2.sayHello(); // hello from second
// Methods are shared, outputs true
console.log(extended.sayHello === extended2.sayHello)
// Now if you delete the property again, it will go up the chain
delete extended.name;
extended.sayHello(); // hello from foo
You could also just do
var extended = Object.create(Foo.prototype);
It would get duplicated if you create a constructor to get new instances instead of Object.create or new Foo
function createFoo() {
return {
name: "foo",
sayHello: function() {
alert("hello from " + this.name);
}
}
}
var a = createFoo();
var b = createFoo();
// The function objects are not shared
alert('Are functions the same? ' + a.sayHello === b.createFoo);
They would also not be shared if you use the closure approach to creating objects. Crockford suggests that to create truly private members. I don't use it because it doesn't use the prototype chain and inheritance is tricky to implement without just copying properties.
Function Foo() {
var name = 'foo';
this.sayHello = function () {
alert(name);
};
this.setName = function (newName) {
name = newName;
};
}
var a = new Foo();
var b = new Foo();
console.log(a.sayHello === b.sayHello); // outputs false
Going to answer my own question here, for my own notes and also to help explain to others:
Q. Using Object.create(), should exemplars have methods attached to their prototype property?
A. No, because Object.create(parentObject) itself sets up the parentObject as the prototype of a dynamically-created constructor.
Also: prototype is always a property on constructor Functions - not on regular Objects. Eg:
var someArray = [];
someArray.__proto__ === Array.prototype
Object.create() dynamically creates constructors, setting the prototype on them to the object in its first argument.
So if I was to create an object literal like this..
var person1 = {
age : 28,
name : 'Pete' }
Then I add a function to the object...
person1.sayHelloName = function(){
return 'Hello ' + this.name;
};
alert(person1.sayHelloName());
Im wondering where the 'prototype' keyword comes into all this. I cant add a prototype to an object (only to a function constructor) so would I be able/have to use the prototype in this scenario.
You can't use the prototype in this way because, like you say, it's used to add properties to a constructor which must be defined with the function() keyword. What you are doing in your example is extending the person1 object, but that object is just a single variable and not an instantiable class. Meaning you can't "create" another person1 using the new keyword, whereas you could if it were an instance of a defined class.
You don't have to (can) use prototype here. As you have only one instance of something (one object), you can set the functions directly on the object.
FWIW, ECMAScript 5 defines Object.getPrototypeOf with which you can get the protoype of any object (only available in "modern" browsers).
var Person = function() {
this.age = 28;
this.name = "peter";
}
Person.prototype.sayHelloName = function(){
return 'Hello ' + this.name;
};
var person1 = new Person;
You would bring prototype into play to define methods all instances of a class have. In your case that doesn't make any sense since you only create a standalone object that doesn't belong to a class.
function Person (age, name)
{
this.age = age;
this.name = name;
}
Person.prototype.sayHelloName = function ()
{
return 'Hello ' + this.name;
}
person1 = new Person(28,'Pete');
alert(person1.sayHelloName());
This only makes sense of course if you are expecting to be creating more than one person.
There's no need for you to use 'prototype' in object literal. Only when you use the constructor pattern (and some other design patterns), that you can use 'prototype'.
Javascript 1.9.3 / ECMAScript 5 introduces Object.create, which Douglas Crockford amongst others has been advocating for a long time. How do I replace new in the code below with Object.create?
var UserA = function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
}
UserA.prototype.sayHello = function() {
console.log('Hello '+ this.name);
}
var bob = new UserA('bob');
bob.sayHello();
(Assume MY_GLOBAL.nextId exists).
The best I can come up with is:
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB);
bob.init('Bob');
bob.sayHello();
There doesn't seem to be any advantage, so I think I'm not getting it. I'm probably being too neo-classical. How should I use Object.create to create user 'bob'?
With only one level of inheritance, your example may not let you see the real benefits of Object.create.
This methods allows you to easily implement differential inheritance, where objects can directly inherit from other objects.
On your userB example, I don't think that your init method should be public or even exist, if you call again this method on an existing object instance, the id and name properties will change.
Object.create lets you initialize object properties using its second argument, e.g.:
var userB = {
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB, {
'id' : {
value: MY_GLOBAL.nextId(),
enumerable:true // writable:false, configurable(deletable):false by default
},
'name': {
value: 'Bob',
enumerable: true
}
});
As you can see, the properties can be initialized on the second argument of Object.create, with an object literal using a syntax similar to the used by the Object.defineProperties and Object.defineProperty methods.
It lets you set the property attributes (enumerable, writable, or configurable), which can be really useful.
There is really no advantage in using Object.create(...) over new object.
Those advocating this method generally state rather ambiguous advantages: "scalability", or "more natural to JavaScript" etc.
However, I have yet to see a concrete example that shows that Object.create has any advantages over using new. On the contrary there are known problems with it. Sam Elsamman describes what happens when there are nested objects and Object.create(...) is used:
var Animal = {
traits: {},
}
var lion = Object.create(Animal);
lion.traits.legs = 4;
var bird = Object.create(Animal);
bird.traits.legs = 2;
alert(lion.traits.legs) // shows 2!!!
This occurs because Object.create(...) advocates a practice where data is used to create new objects; here the Animal datum becomes part of the prototype of lion and bird, and causes problems as it is shared. When using new the prototypal inheritance is explicit:
function Animal() {
this.traits = {};
}
function Lion() { }
Lion.prototype = new Animal();
function Bird() { }
Bird.prototype = new Animal();
var lion = new Lion();
lion.traits.legs = 4;
var bird = new Bird();
bird.traits.legs = 2;
alert(lion.traits.legs) // now shows 4
Regarding, the optional property attributes that are passed into Object.create(...), these can be added using Object.defineProperties(...).
Object.create is not yet standard on several browsers, for example IE8, Opera v11.5, Konq 4.3 do not have it. You can use Douglas Crockford's version of Object.create for those browsers but this doesn't include the second 'initialisation object' parameter used in CMS's answer.
For cross browser code one way to get object initialisation in the meantime is to customise Crockford's Object.create. Here is one method:-
Object.build = function(o) {
var initArgs = Array.prototype.slice.call(arguments,1)
function F() {
if((typeof o.init === 'function') && initArgs.length) {
o.init.apply(this,initArgs)
}
}
F.prototype = o
return new F()
}
This maintains Crockford prototypal inheritance, and also checks for any init method in the object, then runs it with your parameter(s), like say new man('John','Smith'). Your code then becomes:-
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}} // For example
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.build(userB, 'Bob'); // Different from your code
bob.sayHello();
So bob inherits the sayHello method and now has own properties id=1 and name='Bob'. These properties are both writable and enumerable of course. This is also a much simpler way to initialise than for ECMA Object.create especially if you aren't concerned about the writable, enumerable and configurable attributes.
For initialisation without an init method the following Crockford mod could be used:-
Object.gen = function(o) {
var makeArgs = arguments
function F() {
var prop, i=1, arg, val
for(prop in o) {
if(!o.hasOwnProperty(prop)) continue
val = o[prop]
arg = makeArgs[i++]
if(typeof arg === 'undefined') break
this[prop] = arg
}
}
F.prototype = o
return new F()
}
This fills the userB own properties, in the order they are defined, using the Object.gen parameters from left to right after the userB parameter. It uses the for(prop in o) loop so, by ECMA standards, the order of property enumeration cannot be guaranteed the same as the order of property definition. However, several code examples tested on (4) major browsers show they are the same, provided the hasOwnProperty filter is used, and sometimes even if not.
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}; // For example
var userB = {
name: null,
id: null,
sayHello: function() {
console.log('Hello '+ this.name);
}
}
var bob = Object.gen(userB, 'Bob', MY_GLOBAL.nextId());
Somewhat simpler I would say than Object.build since userB does not need an init method. Also userB is not specifically a constructor but looks like a normal singleton object. So with this method you can construct and initialise from normal plain objects.
TL;DR:
new Computer() will invoke the constructor function Computer(){} for one time, while Object.create(Computer.prototype) won't.
All the advantages are based on this point.
Sidenote about performance: Constructor invoking like new Computer() is heavily optimized by the engine, so it may be even faster than Object.create.
You could make the init method return this, and then chain the calls together, like this:
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
return this;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var bob = Object.create(userB).init('Bob');
Another possible usage of Object.create is to clone immutable objects in a cheap and effective way.
var anObj = {
a: "test",
b: "jest"
};
var bObj = Object.create(anObj);
bObj.b = "gone"; // replace an existing (by masking prototype)
bObj.c = "brand"; // add a new to demonstrate it is actually a new obj
// now bObj is {a: test, b: gone, c: brand}
Notes: The above snippet creates a clone of an source object (aka not a reference, as in cObj = aObj). It benefits over the copy-properties method (see 1), in that it does not copy object member properties. Rather it creates another -destination- object with it's prototype set on the source object. Moreover when properties are modified on the dest object, they are created "on the fly", masking the prototype's (src's) properties.This constitutes a fast an effective way of cloning immutable objects.
The caveat here is that this applies to source objects that should not be modified after creation (immutable). If the source object is modified after creation, all the clone's unmasked properties will be modified, too.
Fiddle here(http://jsfiddle.net/y5b5q/1/) (needs Object.create capable browser).
I think the main point in question - is to understand difference between new and Object.create approaches. Accordingly to this answer and to this video new keyword does next things:
Creates new object.
Links new object to constructor function (prototype).
Makes this variable point to the new object.
Executes constructor function using the new object and implicit perform return this;
Assigns constructor function name to new object's property constructor.
Object.create performs only 1st and 2nd steps!!!
In code example provided in question it isn't big deal, but in next example it is:
var onlineUsers = [];
function SiteMember(name) {
this.name = name;
onlineUsers.push(name);
}
SiteMember.prototype.getName = function() {
return this.name;
}
function Guest(name) {
SiteMember.call(this, name);
}
Guest.prototype = new SiteMember();
var g = new Guest('James');
console.log(onlineUsers);
As side effect result will be:
[ undefined, 'James' ]
because of Guest.prototype = new SiteMember();
But we don't need to execute parent constructor method, we need only make method getName to be available in Guest.
Hence we have to use Object.create.
If replace Guest.prototype = new SiteMember();
to Guest.prototype = Object.create(SiteMember.prototype); result be:
[ 'James' ]
Sometimes you cannot create an object with NEW but are still able to invoke the CREATE method.
For example: if you want to define a Custom Element it must derive from HTMLElement.
proto = new HTMLElement //fail :(
proto = Object.create( HTMLElement.prototype ) //OK :)
document.registerElement( "custom-element", { prototype: proto } )
The advantage is that Object.create is typically slower than new on most browsers
In this jsperf example, in a Chromium, browser new is 30 times as fast as Object.create(obj) although both are pretty fast. This is all pretty strange because new does more things (like invoking a constructor) where Object.create should be just creating a new Object with the passed in object as a prototype (secret link in Crockford-speak)
Perhaps the browsers have not caught up in making Object.create more efficient (perhaps they are basing it on new under the covers ... even in native code)
Summary:
Object.create() is a Javascript function which takes 2 arguments and returns a new object.
The first argument is an object which will be the prototype of the newly created object
The second argument is an object which will be the properties of the newly created object
Example:
const proto = {
talk : () => console.log('hi')
}
const props = {
age: {
writable: true,
configurable: true,
value: 26
}
}
let Person = Object.create(proto, props)
console.log(Person.age);
Person.talk();
Practical applications:
The main advantage of creating an object in this manner is that the prototype can be explicitly defined. When using an object literal, or the new keyword you have no control over this (however, you can overwrite them of course).
If we want to have a prototype The new keyword invokes a constructor function. With Object.create() there is no need for invoking or even declaring a constructor function.
It can Basically be a helpful tool when you want create objects in a very dynamic manner. We can make an object factory function which creates objects with different prototypes depending on the arguments received.
You have to make a custom Object.create() function. One that addresses Crockfords concerns and also calls your init function.
This will work:
var userBPrototype = {
init: function(nameParam) {
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
function UserB(name) {
function F() {};
F.prototype = userBPrototype;
var f = new F;
f.init(name);
return f;
}
var bob = UserB('bob');
bob.sayHello();
Here UserB is like Object.create, but adjusted for our needs.
If you want, you can also call:
var bob = new UserB('bob');
While Douglas Crockford used to be a zealous advocate of Object.create() and he is basically the reason why this construct actually is in javascript, he no longer has this opinion.
He stopped using Object.create, because he stopped using this keyword altogether as it causes too much trouble. For example, if you are not careful it can easily point to the global object, which can have really bad consequences. And he claims that without using this Object.create does not make sense anymore.
You can check this video from 2014 where he talks at Nordic.js:
https://www.youtube.com/watch?v=PSGEjv3Tqo0
new and Object.create serve different purposes. new is intended to create a new instance of an object type. Object.create is intended to simply create a new object and set its prototype. Why is this useful? To implement inheritance without accessing the __proto__ property. An object instance's prototype referred to as [[Prototype]] is an internal property of the virtual machine and is not intended to be directly accessed. The only reason it is actually possible to directly access [[Prototype]] as the __proto__ property is because it has always been a de-facto standard of every major virtual machine's implementation of ECMAScript, and at this point removing it would break a lot of existing code.
In response to the answer above by 7ochem, objects should absolutely never have their prototype set to the result of a new statement, not only because there's no point calling the same prototype constructor multiple times but also because two instances of the same class can end up with different behavior if one's prototype is modified after being created. Both examples are simply bad code as a result of misunderstanding and breaking the intended behavior of the prototype inheritance chain.
Instead of accessing __proto__, an instance's prototype should be written to when an it is created with Object.create or afterward with Object.setPrototypeOf, and read with Object.getPrototypeOf or Object.isPrototypeOf.
Also, as the Mozilla documentation of Object.setPrototypeOf points out, it is a bad idea to modify the prototype of an object after it is created for performance reasons, in addition to the fact that modifying an object's prototype after it is created can cause undefined behavior if a given piece of code that accesses it can be executed before OR after the prototype is modified, unless that code is very careful to check the current prototype or not access any property that differs between the two.
Given
const X = function (v) { this.v = v };
X.prototype.whatAmI = 'X';
X.prototype.getWhatIAm = () => this.whatAmI;
X.prototype.getV = () => this.v;
the following VM pseudo-code is equivalent to the statement const x0 = new X(1);:
const x0 = {};
x0.[[Prototype]] = X.prototype;
X.prototype.constructor.call(x0, 1);
Note although the constructor can return any value, the new statement always ignores its return value and returns a reference to the newly created object.
And the following pseudo-code is equivalent to the statement const x1 = Object.create(X.prototype);:
const x0 = {};
x0.[[Prototype]] = X.prototype;
As you can see, the only difference between the two is that Object.create does not execute the constructor, which can actually return any value but simply returns the new object reference this if not otherwise specified.
Now, if we wanted to create a subclass Y with the following definition:
const Y = function(u) { this.u = u; }
Y.prototype.whatAmI = 'Y';
Y.prototype.getU = () => this.u;
Then we can make it inherit from X like this by writing to __proto__:
Y.prototype.__proto__ = X.prototype;
While the same thing could be accomplished without ever writing to __proto__ with:
Y.prototype = Object.create(X.prototype);
Y.prototype.constructor = Y;
In the latter case, it is necessary to set the constructor property of the prototype so that the correct constructor is called by the new Y statement, otherwise new Y will call the function X. If the programmer does want new Y to call X, it would be more properly done in Y's constructor with X.call(this, u)
new Operator
This is used to create object from a constructor function
The new keywords also executes the constructor function
function Car() {
console.log(this) // this points to myCar
this.name = "Honda";
}
var myCar = new Car()
console.log(myCar) // Car {name: "Honda", constructor: Object}
console.log(myCar.name) // Honda
console.log(myCar instanceof Car) // true
console.log(myCar.constructor) // function Car() {}
console.log(myCar.constructor === Car) // true
console.log(typeof myCar) // object
Object.create
You can also use Object.create to create a new object
But, it does not execute the constructor function
Object.create is used to create an object from another object
const Car = {
name: "Honda"
}
var myCar = Object.create(Car)
console.log(myCar) // Object {}
console.log(myCar.name) // Honda
console.log(myCar instanceof Car) // ERROR
console.log(myCar.constructor) // Anonymous function object
console.log(myCar.constructor === Car) // false
console.log(typeof myCar) // object
I prefer a closure approach.
I still use new.
I don't use Object.create.
I don't use this.
I still use new as I like the declarative nature of it.
Consider this for simple inheritance.
window.Quad = (function() {
function Quad() {
const wheels = 4;
const drivingWheels = 2;
let motorSize = 0;
function setMotorSize(_) {
motorSize = _;
}
function getMotorSize() {
return motorSize;
}
function getWheelCount() {
return wheels;
}
function getDrivingWheelCount() {
return drivingWheels;
}
return Object.freeze({
getWheelCount,
getDrivingWheelCount,
getMotorSize,
setMotorSize
});
}
return Object.freeze(Quad);
})();
window.Car4wd = (function() {
function Car4wd() {
const quad = new Quad();
const spareWheels = 1;
const extraDrivingWheels = 2;
function getSpareWheelCount() {
return spareWheels;
}
function getDrivingWheelCount() {
return quad.getDrivingWheelCount() + extraDrivingWheels;
}
return Object.freeze(Object.assign({}, quad, {
getSpareWheelCount,
getDrivingWheelCount
}));
}
return Object.freeze(Car4wd);
})();
let myQuad = new Quad();
let myCar = new Car4wd();
console.log(myQuad.getWheelCount()); // 4
console.log(myQuad.getDrivingWheelCount()); // 2
console.log(myCar.getWheelCount()); // 4
console.log(myCar.getDrivingWheelCount()); // 4 - The overridden method is called
console.log(myCar.getSpareWheelCount()); // 1
Feedback encouraged.