permalink

65

A Few New Things Coming To JavaScript

I believe the day-to-day practice of writing JavaScript is going to change dramatically for the better when ECMAScript.next arrives. The coming year is going to be an exciting time for developers as features proposed or finalised for the next versions of the language start to become more widely available.

In this post, I will review some of the features I'm personally looking forward to landing and being used in 2013 and beyond.

ES.next implementation status

Be sure to look at Juriy Zaytsev's ECMAScript 6 compatibility table, Mozilla's ES6 status page as well as the bleeding edge versions of modern browsers (e.g Chrome Canary, Firefox Aurora) to find out what ES.next features are available to play with right now.

In Canary, remember that to enable all of the latest JavaScript experiments you should navigate to chrome:flags and use the 'Enable Experimental JavaScript' option.

Alternatively, many ES.next features can be experimented with using Google's Traceur transpiler (useful unit tests with examples here) and there are shims available for other features via projects such as ES6-Shim and Harmony Collections.

Finally, in Node.js (V8), the --harmony flag activates a number of experimental ES.next features including block scoping, WeakMaps and more.

Modules

We're used to separating our code into manageable blocks of functionality. In ES.next, A module is a unit of code contained within a module declaration. It can either be defined inline or within an externally loaded module file. A skeleton inline module for a Car could be written:

module Car {
  // import …
  // export …
}

A module instance is a module which has been evaluated, is linked to other modules or has lexically encapsulated data. An example of a module instance is:

module myCar at "car.js";

module declarations can be used in the following contexts:

module UniverseTest {};
module Universe { module MilkyWay {} };
module MilkyWay = 'Universe/MilkyWay';
module SolarSystem = Universe.MilkyWay.SolarSystem;
module MySystem = SolarSystem;

An export declaration declares that a local function or variable binding is visible externally to other modules. If familiar with the module pattern, think of this concept as being parallel to the idea of exposing functionality publicly.

module Car {

  // Internal 
  var licensePlateNo = '556-343';     

  // External
  export function drive(speed, direction) {
    console.log('details:', speed, direction);
  }

  export module engine{
    export function check() { }
  }

  export var miles = 5000;
  export var color = 'silver';

};

Modules import what they wish to use from other modules. Other modules may read the module exports (e.g drive(), miles etc. above) but they cannot modify them. Exports can be renamed as well so their names are different from local names.

Revisiting the export example above, we can now selectively choose what we wish to import when in another module.

We can just import drive():

import drive from Car;

We can import drive() and miles:

import {drive, miles} from Car;

Earlier, we mentioned the concept of a Module Loader API. The module loader allows us to dynamically load in scripts for consumption. Similar to import, we are able to consume anything defined as an export from such modules.

// Signature: load(moduleURL, callback, errorCallback)

Loader.load('car.js', function(car) {
  console.log(car.drive(500, 'north'));
}, function(err) {
  console.log('Error:' + err);
});

load() accepts three arguments:

  • moduleURL: The string representing a module URL (e.g "car.js")
  • callback: A callback function which receives the output result of attempting to load, compile and then execute the module
  • errorCallback: A callback triggered if an error occurs during loading or compilation

Whilst the above example seems fairly trivial to use, the Loader API is there to provide a way to load modules in controlled contexts and actually supports a number of different configuration options. Loader itself is a system provided instance of the API, but it's possible to create custom loaders using the Loader constructor.

What about classes?

I'm not going to be covering ES.next classes in this post in more, but for those wondering how they relate to modules, Alex Russell has previously shared a pretty readable example of how the two fit in – it's not at all about turning JavaScript into Java.

Classes in ES.next are there to provide a declarative surface for the semantics we're used to (e.g functions, prototypes) so that developer intent is expressed instead of the underlying imperative mechanics.

Here's some ES.next code for defining a widget:

module widgets {
  // ...

  class DropDownButton extends Widget {
    constructor(attributes) {
      super(attributes);
      this.buildUI();
    }

