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'?
You could make the
init
method returnthis
, and then chain the calls together, like this:You have to make a custom
Object.create()
function. One that addresses Crockfords concerns and also calls your init function.This will work:
Here UserB is like Object.create, but adjusted for our needs.
If you want, you can also call:
new
andObject.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 withObject.create
or afterward withObject.setPrototypeOf
, and read withObject.getPrototypeOf
orObject.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 referencethis
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, otherwisenew Y
will call the functionX
. If the programmer does wantnew Y
to callX
, it would be more properly done in Y's constructor withX.call(this, u)
TL;DR:
new Computer()
will invoke the constructor functionComputer(){}
for one time, whileObject.create(Computer.prototype)
won't.All the advantages are based on this point.
Though for some inside-engine-optimization reasons,
Object.create
may be slower, which is not intuitive.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.
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 yourinit
method should be public or even exist, if you call again this method on an existing object instance, theid
andname
properties will change.Object.create
lets you initialize object properties using its second argument, e.g.: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 theObject.defineProperties
andObject.defineProperty
methods.It lets you set the property attributes (
enumerable
,writable
, orconfigurable
), which can be really useful.