Sweeter Javascript: Defining Properties to Add Syntactic Sugar

Syntatic sugar makes for more human-readable code and, if done
correctly, provides for more flexibility. In the world of Node
many turn to Coffeescript to add that “sweetness”,
but you can also achieve it with plain old Javascript.TL;DR »

Object.defineProperty

It all comes down to using defineProperty of theObject class. Introduced as part of ECMAScript 5 and
implemented in Javascript 1.8.5 – which is supported by Node and most
major browsers – it allows you to add or modify
a property on an object by not just determining its value but its
entire behaviour.

Object.defineProperty(obj, prop, descriptor)

The magic comes in the descriptor parameter. You can
directly set an initial value that will always be returned
by anyone using the property. You can use other attributes in the
descriptor to make the propertywritable (allowing new values assigned),configurable (permit further changes), andenumerable (returned during property enumeration).

But you can go beyond the simple retrieval and assignment of value to
properties by passing functions as the get and/or set
attributes in the descriptor.

Let’s see how we can use it to add our sugar.

An Example: Read-only Attributes

Recently I published nock-vcr, a node module to deliver
the same functionaly of the Ruby gem VCR but built atop the HTTP
mocking framework nock. VCR use the metaphor of “cassettes”
to record the HTTP interactions, with the interactions being written
out to a file named after the name given to the cassette. If a
cassette doesn’t exist at the time of recording, it will be created.

For various reasons, we don’t want to give users of the module
the ability to change the name of a Cassette
once an instance is created. This is accomplished by usingObject.defineProperty to explicity set thevalue:

Now when we instantiate an instance of Cassette, the string
passed in the construtor is returned by the name property.

More importantly, because we never set the writable
attribute in the descriptor to true, no one can assign
a new value to the property:

Now Add the Syntactic Sugar

At this point, we haven’t done anything to “sweeten” the syntax. An
opportunity arises in implementing exists. If implemented
as a function, it would be used as follows:

if(cassette.exists())

But wouldn’t it be sweeter to do the following?

if(cassette.exists)

In this case, we pass in a get function when defining
‘exists’ as a property. This function will be run every time the property is
referenced, ensuring it represents the current state of the cassette, but without
need of keeping tract with another attribute.

Now we can call exists without need of the parenthesis:

A small, “sweet” victory.

Another Example: Sweeter Tests

The previous example did not really gain us much; what are a couple of
parenthesis in the larger scheme of things? A more useful example of this
technique and the benefits of syntactic sugar comes in writing out achai-like test framework called rooibos.

Let’s begin by creating an Assertion “class”, that accepts a
value we will be running our assertions against. We will also create and
export expect function as the only interface into our framework and
creating these assertions.

We can add two basic assertions; truthy which will
assert if the value is non-falsy and empty, which asserts
the value is has no characters if a string, no elements if an array, and
is non-null or undefined in all other cases.

At this point, we can use the framework as follows:

We can sweeten this further with to and be properties that pass through the Assertion instance, doing nothing but enhancing readability.