    buildUI() {
      this.domNode.onclick = function(){
        // ...
      };
    }
  }
}

Followed by today's de-sugared approach that ignores the semantic improvements brought by ES.next modules over the module pattern and instead emphasises our reliance of function variants:

var widgets = (function(global) {
  // ...

  function DropDownButton(attributes) {
    Widget.call(this, attributes);
    this.buildUI();
  }

  DropDownButton.prototype = Object.create(Widget.prototype, {
    constructor: { value: DropDownButton },
    buildUI:     {
      value: function(e) {
        this.domNode.onclick = function(e) {
          // ...
        }
      }
    }
  });
})(this);

All the ES.next version does it makes the code more easy to read. What class means here is function, or at least, one of the things we currently do with functions. If you enjoy JavaScript and like using functions and prototypes, such sugar is nothing to fear in the next version of JavaScript.

Where do these modules fit in with AMD?

If anything, the landscape for modularization and loading of code on the front-end has seen a wealth of hacks, abuse and experimentation, but we've been able to get by so far.

Are ES.next modules a step in the right direction? Perhaps. My own take on them is that reading their specs is one thing and actually using them is another. Playing with the newer module syntax in Harmonizr, Require HM and Traceur, you actually get used to the syntax and semantics very quickly – it feels like using a cleaner module pattern but with access to native loader API for any dynamic module loading required at runtime. That said, the syntax might feel a little too much like Python for some peoples tastes (e.g the import statements).

I'm part of the camp that believe if there's functionality developers are using broadly enough (e.g better modules), the platform (i.e the browser) should be trying to offer some of this natively and I'm not alone in feeling this way. James Burke, who was instrumental in bringing us AMD and RequireJS has previously said:

I want AMD and RequireJS to go away. They solve a real problem, but ideally the language and runtime should have similar capabilities built in. Native support should be able to cover the 80% case of RequireJS usage, to the point that no userland "module loader" library should be needed for those use cases, at least in the browser.

James has however questioned whether ES.next modules are a sufficient solution. He covered some more of his thoughts on ES.next modules back in June in ES6 Modules: Suggestions for improvement and later in Why not AMD? for anyone interested in reading more about how these modules fit in with RequireJS and AMD.

Isaac Schlueter has also previously written up thoughts on where ES6 modules fall short that are worth noting. Try them out yourself using some of the options below and see what you think.

Use it today

Object.observe()

The idea behind Object.observe is that we gain the ability to observe and notify applications of changes made to specific JavaScript objects. Such changes include properties being added, updated, removed or reconfigured.

Property observing is behaviour we commonly find in JavaScript MVC frameworks at at the moment and is an important component of data-binding, found in solutions like AngularJS and Ember.

This is a fundamentally important addition to JS as it could both offer performance improvements over a framework's custom implementations and allow easier observation of plain native objects.

// A model can be a simple object
var todoModel = {
    label: 'Default',
    completed: false
};

// Which we then observe
Object.observe(todoModel, function(changes) {
    changes.forEach(function(change, i) {
        console.log(change);
        /*
            What property changed? change.name
            How did it change? change.type
            Whats the current value? change.object[change.name]
        */
    });
});


// Examples

todoModel.label = 'Buy some more milk';

/*
    label changed
    It was changed by being updated
    Its current value is 'Buy some more milk'
*/

todoModel.completeBy = '01/01/2013';

/*
    completeBy changed
    It was changed by being new
    Its current value is '01/01/2013'
*/


delete todoModel.completed;

/*
    completed changed
    It was changed by being deleted
    Its current value is undefined
*/

Availability: Object.observe will be available in Chrome Canary behind the "Enable Experimental JS APIs" flag. If you don't feel like getting that setup, you can also checkout this video by Rafael Weinstein discussing the proposal.

Use it today

Read more (Rick Waldron).

Default Parameter Values

Default parameter values allow us to initialize parameters if they are not explicitly supplied. This means that we no longer have to write options = options || {};.

The syntax is modified by allowing an (optional) initialiser after the parameter names:

