Object.create(): the New Way to Create Objects in JavaScript
There are a lot of ways to create Objects in JavaScript, perhaps even more to integrate inheritance into them. Just when you thought that you've seen every possible way to create JS objects, I'm here to announce that there's yet another: the new Object create() method. Wouldn't you know it, there are some pretty good reasons to use it. But before we get into that, let's get familiar with how it works.
JS Object Creation Revisited
Let's quickly review some typical ways that objects are created in JS right now.
Most people define a constructor function and then create an object by using the new keyword:
function Car (desc) { this.desc = desc; this.color = "red"; this.getInfo = function getInfo() { return 'A ' + this.color + ' ' + this.desc + '.'; }; } //instantiate object using the constructor function var car = new Car('Porsche boxter'); car.color = "blue"; alert(car.getInfo()); //displays 'A blue Porsche boxter.'
A variation on the above theme is to create a constructor function, but to append methods to the Object prototype. That shares the method across objects:
function Car (desc) { this.desc = desc; this.color = "red"; } Car.prototype.getInfo = function() { return 'A ' + this.color + ' ' + this.desc + '.'; }; A more sophisticated use of the prototype property is to set it in one fell swoop using either a function or an object literal: function Car (desc) { this.desc = desc; this.color = "red"; } Car.prototype = { getInfo: function() { return 'A ' + this.color + ' ' + this.desc + '.'; }, drive: function() { //DO SOMETHING }, stop: function() { //DO SOMETHING } };
The Object.create() Method
The Object.create() method can just as adeptly create our Car object. It accepts either one or two properties as follows:
Object.create(proto [, propertiesObject ])
The first argument is the prototype to extend. If you aren't subclassing another object then you must pass a null value to the function. The second optional argument is an object containing the object's property descriptors. More on those in a bit.
We already have a Car prototype, so it makes sense to pass it to Object.create(). Unfortunately, what makes sense isn't always what works!
function Car (desc) { this.desc = desc; this.color = "red"; } Car.prototype = { getInfo: function() { return 'A ' + this.color + ' ' + this.desc + '.'; } }; //instantiate object using the constructor function var car = Object.create(Car.prototype); car.color = "blue"; alert(car.getInfo()); //displays 'A blue undefined.' ??!
The description is lost. So why is that? Simple; the create() method only uses the prototype and not the constructor. Hence, Object.create() is an excellent choice for creating an object without going through its constructor. We'll be examining that application in the next instalment. For now, let's tackle how to assign the description.
The solution of course is to supply it via the second parameter.
While we're at it, why not assign the color property as well using a Properties Object:
var Car2 = Object.create(null); //this is an empty object, like {} Car2.prototype = { getInfo: function() { return 'A ' + this.color + ' ' + this.desc + '.'; } }; var car2 = Object.create(Car2.prototype, { //value properties color: { writable: true, configurable:true, value: 'red' }, //concrete desc value rawDesc: { writable: false, configurable:true, value: 'Porsche boxter' }, // data properties (assigned using getters and setters) desc: { configurable:true, get: function () { return this.rawDesc.toUpperCase(); }, set: function (value) { this.rawDesc = value.toLowerCase(); } } }); car2.color = 'blue'; alert(car2.getInfo()); //displays 'A RED PORSCHE BOXTER.'
It looks a little confusing at first glance because each property has its own set of properties known collectively as a descriptor. A Descriptor is an object that can be one of two types - Data Descriptors or Accessor Descriptors.
Data Descriptors
- writable: Whether the concrete value of the property may be changed. Only applies to data descriptors.
- configurable: Whether the type of descriptor may be changed, or if the property can be removed.
- enumerable: Whether the property is listed in a loop through the properties of the object.
- value: The value of a property. This property only applies to Data descriptors because they reference concrete values, so the value describes the concrete data bound to the property.
Accessor Descriptors
Accessor descriptors, on the other hand, proxy access to the concrete value through getter and setter functions. These are useful when some type of transformation or constraints are required. When not set, they'll default to undefined.
- get (): A function called with no arguments when the property value is requested using dot notation (i,e: obj.prop).
- set (newValue): A function called with the new value for the property when the user tries to modify the value of the property using dot notation (i,e: obj.prop = 'new value').
Browser Support
Most of the major browsers support Object.create(), with the exception of Internet Explorer, which is adding it to version 10.
What's Next
The create() method is just one of several new Object methods. We'll be looking at how to use those next time.