Wednesday, January 6, 2016

Properties in JavaScript

Continuing my exploration of JavaScript, with Kyle Simpson's this and Object Prototypes as my guide, I'm going to look at some of the functionality introduced with ES5 to allow greater control over the behavior of object properties, which Simpson looks at in Chapter 3 of his book.


Object Properties


There are a number of ways of attaching a property "a" to an object "myObject":

var myObject = {};
myObject.a = 2;

or

myObject["a"] = 2;

or 

var myObject = {
  a: 2
}

The Object.defineProperty function


Starting with EcmaScript5 (which is widely supported since the release of IE9), you can also add a property like this:

var myObject = {};

Object.defineProperty(myObject, "a", {
  value: 2
});

So why would you want to do that? Well, this adds a bit more control to how the property behaves.  (Note: A good way to run through these examples is with interactive "node", so you don't have to throw alerts or add console.log statements to inspect values, since myObject.a will print the value to the screen.)

By default this property is read-only:

myObject.a; // 2

myObject.a = 3;  // Doesn't throw an error, but has no effect.

myObject.a; // 2

You can make the property writable by specifying this:

var myObject2 = {}

Object.defineProperty(myObject2, "a", {
  value: 2,
  writable: true
});

myObject.a; //2

myObject.a = 3;

myObject.a; //3

This throws a TypeException only if  "use strict"; (or --use-strict for the node REPL) has been called.  Otherwise the assignment fails silently.

Two other properties you can set are worth a look:

configurable: [true/false]

determines whether you can change these settings.  It is a one-way street; you cannot change a property from configurable: false to configurable: true, because that would be changing its configuration.  Interestingly, there is a loophole for "writable": you can make a field read-only after you've locked down configuration, but you cannot change it back.

enumerable: [true/false]

determines whether the property shows up in a statement like this:

for (var key in myObject) {...}

It also appears to determine whether the property shows up on a REPL.  If you type the name of an object with a non-enumerable property, it shows up as "{}", but an enumerable property will display: "{ a: 1 }".

There are two additional functions available from Object, which allow you to lock down configuration for all properties at a stroke.  Object.seal(someObject) sets configurable:false for all properties, and Object.freeze(someObject) does that and also makes the properties read-only, making the object immutable.

For example, let's lock down a normal JavaScript property.
>var car = {make: 'Honda'}
{ make: 'Honda' }
>car
{make: 'Honda'}
>car.make = 'Chevy'
'Chevy'
>car.make
'Chevy'
>Object.freeze(car)
{ make: 'Honda' }
>car.make = 'Dodge'
'Dodge'
>car.make
'Chevy'

As usual, the assignment fails silently unless in strict mode, in which case you get a type error when you try to assign to the (now) read only property


Getters and Setters

Finally, there are formal getters and setters, much like in C#:

var myObject = {
  get a() { return 1; }
}

On node, if you type myObject, you get this:
{ a: [Getter] }

And typing myObject.a gets you this:
1

So we now have a read-only property.  We can add a setter as well:

var myObject = {
  set a(value) { this._a_ = value; }
  get a() { return this._a_; }
}

>myObject
{ a: [Getter/Setter] }
>myObject.a = 1;
{ a: [Getter/Setter], _a_: 1 }
>myObject.a
1

So now we can do all kinds of settery things, like constrain values to an acceptable range, or log changes.

Conclusions

All this stuff may be widely known in the JavaScript community, but for me as a C# guy whose JS knowledge has been limited to peeking in JQuery documentation from time to time, reading about this functionality has been a revelation. I had no idea this was there. It's interesting how the "Object" prototype is the mechanism for introducing this functionality, and it is clear that this is functionality I will want to make use of.  It's very appealing to me how these methods allow you to combine the fluidity of JavaScript object manipulation with the rigors of strongly typed (C#) and functional (F#) languages.  There's a lot of possibilities here.






No comments:

Post a Comment