function addTodo(caption = 'Do something') {
    console.log(caption);
}

addTodo(); // Do something

Only trailing parameters may have default values:

function addTodo(caption, order = 4) {}

function addTodo(caption = 'Do something', order = 4) {}

function addTodo(caption, order = 10, other = this) {}

Traceur demo

Availability: FF18

Block Scoping

Block scoping introduces new declaration forms for defining variables scoped to a single block. This includes:

  • let: which syntactically is quite similar to var, but defines a variable in the current block function, allowing function declarations in nested blocks
  • const: like let, but is for read-only constant declarations

Using let in place of var makes it easier to define block-local variables without worrying about them clashing with variables elsewhere in the same function body. The scope of a variable that's been declared inside a let statement using var is the same as if it had been declared outside the let statement. These types of variables will still have function scoping.

var x = 8;
var y = 0;

let (x = x+10, y = 12) {
  console.log(x+y); // 30
}

console.log(x + y); // 8

let availability: FF18, Chrome 24+

const availability: FF18, Chrome 24+, SF6, WebKit, Opera 12

Maps and sets

Maps

Many of you will already be familiar with the concept of maps as we've been using plain JavaScript objects as them for quite some time. Maps allow us to map a value to a unique key such that we can retrieve the value using the key without the pains of prototype-based inheritance.

With the Maps set() method, new name-value pairs are stored in the map and using get(), the values can be retrieved. Maps also have the following three methods:

  • has(key) : a boolean check to test if a key exists
  • delete(key) : deletes the key specified from the map
  • size() : returns the number of stored name-value pairs
let m = new Map();
m.set('todo', 'todo'.length);  // "something" → 4
m.get('todo');                 // 4
m.has('todo');                 // true
m.delete('todo');              // true
m.has('todo');                 // false

Availability: FF18

Read more (Nicholas Zakas)

Use it today

Sets

As Nicholas has pointed out before, sets won't be new to developers coming from Ruby or Python, but it's a feature thats been missing from JavaScript. Data of any type can be stored in a set, although values can be set only once. They are an effective means of creating ordered list of values that cannot contain duplicates.

  • add(value) – adds the value to the set.
  • delete(value) – sets the value for the key in the set.
  • has(value) – returns a boolean asserting whether the value has been added to the set
let s = new Set([1, 2, 3]);  // s has 1, 2 and 3.
s.has(-Infinity);            // false
s.add(-Infinity);            // s has 1, 2, 3, and -Infinity.
s.has(-Infinity);            // true
s.delete(-Infinity);         // true
s.has(-Infinity);            // false

One possible use for sets is reducing the complexity of filtering operations. e.g:

function unique(array) {
    var seen = new Set;
    return array.filter(function (item) {
        if (!seen.has(item)) {
            seen.add(item);
            return true;
        }
    });
}

This results in O(n) for filtering uniques in an array. Almost all methods of array unique with objects are O(n^2) (credit goes to Brandon Benvie for this suggestion).

Availability: Firefox 18, Chrome 24+

Read more (Nicholas Zakas)

Use it today

Proxies

The Proxy API will allow us to create objects whose properties may be computed at run-time dynamically. It will also support hooking into other objects for tasks such as logging or auditing.

var obj = {foo: "bar"};

var proxyObj = Proxy.create({
  get: function(obj, propertyName) {
    return 'Hey, '+ propertyName;
  }
});

console.log(proxyObj.Alex); // "Hey, Alex"

Also checkout Zakas' Stack implementation using ES6 proxies experiment.

Availability: FF18, Chrome 24

Read more (Nicholas Zakas)

WeakMaps

WeakMaps help developers avoid memory leaks by holding references to their properties weakly, meaning that if a WeakMap is the only object with a reference to another object, the garbage collector may collect the referenced object. This behavior differs from all variable references in ES5.

A key property of Weak Maps is the inability to enumerate their keys.

let m = new WeakMap();
m.set('todo', 'todo'.length);  // Exception!
// TypeError: Invalid value used as weak map key

