views:

382

answers:

2

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'?

+5  A: 

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.

CMS
1. Thanks for the pointer to differential inheritance.2. Does this mean no more constructors? I need to remember to set 'id' to MY_GLOBAL.nextId() every time I create a user?
Graham King
You're welcome @Graham, you're right, no more constructors needed with this method, although the currently available implementations on Firefox 3.7apre5, the latest WebKit Nightly builds and Chrome 5 Beta, are [not so fast](http://webreflection.blogspot.com/2010/03/new-constructor-vs-objectcreate.html) compared with plain old constructors, hopefully this will change in the near future. For the object creation, you could create a *factory* function(i.e. `function createUser(name) { ... }`, with all the needed logic to create your user objects within with `Object.create`.
CMS
A: 

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');
edwin