RSS
 

Archive for the ‘ECMAScript 6’ Category

ECMAScript Harmony’s arrow functions

05 Jun

After being out of the loop for quite a while for personal reasons, I got around to getting myself back up to date. One thing I’ve been playing around with is Spidermonkey’s implementation of new ECMAScript features introduced in the 6th edition draft specification.

When I first read the proposals for arrow functions a couple of years ago, I was put off quite a lot by the syntax and I just thought of it as something impure that CoffeeScript developers were trying to force into the language. Maybe I’m a little more open-minded now because, after actually getting to use them, my mind has changed completely. I think the new syntax is beautifully concise, and the real issues that arrow functions solve make it more than worth the short amount of time you’ll need to get acquainted with them. Take the following example:

    // Numerical sort, the old way
    someArray.sort(function (a, b) { return a - b });

    // The new way
    someArray.sort((a, b) => a - b);

It’s not just about saving bytes, though. Arrow functions offer an extra conveniences: no prototype property, internal [[Construct]] method, and lexical this. This means that an error is thrown if they are used with the new operator, and the value of this is fixed to whatever it is in the parent scope:

    // This will cause an error
    new (() => {});

    // `this` inside the timeout function is myObj
    var myObj = {
        myFunction: function () {
            setTimeout(() => alert(this.myProperty), 500);
        },
        myProperty: 'Hello!'
    };

    myObj.myFunction();
    // alert: 'Hello'

That’s really great, and I’m impressed that we’ll be able to ditch Function.prototype.bind() for cases like these. But what if we wanted dynamic this? There have been some proposals, such as thin arrow syntax, but some feel that the proposals add would add confusion with casual coders or that dynamic this isn’t important. On Kevin Smith’s survey of 20667 functions from a large number of code bases, Dr Axel Rauschmayer wrote the following:

These findings mean that JavaScript most urgently needs easy ways to define subroutines (with lexical this) and methods. ECMAScript.next therefore makes the right choices. Another implication is that arrow functions with dynamic this are not that important, even for current JavaScript code.

The confusion argument holds a lot of weight, I suppose. Like the difference between single quotes and double quotes in Perl/PHP/sh/etc, having fat arrows and thin arrows in JavaScript could cause the same confusion. However, in my experience, such confusion is usually cleared up in a very short period of time. We should be trying to improve the language for people who use JavaScript every day, rather than people who just write a couple of functions every 6 months to 2 years.

That being said, if there’s no call for it, then maybe it shouldn’t exist. I did think of one (albeit small) use case recently where dynamic this in arrow functions would shine: generic instance methods. You’d always want these to be lightweight (no [[Construct]] or prototype) and dynamic this. Take shims of native array generics introduced in ES5, for instance. You’d never get these to pass their respective tests on the test262 suite because all the existing shims have a prototype property and don’t throw an error when you try and construct them. With dynamic this, we could make ES6 shims of ES7 functions virtually indistinguishable ― Function.prototype.toString aside ― from their native counterparts and use the test262 suite to verify them. Like I said, it’s a small use case, but I think it would be nice to have.

Either way, I’m looking forward to being able to use arrow functions in JavaScript. As soon as the finalised specification draws near, I think we can expect minifiers to adopt support for this syntax and churn out backwards-compatible minified code.

 

ES6 – a quick look at Weak Maps

31 Oct

One of my favourite proposals in ECMAScript Harmony is Weak Maps. Simply put, ‘weak maps are unordered tables mapping objects to values’ with references to those objects and values being weak. A weak reference means that the item won’t be protected from garbage collection if no other references are held. For example:

// Using the proposed API (currently implemented in Firefox)
var wm = WeakMap();

(function () {
    var obj = {};
    wm.set(obj, 1);
})();

console.log(wm); //-> "[object WeakMap]"

By now a very obvious use case might have occurred to you, but I’ll get to that in a minute. When the above function finishes execution, obj is no longer reachable, so it can be garbage collected even though our WeakMap object is still reachable. This addition to the language is a useful tool for an age-old concern in JavaScript and the DOM; memory leaks. That aforementioned use case is becoming more obvious now, eh?

Where do Weak Maps fit in with current code?

OK, if you never had to implement this kind of thing before, you might not have thought of jQuery’s data() method. jQuery.data() allows us to attach an arbitrary value to a DOM object. Whilst you can do this with expando properties, ordinarily you run the risk of circular references and memory leaks. jQuery tackles this by creating a unique ID for itself (only once) and each element it is attaching data to. It tacks the element’s unique ID, which is really just an incremented counter, onto an expando property whose name is the unique ID. So it looks something like this:

     element[jQuery.expando] = elementId;

Any data you attach to the element is actually stored in an array at index elementId. jQuery is able to avoid memory leaks because there are no circular references, and it can delete all stored data when the page is unloaded (or the element is removed using jQuery’s own methods). However, it does have some limitations. For example — according to the source code — object/embed/applet cannot accept expando properties and, therefore, jQuery.data() cannot work with them.

Weak maps come in here because they can do the job much better. They cut out the need for the expando property entirely, along with the requirement of handling JS objects differently to DOM objects. They also expand on jQuery’s ability to allow garbage collection when DOM elements are removed by its own methods, by automatically allowing garbage collection when DOM elements no longer reachable after they’ve been removed by any method. Had WeakMaps been implemented in ECMAScript 3 (if only), jQuery’s workload with data() could have been significantly reduced. Perhaps jQuery wouldn’t have bothered implementing data if we could create weak maps, though.

Another use case to consider is secretly extending objects without the danger of naming collisions. Using someGloballyAvailableObject.someProperty = "some arbitrary value"; as an example, what if someGloballyAvailableObject already has someProperty? We’ve just written a potentially script-breaking piece of code. However, with a weak map we can eliminate this danger entirely:

(function () { 
    var wm = new WeakMap();
    wm.set(someGloballyAvailableObject, {someProperty:"some arbitrary value"});
})();

There’s also a hidden bonus here. I used the word secretly before because no other piece of our script can access this mapped value if we never expose the wm variable.

You might recall (if you read it) my article on creating lookup lists. If you do, you might be thinking that weak maps are a good use for creating lists, but in reality this isn’t the case because weak maps — in the current proposal, at least — will only map a value to an object.

Weak map compatibility shims

A true weak map is technically impossible in ECMAScript 3. We can create a map of objects to values by simply using two arrays:

function WeakMap() {
    this.keys = [/* key01, key02, key03 */];
    //                ↓      ↓      ↓
    this.vals = [/* val01, val02, val03 */];
}

Unfortunately, there’s 2 very important caveats here. Firstly, these references are not (and cannot be) weak, so they will not be garbage collected as long as the mapping exists. Secondly, for our get(key) method to work, we need to search our keys array. This significantly impacts performance, particularly on large maps.

As for ES5, I was pointed in the direction of this implementation recently. It’s very clever; It eliminates the need for a keys array and uses a custom valueOf function to keep track of the mapped value. I profiled the method in Chrome and it does indeed appear to prevent leaks. However, it does still have its limitations like not working on frozen objects or objects whose valueOf function is not configurable.