m.has('todo');                 // Exception!
// TypeError: Invalid value used as weak map key

let wmk = {};
m.set(wmk, 'thinger');  // wmk → 'thinger'
m.get(wmk);             // 'thinger'
m.has(wmk);             // true
m.delete(wmk);          // true
m.has(wmk);             // false

So again, the main difference between WeakMaps and Maps is that WeakMaps are not enumerable.

Use it today

Read more (Nicholas Zakas)

API improvements

Object.is

Introduces a function for comparison called Object.is. The main difference between === and Object.is are the way NaNs and (negative) zeroes are treated. A NaN is equal to another NaN and negative zeroes are not equal from other positive zeroes.

Object.is(0, -0); // false
Object.is(NaN, NaN); // true

0 === -0; // true
NaN === NaN; // false

Availability: Chrome 24+

Use it today

Array.from

Array.from: Converts a single argument that is an array-like object or list (eg. arguments, NodeList, DOMTokenList (used by classList), NamedNodeMap (used by attributes property)) into a new Array() and returns it;

Converting any Array-Like objects:

Array.from({
    0: 'Buy some milk',
    1: 'Go running',
    2: 'Pick up birthday gifts',
    length: 3
});

The following examples illustrate common DOM use cases:

var divs = document.querySelectorAll('div');

Array.from(divs);
// [<div class=​"some classes" data-info=​"12">​</div>​, <div data-info=​"10">​</div>​]


Array.from(divs).forEach(function(node) {
    console.log(node);
});

Use it today

Conclusions

ES.next is shaping up to potentially include solutions for what many of us consider are missing from JavaScript at the moment. Whilst ES6 is targeting a 2013 spec release, browsers are already implementing individual features and it's only a matter of time before their availability is widespread.

In the meantime, we can use (some) modern browsers, transpilers, shims and in some cases custom builds to experiment with features before ES6 is fully here.

For more examples and up to date information, feel free to checkout the TC39 Codex Wiki (which was a great reference when putting together this post) maintained by Dave Herman and others. It contains summaries of all features currently being targeted for the next version of JavaScript.

Exciting times are most definitely ahead.

