ECMAScript 5: a revolution in object properties
Over the coming weeks I'm going to focus on discussing the mini revolution that ECMAScript 5 brought, and the implications in particular for objects and their properties.
ECMA5's final draft was published at the end of 2009, but it was only really when IE9 launched in early 2011 - and, with it, impressive compatibility for ECMA5 - that it became a genuinely usable prospect. Now in 2012, it is being used more and more as browser vendors support it and its power becomes apparent. (Full ECMA5 compatibility table).
JavaScript has always been a bit of an untyped, unruly free-for-all. ECMAScript 5 remedies that somewhat by giving you much greater control over what, if anything, can happen to object properties once changed - and it's this I'll be looking at in this first post.
A new approach to object properties
In fact the whole idea of an object property has changed; it's no longer a case of it simply being a name-value pairing - you can now dictate all sorts of configuration and behaviour for the property. The new configurations available to each property are:
- value - the property's value (obviously)
- writable - whether the property can be overwritten
- configurable - whether the property can be deleted or have its configuration properties changed
- enumerable - whether it will show up in a for-in loop
- get - a function to fire when the property's value is read
- set - a function to fire when the property's value is set
Collectively, these new configuration properties are called a property's descriptor. What's vital to understand, though, is that some are incompatible with others.
Two flavours of objects
The extensive MDN article on ECMAScript 5 properties suggests thinking of object properties in two flavours:
- data descriptors - a property that has a value. In its descriptor you can set value and writable but NOT get or set
-
accessor descriptors - a property described not by a value but by a pair of getter-setter functions. In its descriptor you can set get and set but NOT value or writable.
Note that enumerable and configurable are usable on both types of property. I'm struggling to understand why someone thought the ability to set a value and a setter function, for example, were incompatible desires. If I find out, I'll let you know.
New methods
To harness this new power, you need to define properties in one of three ways - all stored as new static methods of the base Object object:
- defineProperty()
- defineProperties()
- create()
The first two work identically except the latter allows you to set multiple properties in one go. As for Object.create(), I'll be covering that separately in a forthcoming post.
Object.defineProperty() is arguably the most important part of this new ECMAScript spec; as John Resig points out in his post on the new features, practically every other new feature relies on this methd.
Object.defineProperty() accepts three arguments:
- the object you wish to add a property to
- the name of the property you wish to add
- a descriptor object to configure the property (see descriptor properties above)
Let's see it in action.
1var obj1 = {};
2Object.defineProperty(obj1, 'newProp', {value: 'new value', writable: false});
3obj1.newProp = 'changed value';
4console.log(obj1.newProp); //new value - no overwritten
See how the overwrite failed? No error or warning is thrown - it simply fails silently. In ECMA5's new 'strict mode', though, it does throw an exception. (Thanks to Michiel van Eerd for pointing this out.)
There we set a data descriptor. Let's set an accessor descriptor instead.
1var obj = {}, newPropVal;
2Object.defineProperty(obj, 'newProp', {
3 get: function() { return newPropVal; },
4 set: function(newVal) { newPropVal = newVal; }
5});
6obj.newProp = 'new val';
7console.log(obj.newProp); //new val
You might be wondering what on earth is going on with that newPropVal variable. I'll come to that in my next post which will look at getters and setters in detail. Note also how, with our setter, the new value is forwarded to it as its only argument, as you'd expect.
The fact that these properties can be set only via these methods means you cannot create them by hand or in JSON files. So you can't do:
1var obj = {prop: {value: 'some val', writable: false}}; //etc
2obj.prop = 'overwritten'; //works; it's not write-protected
ECMA 5 properties don't replace old-style ones
An important thing to understand early on is that this new form of 'uber' property is not the default. If you define properties in the old way, they will behave like before.
1var obj = {prop: 'val'};
2obj.prop = 'new val'; //overwritte - no problemo
Reporting back
Note that these new configuration properties are, once set, not available via the API; rather, they are remembered in the ECMAScript engine itself. So you can't do this (using the example above):
console.log(obj1.newProp.writable); //error; newProp is not an object
Instead, you'll be needing Object.getOwnPropertyDescriptor. This takes two arguments - the object in question and the property you want to know about. It returns the same descriptor object you set above, so something like:
{value: 'new value', writable: true, configurable: true, enumerable: true}
More to come..
So there you go - a very exciting mini revolution, as I said. This new breed of intelligent object property really is at the heart of arguably the most major shake-up to the language for a long time. Next week I'll continue this theme - stay posted!
Comments (10)
WebManWalking, at 2/03/'12 23:30, said:
Adam, at 5/03/'12 20:40, said:
Adam Eivy, at 9/03/'12 17:23, said:
Adam Eivy, at 9/03/'12 17:30, said:
Mitya, at 9/03/'12 17:42, said:
Alex, at 12/03/'12 16:22, said:
Michiel van Eerd, at 12/03/'12 18:11, said:
Mitya, at 12/03/'12 19:47, said:
a-sk, at 17/03/'12 11:39, said:
Mitya, at 17/03/'12 11:48, said: