Top 5 ES.next Features

   Uncategorized
Top 5 ES.next Features

JavaScript and ECMAScript are not the same thing. JavaScript was developed by Netscape for its browser and ECMAScript is a language based upon JavaScript. In order to assure that JavaScript written for one company’s browser would work in all companies’ browsers, the international ECMA organization was chosen to develop and maintain standards for the language. Specifically, the T39 Task Group is in charge of the standard for the ECMA language, as well as test suites, libraries, and other related items.

ECMA released the third edition of the ECMA standard (ECMA-262), also known as ECMA3, in 1999. It released the fifth edition of ECMA-262 in 2009. We are all familiar with ECMA5, as it is what we’ve been using to make our modern web apps.

TC39 is planning to release an exciting update to the language, tentatively in December of 2014. ECMA6 (codename ES.next) has quite a few new features and language constructs that we’re going to be using, some of them are already implemented in the various browsers. Firefox seems to be leading the way with the most features implemented to date.

Let’s have a look at my top 5 features that we can look forward to using next year. The ES6 spec is evolving even as I write this, so not everything I describe below may end up in the real world.

1. Classes

ECMAScript has been missing OOP style classes, though it’s had prototype based inheritance all along. ES6 implements proper syntax for OOP style classes.

class Point2D {
  constructor(x,y) {
    this.x = x;
    this.y = y;
  }
  reset() {
    this.x = 0;
    this.y = 0;
  }
  static origin() {
    return new Point2D(0,0);
  }
}

class Point3D extends Point2D {
  constructor(x,y,z) {
    super(x,y);
    this.z = z;
  }
  // overrides reset() from Point2D
  reset() {
    super.reset();
    this.z = 0;
  }
  static origin() {
    return new Point3D(0,0,0);
  }
}

Frameworks like ExtJS have implemented their own OOP style class systems in JavaScript on top of prototypes. ExtJS, in particular, relies upon a deprecated feature of JavaScript, Function.caller, to implement the super functionality shown above. For this reason, you cannot use strict mode with ExtJS.

It is important to understand that this class syntax is really syntactic sugar on top of the existing prototype based inheritance already in ES5. Existing code, like ExtJS will work just fine without modification.

What appears to be missing is the concept of private and public members. I can see that the public and private keywords are reserved in the latest ES6 spec, and there are proposals to implement them for classes. To the best I can tell, it’s looking like these will be left for ES7.

One feature of the new Class mechanism is that you’ll be able to implement classes that extend HTMLElements. Consider:

class MyButton extends HTMLButtonElement {
  constructor() { 
    this.value = 'My Button';
    this.onclick = function() { alert('button clicked!'); };
  }
}
document.body.appendChild(new MyButton());

2. Proxies

JavaScript Objects have had getters and setters for quite a while now, but these work only for named members. In this example, the named member is x:

var point = new Point();
{
    get x() { return point.x; },
    set x(value) { point.x = value; }
}

There are cases where it would be useful to be able to define getters and setters for any arbitrary member name without knowing the name beforehand. If you’re familiar with MongoDB’s shell environment, you can see this in action. If you try these commands:

test> db.collection1.save({ a: 1, b: 2})
test> db.collection2.save({ a: 1, b: 2})

The members collection1 and collection2 can be any arbitrary name, as far as the Mongo shell is concerned. In traditional JavaScript, both would be undefined though, and the .save() would cause a runtime error.

Thanks to proxies, you can implement an API for this sort of thing that works.

The code looks something like this:

var db = new Proxy({}, {
    get: function(receiver, name) {
        return MongoDB.getOrCreateCollection(name);
    }
});

When db.collection1.save() is executed, the Proxy get() method is called with receiver = ‘db’ and name = ‘collection1’. The get() method returns a valid Collection instance that has a save() method, so it works. Similarly when db.collection2.save() is executed, get() is called with name = ‘collection2’ and the Collection returned is as expected.

The uses for proxies are endless. Consider validation:

var theRealObject = {};
var proxyObject = new Proxy(theRealObject, {
    set: function(receiver, name, value) {
      if (name === 'firstName' && !isString(value) || !value.length) {
        throw new Error('firstName must be a non-empty string');
      }
      theRealObject[prop] = value;
    }
};

proxyObject.firstName = 'George'; // OK
proxyObject.firstName = ''; // throws error
proxyObject.firstName = 10; // throws error

3. Destructuring Assignments

Destructuring assignments are a great shorthand way to select some items from an array or object into another array or object. They are particularly handy with commonJS (NodeJS) style require(). For example:

// old way:
var open = require('fs').open,
    close = require('fs').close;

// using destructuring, same results as above:
var { open, close } = require('fs');

The exports object returned by require(‘fs’) certainly contains more members than open and close, but we only care about the two: open and close.

An example using arrays:

var [ firstName, lastName, suffix ] = 'John Smith Jr.'.split(' ');
console.log(lastName); // => Smith

4. Template Strings

You’re probably familiar with some sort of tempting system like Mustache and Handlebars, or string interpolation features of languages like PHP. ES6 is going to introduce a form of template strings.

In PHP, you might do something like this:

$foo = "hello";
$bar = "$foo world"; // => "hello world"

In ES6, you will be able to do something similar (note backtick use):

var foo = 'hello';
var bar = `${foo} world`; // => "hello world"

ES6 also allows multiline strings.

// old way
var multilineString = [
    'line 1',
    'line 2'
].join('\n');

// or

var multilineString = 'line 1\n' +
    'line 2';

// ES6 using backtick
var multilineString = `line1
line2`;

You can also use expressions within template strings:

var a = 1, b = 2;
console.log(`a = ${a}, b = ${b}, a+b = ${a + b}`); 
          // => "a = 1, b = 2, a+b = 3"

5. Arrow Functions

ES6 introduces a shorthand for anonymous functions called arrow functions. Arrow functions provide both a clarity (shorter functions) when expressed as well as they lexically share the “this” value of the code where the arrow function is defined.

The syntax of an arrow function is:

param => {
… // statements that can use param
}

or

(param, param2) =>  {
… // statements that can use param and param2
}

An alternate form requires no braces if the function returns an expression:

param => param + 1; // returns param+1

For arrow functions that take no arguments, you must use ():

() => 1; // arrow function that returns 1

A common use will likely be with Array.sort.

var beatles = [ 
  { firstName: 'John', lastName: 'Lennon' },
  { firstName: 'Paul', lastName: 'McCartney' },
  { firstName: 'George', lastName: 'Harrison' },
  { firstName: 'Ringo', lastName: 'Starr' },
  { firstName: 'Ozzy', lastName: 'Osbourne' } // the 5th Beatle 😉
 ];

beatles.sort(function(a,b) { 
  return a.lastName.localeCompare(b.lastName);
});

// becomes:

beatles.sort((a,b) => a.lastName.localeCompare(b.lastName));

A common thing we do in JavaScript code is to capture the value of this so it can be used in a function defined within a function. For example:

function Counter() {
  var me = this;
  me.count = 0;
  setInterval(function() {
    me.count++;
  }, 10*1000);  // bump Counter instance's count every 10 seconds
}
var counter = new Counter();

We had to capture “this” in the “me” variable because “this” would be the global object (window) inside the anonymous function called by setInterval().

Consider how arrow functions solve this:

function Counter() {
  this.count = 0;
  setInterval(() => this.count++; , 10*1000);
}
var counter = new Counter();

Note that the “this” in the this.count++ line is the same as the Counter instance’s. No need to capture its “this”.

Conclusion

Since the ES6 specification is still in flux, not all of the browsers support these features yet. It’s a certainty they will support the final specification not too long after it is finalized. In the mean time, there is a way to try them out!

Traceur is a compiler that compiles ES6 to ES5 so you can run your programs in current browsers and server side environments. Using it will add a compilation step to your JavaScript, either in the browser or on the server-side. If you implement Traceur server-side, you can also provide Source Maps, so you can debug your ES6 code with Chrome Dev Tools.


Like What You See?

Got any questions?