65 Comments

  1. Pingback: A Few New Things Coming To JavaScript | My Daily Feeds

    • I know there’s a lot coming, but it’s thankfully not too scary :) Many of these changes will take time to be both finalized and implemented across all modern browsers so we’ll have a decent period in between to get used to the new syntax and semantics.

  2. Pingback: In the News: 2012-11-22 | Klaus' Korner

  3. Many of these shiny new features are available today via CoffeeScript + Static Compilation + Closure Library. For example: github.com/Steida/este
    Of course native support is much better, but we don’t have to wait for awesomeness.

  4. Very nice review of ES.next features, Addy.
    I look forward to use most of these features, however I see an increasing layer of complexity being added to JS. Making for a steep learning curve for beginners.
    Criticisms aside, I’m really excited for obj.observe and modules =)

  5. Can you explain what the point of Map and Set are as in what was wrong with plain-old js object?

    One of things I dislike about python is the difference between on object and a dict. One of the things I love about JavaScript is there is only one kind and the syntax is always correct.

    In python, sometimes it’s v.key, sometimes it’s v["key"]. In JS both are the same. In python sometimes it’s if key in dict. Otherwise it’s if hasattr(key). In JS it’s always simple.

    Serializing is simple. Inspecting and debugging is simple.

    python pseudo code

    def dump(o):
    if o is obj
    iterater through attributes
    if o is dict
    iterator through keys

    js pseudo code

    function dump(o) {
    iterate through keys

    Adding map and set will make life more complicated :-(

    • I think you are right here.
      I’m trying to learn Python and I ran today into a problem where I needed to create some custom dictionaries ( dictionary[key1][key2] ). With JavaScript, it would have been very easy but with Python… let’s just I’m grateful StackOverflow exists.

  6. I’ve written an ES6 virtual machine that runs in ES3 (IE8+). It has nearly everything that’s going to be in ES6 including modules and the loader api, proxies, generators, destructuring, @symbols (the really hard/impossible stuff to shim). It also has a built in debug API and built in debugger.

    http://benvie.github.com/continuum.

    • I’m certain someone like Rick Waldron or Alex Russell would have a better insight into this than I, but as far as I’m aware the spec won’t be ‘complete’ until mid-2013.

  7. Just a heads up, the proxy proposal is a bit different now than it was when I wrote my article and examples. These are now called direct proxies to distinguish them from the earlier proxy proposal. The main difference is that direct proxies are designed specifically to wrap an object whereas the original proxies were just generic objects that had no relationship to any other objects. As far as I know, no browsers have implemented direct proxies yet (Firefox and Chrome still have the old proxy implementation).

    • Thank you Nicholas. I greatly appreciate the head’s up regarding direct proxies – I wasn’t aware they changed directions. I’ll have to check on the status of us updating the old proxy implementation in Chrome to the newer proposal. Great work on the very thorough write-ups on ES.next that you’ve been publishing of late.

  8. Oh oh. The classical programmers have invaded. I wonder what Crockford thinks of all this?

    Javascript is not a classical language, and any attempt of making it so is obfuscation. GWT. So I cringed a bit when you called the classes example a “semantic improvement”. But I suppose that from the perspective of a classical programmer, that would be true. “This is what I mean in the language I understand. Now javascript, convert this to something you understand.”

    I empathize with Dumbunny on collections in javascript. Probably not a coincidence that my favorite languages (php, C, javascript) are the simplest and don’t have many datatypes.

    • Just want to point out that this actually is a semantic enhancement.
      I don’t know how much you use .NET and/or LINQ, but there were the same arguments when LINQ was introduced.
      The idea is that instead of writing HOW something should be done, I would rather write WHAT I want to do. The language is smart enough to figure out how this should be done, for me.
      The way I see it, the issue is the name, not the feature. Because, like you said, these are not actually classes in the classical sense, but rather just syntactic sugar for objects and prototypes. If only we could think of a better name that describes what we are trying to accomplish.

  9. Nice review!
    There is small inaccuracy regarding observables. As far as i know AngularJS dosen’t have observables, it is doing “dirty checking” of properties, not getters and setters. KnockoutJS on the other hand has really great observables, better use it as example.

  10. Still not mentioning TypeScript as a stepping-stone.. you guys sure know how to ignore something not part of your world. ;-)
    Anyway, nice read, as always..

  11. ES.next is the salvation for Javascript. It will take Javascript from “too simple” to “just right simple” . Remember: Everything should be as simple as possible but no simpler. JS extreme simplicity in fact complicated things unnecessarily. With ES.next JS reaches the perfect middle term between too simple and bloated. I believe in harmony. People complaining are obssessed with minimalism. Except that minimalism is as much evil as ‘bloatism’ is. Yeah JS needed native module support (i dislike every current module system), yeah JS needed lightweight classes and now we got them.

    • Maybe … but I’m concerned that once the ball gets rolling, it won’t stop until Javascript becomes bloated – I can’t see Occam’s razor applied to what’s being added to the language.

      Weak maps are just a workaround for memory leaks – but memory leaks should be fixed, not worked around. Sets can be easily implemented with what the language already provides, with O[ log n] complexity for insertion and search. Module loading will always be platform-dependant, why pack it into the language spec? Object.observe is half-baked – it doesn’t allow proper property validation or transformation, and it introduces problems related to the order of execution.

      What I like is classes and let. Default parameter values are syntactic sugar, but IMO unlikely to have bad side effects – as opposed to Object.observe and proxies (IMO hiding function calls behind assignments is a bad idea in any language not supporting operator overloading – it makes the caller unaware of the potential cost of his assignment). I’d rather keep Array.from as a shim than having it part of the language – it adds to the interpreter complexity without adding significant benefits when compared to a shim, IMO.

      • What JavaScript really needs is a strong focus on “mechanism, not policy”. Alas, that’s highly unlikely, given that it’s now a design-by-comittee language and has a particularly awful (read: highly political) committee at that.

  12. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1239

  13. Pingback: Nachtwächter-Blah » Das hat ja gedauert. ECMAScript.next bekommt end…

  14. Sets are implicitly unique ;)

    Ex:

    new Set([ 1, 1, 2, 2, 3, 4 ]); // [ 1, 2, 3, 4 ]

    So you could make a unique array like this:

    var unique = Array.from(new Set([ 1, 1, 2, 2, 3, 4 ]));

    … Currently spread is spec’ed to disallow a similar behaviour, but I suspect that after next week’s TC39 meeting, the following will be allowed:

    var unique = [ ... new Set([ 1, 1, 2, 2, 3, 4 ]) ];

    • Even if not using the constructor this way, the set.has(value) test is unnecessary. Adding an element will perform the same test. Which is a great thing about sets. You just keep adding stuff and don’t worry about duplicates.

  15. Pingback: Design Focus: Great Deals | Devlounge

  16. Hi Addy,

    I can’t get the module feature to work on Chrome Canary 25.0.1333.0 after I enabled the Experimental JavaScript functionality. Isn’t there any other step I must take or is the software version correct? I sooo wanna use it right now.

  17. I love the new features, and I love the performance improvements modern browsers have recently delivered.

    My question is how much of a performance hit are we going to have to absorb using the new features?

    Will be be back to poor performing javascript?

  18. Pingback: Weekly Design News – Resources, Tutorials and Freebies (N.160) | Wordpress Webdesigner

  19. :) Thanks for tracking all of this Addy. There really is a lot to keep up with. How is Google going to get the word out to industry that we’ll need to emphasize client-side software engineering?

    Unrelated thoughts: Have you ever considered a wider content area on your blog? While your at it, perhaps a smaller line height for code blocks?

    • Re unrelated: I have indeed. The challenge with the last redesign was balancing content width with discovery of some of the larger resources available on the site, but I might try experimenting with a wider layout in the next refresh. I completely agree about a smaller line height for code blocks, though.

    • It’s possible to define parameter defaults of any type passed to a function, but I’m not sure how you would hope to apply it in your question. Do you mean something like.. function getText({ a: ‘default’, b: other, c: other})?. A code example of what you’re after would help :)

  20. Pingback: IT Digest: Google Webmaster Tools Breach, Apple is the World’s Most Valuable Company, Apollo Plus and Much More | Zfort Group Blog

  21. So ES6 classes will be sugar, rather than a new feature? As much as prototypical syntax and semantics can be confusing to developers who spend more time in class-based languages, I do not want to see the fundamental paradigm of Javascript become fractured. Even introducing the “class” keyword may confuse new developers, but I’m guessing this change was made for a good reason.

    Modules, on the other hand, thank God for them. Great addition to the spec.

  22. Pingback: A Few New Things Coming To JavaScript | Mundo Digitais

  23. Pingback: Talks To Help You Become A Better Front-End Engineer In 2013 | Creative Online Journal by Emrah Akman

  24. Pingback: Tweet Parade (no.51 Dec 2012) | gonzoblog

  25. Pingback: Talks To Help You Become A Better Front-End Engineer In 2013

  26. Pingback: A year for the designer - @rjzaworski

  27. It’s good to see JS getting some significant, useful updates based on how JS is already being used in the real world. I’d like to see Javascript expanded out so that we have to rely less on ‘frameworks’ and plugins and can do more with JS natively. Having things in-built means it is easier to standardise and it’s easier to apply best-practice, and also less HTTP requests per page due to including fewer JS libraries.

  28. Pingback: SPA example application with AngularJS and requireJS as a comparison to the ROCA Spring variant | codecentric Blog

  29. Pingback: A Few New Things Coming To JavaScript | Nuixen Technology

  30. Pingback: 未来两年前端开发的趋势是什么? - HTML5 - 开发者问答

Leave a Reply

Required fields are marked *.