DotNetCurry Logo

ECMAScript 2015 a.ka. ES 6 – New language improvements in JavaScript

Posted by: Ravi Kiran , on 2/20/2015, in Category HTML5 & JavaScript
Views: 26273
Abstract: ECMAScript 2015, previously known as ECMAScript 6 (ES6) comes with a number of features that makes JavaScript a better language to work with in large scale applications and adds a lot of useful syntactic sugar at the same time. This article covers features of ES6 that makes JavaScript look and feel better.

If you are a Web developer, probably the silliest question someone can ask you today would be “Do you know JavaScript?” JavaScript is an integral part of web development, although the usage of the JavaScript language has changed over the years. The same language that was used a couple of years ago for simple DOM manipulations on the browser, is now used to create extremely rich UI experiences for almost any device, and also to build lightweight web server apps. Today, the day jobs of a sizeable number of developers on this planet is writing only JavaScript.

Though usage of the language has widened, it was not designed with such wide usage in mind. Despite the wide adoption, the language lacks some of the much needed features that other programming languages possess. The JavaScript Community has been very active in developing a number of wrappers and libraries to make the experience better for every developer out there. Thanks to the openness of the web standards; the Web standards committee has been accepting ideas from popular libraries and frameworks and making it a part of the web.

 

ECMAScript (a.k.a ES) is the specification of JavaScript. Anything that has to become a part of the language has to be specified in ES. The current version of JavaScript used in all popular browsers is based on the ES5 specification. Currently, the ES team is working on standardizing specifications for the next version, i.e. ECMAScript 6 (ES6). This version comes with a number of additions to the syntax, new APIs and improvements to some existing APIs as well. A complete list of features can be found on the Official site for ES6. An HTML version of the specification can also be found on Mozilla’s site.

Update: ES6 was later renamed to ECMAScript 2015. Please read ECMAScript 2015 whereever you find references to ES6.

After taking a look at the current version of the specification, I personally feel that the resulting language would make the job of JavaScript developers much easier. Those new to the language or coming from different programming backgrounds will also find useful features and techniques they are already familiar with. Most of the new features are adopted from JavaScript wrappers like TypeScript, CoffeeScript and also from some of the popular libraries that are helping web developers today, to get their job done.

Though it will take some more time to get the final specification of ES6, browsers have started implementing these features and we also have a number of transpilers and polyfills to work with ES6 today. Transpilers and polyfills are tools to try out tomorrow’s features, today.

How to write and test ECMAScript 6 (ES6) today?

Though ES6 is still in works, we have a good number of options around to try, see and use ES6. Most of the leading browsers have already started implementing features of ES6.

In current versions of Opera and Chrome, you need to set Experimental JavaScript flag in order to try ES6 features. Firefox has an unstable version called Firefox Nightly that implements more features than the stable release of Firefox. If you are planning to write production JavaScript code using ES6 today, then targeting specific browsers is not a good option as your site will not reach to a large number of users.

To write JavaScript code using ES6, you can use some of the transpilers and pollyfills we mentioned earlier. Transpilers take ES6 code and convert it to its ES5 equivalent that today’s browsers can understand. Traceur from Google happens to be the most popular transpiler of ES6. Other transpilers include 6to5, ES6-transpiler and a couple of others. The transpilers define certain commands using which we can convert ES6 code to ES5. We also have Grunt and Gulp packages to automate this process. Details about all these transpilers and their usage options are listed on Addy Osmani’s ES6 tools page.

To check for a list of features supported by a particular browser or a transpiler, visit the ES6 compatibility table: http://kangax.github.io/compat-table/es6/

 

Setting up ES6 Environment

To run the code of this article, I have used traceur. To use traceur, you need to have Node.js and the following NPM package installed.

npm install traceur -g

With this setup, you can compile any ES6 file using the following traceur command:

traceur --out .js --script .js 

This command produces ES5 equivalent of the ES6 code. This can be referred on a page and loaded into a browser to test the behavior. Each ES6 source file in the sample code has the command to compile the file.

The ES5 files produced after traceur compilation contains traceur objects. We need the traceur library to be loaded before loading the compiled file. This library has to be installed using bower.

bower install traceur --save-dev

Load the traceur library file and set the experimental flag to true before loading compiled scripts.




The step involving compilation of the source files can be automated using Grunt or, Gulp tasks. Addy Osmani’s ES6 tools page lists these tasks. I am leaving it as an assignment for the reader to setup Grunt or Gulp.

Variables in ES6

The “let” keyword

The “var” variable specifier of JavaScript creates variables of two scopes: Global and Function. The var specifier cannot create block scoped variables. A variable declared at a block level is also hoisted at the function level. For example, consider the following snippet:

(function(){
    var numbers = [10, 20, 25, 90, 17, 38, 61];
    var average=0;

    for(var count=0; count < numbers.length; count++){
        average += numbers[count];
    }

    average = average/numbers.length;
    console.log(average);
    console.log(count);
}());

The variable count is hoisted at the function level and so the last console.log() statement is valid. This behavior frustrates programmers that have worked on any other programming languages like C#, VB.NET or Java. There are good reasons for a variable to not exist beyond a block. This is particularly useful in case of for loops, where we don’t need the counter to exist beyond the loop.

ES6 introduced a new keyword let, that makes it possible to create block level variables. Replace var in the for loop with let in the function we just saw:

(function(){
    var numbers = [10, 20, 25, 90, 17, 38, 61];
    var average=0;

    for(let count=0; count < numbers.length; count++){
        average += numbers[count];
    }

    average = average/numbers.length;
    console.log(average);
    console.log(count);
}());

Now, the last statement would throw an error in strict mode, as the variable is not declared at the function level.

The “const” keyword

In the current version of JavaScript, there is no direct way to define a constant. In real applications, constants have significant importance. The only possible way I see is using Object.freeze, but this method is created for a different purpose. ES6 solves this problem with the const keyword.

The const keyword applies two restrictions on the variable:

  • Prevents modification of value of the variable
  • Sets block to the variable

In other words, one can also say that const is similar to let but with value modification prevented.

Following snippet demonstrates usage of const:

function getAreaOfCircle(radius) {
    if(radius) {
        const PI = 3.412;
        const R;  //Error, constant must be initialized
        PI=22/7;  //Error, PI cannot be reassigned 
        return PI * radius * radius;
    }
    console.log(PI);  //Error, PI is block scoped
    return 0;
}

console.log(getAreaOfCircle(10));
console.log(getAreaOfCircle());
//console.log(PI);

Destructuring

The destructuring feature allows us to assign values to a set of variables from a stream of data without navigating through each entry in the stream. The stream can be an object, an array or any value of one of these types returned from a function.

Consider the following object:

var topic = {name:'ECMAScript 6',
comment: 'Next version of JavaScript',
browserStatus: {
    chrome: 'partial',
    opera:'partial',
    ie: 'very less',
    ieTechPreview: 'partial'
}};

Let’s assign values of name and comment to two different variables using destructuring:

var {name, comment} = topic;

The above statement declares the variables name and comment and assigns values of these properties in the object topic. If a property with identical name is not found in the object, the variable would contain undefined. It is also possible to destructure properties out of an object present at any level inside the main object. For example, the following snippet gets values assigned to chrome and ieTechPreview properties of the browserStatus object.

var {browserStatus:{chrome : chromeSupport, ieTechPreview: iePreviewSupport}} = topic;

Destructuring can be applied on an array of values as well. Following snippet shows this:

var numbers=[20, 30, 40, 50];

var [first, second, third ] = numbers;
console.log(first);    //20
console.log(second);    //30
console.log(third);    //40

It is possible to skip some numbers out of the array and destructure rest of them. Following snippet shows this feature:

var [, , fourth, fifth ] = numbers;

console.log(fourth);    //40
console.log(fifth);    //50

Destructuring can also be applied on parameters passed to a function. The way to apply destructuring remains the same. Following is an example:

function add({firstNumber, secondNumber}){
    return firstNumber + secondNumber;
}

console.log(add({firstNumber: 94, secondNumber: 19}));

Spread

The spread operator is used to expand values in an array into individual elements. The spread list can be used to pass into a function as arguments or, as part of another array.

Syntax for using the spread operator is the array object prepended with three periods (dots). Following snippet shows the syntax and usage within a function:

function printPersonDetails(name, place, occupation){
    console.log(name + " works at " + place + " as a/an " + occupation);
}
printPersonDetails(...['Ravi', 'Hyderabad', 'Software Professional']);

Here, the three values would be assigned to three parameters in the function. Spread operator can be used to make an array a part of another array as well.

var arr = [1, 2, 3];
var arr2 = [19, 27, 12,...arr, 63, 38];
console.log(arr2);

In the above snippet, the resulting array arr2 would contain 8 elements in it.

Functions in ES6

The Arrow function

As C# developers, we love lambdas. When we write code in another language, we miss this feature at times. ES6 has made us happy by including this feature in the JavaScript language.

When we write large scale JavaScript, callbacks and anonymous functions are inevitable. Most of the popular JavaScript libraries embrace extensive usage of callbacks and currying. Arrow functions add a syntactic sugar to the way we write anonymous functions and make development more productive.

For example, say we have a list of employees and we need to find the average of their age. We can do it using a callback passed into the Array.forEach function as shown below:

var employees = [{name:'Alex',age:33},
    {name:'Ben',age:29},
    {name:'Craig',age:57},
    {name:'Dan',age:27},
    {name:'Ed',age:48}];
var totalAge = 0;

employees.forEach(function(emp) {
    totalAge += emp.age;
});

console.log("Average age: " + totalAge / employees.length);

Let’s replace callback in the forEach function using an arrow function. By adding the arrow function, we can get rid of the function keyword and parentheses as we just haveone argument. Following is the modified usage of forEach function:

employees.forEach(emp => {
    totalAge += emp.age;
});

If the callback has just one statement with a return value, we can get rid of the curly braces as well. To demonstrate this, let’s find if every employee in the above array is aged over 30:

var allAbove30 = employees.every(emp => emp.age >= 30 );
console.log(allAbove30);

In addition to providing syntactic sugar, arrow functions ease usage of this reference in the callbacks. Callbacks are executed in the context of the object that calls them and so this reference inside a callback method refers to the object used to call the method. For example, consider the following snippet:

var events = {
    registeredEvents:[],
    register: function(name, handler){
        this.registeredEvents.push({name: name, handler: handler});
    },
    raise: function(name){
        var filtered = this.registeredEvents.filter( e => e.name === name)[0];
        filtered.handler();
    }
};
var worker1 = {
    name:'Ravi',
    registerWorkDone: function(){
        events.register('workDone', function(){
            console.log(this.name + "'s current task is done! Free for new assignment.");
        });
    }
};
worker1.registerWorkDone();
events.raise('workDone');

//Output: workDone's current task is done! Free for new assignment.

Here we have an event object and a worker object. The worker object registers an event, in which it attempts to print name of the worker. But as the event is called by the event entry object, where name refers to name of the event, it prints workDone instead of printing Ravi. In order to avoid it, we need to cache the this reference in a local variable and use it. Following snippet shows this:

var worker1 = {
    name:'Ravi',
    registerWorkDone: function(){
        var self=this;
        events.register('workDone', function(){
            console.log(self.name + "'s current task is done! Free for new assignment.");
        });
    }
};

//Output: Ravi's current task is done! Free for new assignment.

If we use arrow functions, we don’t need to deal with these difficulties. Arrow functions are executed in the context of the containing object and so this reference would always refer to the object inside which the function is written.

var worker2 = {
    name:'Kiran',
    registerWorkDone: function(){
        events.register('workDone2', () => {
            console.log(this.name + "'s current task is done! Free for new assignment.");
        });
    }
};
worker2.registerWorkDone();
events.raise('workDone2');

//Output: Ravi's current task is done! Free for new assignment.


Default Parameters

Another feature that is commonly available in other programming languages is providing default values to parameters of functions. Value of the parameter would be set to default if the value is not passed while calling the function. Today we can do it in JavaScript, by checking if the parameter has any value.

function sayHi(name, place){
    name = name || 'Ravi';
    place = place || 'Hyderabad';
    console.log("Hello, " + name +" from " + place + "!");
}


We need to add a statement for every parameter that needs a default value and there are good possibilities of missing the parameters. ES6 adds the feature of specifying default values to the parameters inline in the function signature. Default value is assigned when either value is not passed in for the parameter or, set to null. Following snippet demonstrates some use cases of default values:

function sayHi(name = 'Ravi', place = 'Hyderabad'){
    console.log("Hello, " + name +" from " + place + "!");
}
sayHi();
sayHi('Ram');
sayHi('Hari','Bengaluru');
sayHi(undefined, 'Pune');

Value of one parameter can be used to calculate default value of another parameter. Let’s add another parameter message to the function above. It would use the parameter name in its value:

function sayHi(name = 'Ravi', place = 'Hyderabad', message = "No additional message for " + name){
    console.log("Hello, " + name +" from " + place + "! (" + message + ")");
}
sayHi('Ram');
sayHi('Hari','Bengaluru', 'Message from Rani, Bengaluru');

Rest Parameters

The Rest parameter allows the caller to specify any number of arguments to the function. All passed values are stored in a single array (similar to params in C#).

Now you may think what’s the difference between a rest parameter and arguments? Basic difference is, arguments is not an array. It is an object with some properties taken from array; whereas a rest parameter is an array.

Syntax to specify Rest parameter is by prefixing the parameter with three periods (…). Following snippet shows the syntax:

function addNumbers(...numbers){
    var total = 0;
    numbers.forEach(number => {
        total += number;
    });
    console.log(total);
}

You can call this function by passing any number of values (numbers) to it and it would print sum of all the numbers. Other parameters can also be accepted along with rest parameters, but it is advised to always use rest parameter as the last parameter.

function addEmployeesToDepartment(departmentId, ...employees){
    var department=getDepartment();
    employees.forEach(emp => {
        department.employees.push(emp);
    });
}

Classes in ES6

Constructor, Properties, Methods

While writing large JavaScript applications, we may want to enclose certain pieces of logic inside units like classes. Classes could be nice abstraction layers in JavaScript apps as they can contain data and logic together. Though we cannot write classes in JavaScript like we do in Object Oriented languages, we can write similar constructs using prototype property of objects. We also can inherit new classes using the prototype property.

There is a learning curve around the prototype property before one can use it comfortably to write OOPs like code in JavaScript. To ease this, ES6 includes classes. Using ES6 class feature, we can write classes, create objects, inherit and override features of parent class inside child classes.

Following snippet shows syntax of writing a class:

class City{

    constructor(name, country){
        this._name=name;
        this._country=country;
    }

    startDay(){
        return "Good Morning!";
    }

    get name(){
        return this._name;
    }

    set name(value){
        this._name=value;
    }

    get country(){
        return this._country;
    }

    set country(value){
        this._country=value;
    }
}

As you can see, this class has the following components:

  • A constructor: The constructor is defined using the constructor keyword. Here, the constructor accepts two parameters and sets them to class members
  • Two properties, name and country: Keywords for defining properties are get and set. You can create a property with only get; that will be a read only property. Properties in the city class are used to get value of and set value to the class members _name and _country respectively. Names of property and its corresponding member should be different; otherwise it would lead to an infinite recursion.
  • A method, startDay. The syntax of the method may have puzzled you, as it doesn’t have the function keyword attached. This is the new way of creating methods in ES6. This syntax can be used to define methods inside objects as well.
var city= {
    startDay()
    {
        return "Starting day...";
    }
};

Now that we have defined a class and added some members to it, we can create an object and play with it. Syntax for creating an instance of the class is also similar to the one we do in OOPs based languages.

let city = new City("Hyderabad", "India");
console.log(city.startDay());
console.log(city.name);
city.name="Bengaluru";
console.log(city.name);

Inheritance

Classes can be inherited from other classes using extends keyword. All members of parent class are accessible using objects of child class. Members of parent class can be invoked from child class using super keyword.

Let’s write a child class MetroCity by deriving from City class. The first thing that we would like to do while writing a child class is to invoke constructor of the parent class. Parent class constructor can be called using super keyword. Calling of parent class constructor need not be the first statement in the child class; but it is a good practice to do it as the first thing.

class MetroCity extends City
{
    constructor(name, country, hasMetroRail)
    {
        super(name, country);
        this._hasMetroRail = hasMetroRail;
    }
}

Let’s override the startDay method inside the child class. Following is the snippet:

startDay(){
    return super.startDay() + "\n Good Morning from Radio 365!";
}

Notice the way it uses the keyword super to call the parent class method. Now that the child class is ready, let’s create an object and call the startDay method.

let metroCity = new MetroCity("New Delhi", "India", true);
console.log(metroCity.startDay());

This snippet prints the combined message from parent class and child class.
 

Iterators in ES6

Built-in Iterators

ES6 has iterators to help us loop through arrays. Iterators are objects with a method called next implemented in it and this method returns an object that has the following properties:

  • value: Value of the occurrence
  • done: A Boolean value indicating if the last element is reached

The simplest to understand iterators is using one of the built-in iterators. Every array in ES6 has an iterator associated with it. It can be invoked using values() method on any array. Following snippet shows usage of this iterator using a while loop:

let sum = 0;
let numbers = [10, 20, 30, 40, 50];
let iterator = numbers.values();
let nextNumber = iterator.next();
while(!nextNumber.done){
    sum += nextNumber.value;
    nextNumber = iterator.next();
}

The next method helps in iterating through the array and fetching the next item from the array with status of iteration. The while loop continues till the last element in the array. Last element returned by the iterator has value set to null and done set to true.

For…of loop

A new loop has been added in ES6 to ease iteration through iterators. Using the for…of loop, we can iterate without calling the next method and checking done property explicitly. The array internally performs all the required logic to continue iteration. Following snippet shows the syntax of using for…of loop.

for(let n of numbers){
    sum += n;
}

This loop does the same work as the while loop we just saw.

Defining Custom Iterators

We can define custom iterators to iterate over object of a class. Say, we have a shelf class with an array containing books in it.

class Shelf{
    constructor(){
        this.books = [];
    }

    addBooks(...bookNames){
        bookNames.forEach(name => {
            this.books.push(name);
        });
    }
}

Let’s add an iterator to make objects of this class iterable. In C#, we need to implement the IEnumerable interface on a class to make it iterable. In ES6, we don’t have interfaces, but we need to implement a special type of function to make the objects iterable.

ES6 has a special object, Symbol. Value of the Symbol object is unique on every usage. This object contains a set of well-known symbols, including iterator. The iterator returns default iterator for an object and it can be used to create an iterable class.

To make the above Shelf class iterable, we need to add a method to the class and name it as iterator symbol. Presence of this method indicates that an object of this class is iterable. The method should do the following:

  • return an object containing the method next; this method is used to continue iteration
  • the next method should return an object containing value of the entry and a Boolean property done to indicate if the iteration is completed.

Following is the iterator method:

[Symbol.iterator](){
    let obj=this;
    let index = 0;
    return {
        next: function(){
            var result = {value: undefined, done: true};
            if(index < obj.books.length){
                result.value = obj.books[index];
                result.done=false;
                index += 1;
            }
            return result;
        }
    }
}

Now, you can create an object of the Shelf class and iterate through it using for…of loop.

var shelf = new Shelf();
shelf.addBooks('Programming ASP.NET MVC 5','Getting lost in JavaScript','AngularJS in Action','C# 6: What\'s new in the language' );
for(let book of shelf){
    console.log(book);
}

Generators

These techniques of creating iterators that we just saw may not always be the best option as we need to follow a specified set of rules to make a class iterable.

Generators make the process of creating iterators easier. Generators follow a special syntax. Following are some features of the syntax:

· A generator function has an asterisk in its name

· Uses yield keyword to return values

Use of the yield keyword reduces a lot of work for us. A function can return multiple values using the yield keyword and it is smart enough to remember state of the last iteration and return next value when asked in the subsequent call.

Let’s rewrite the above iteration function using yield keyword.

*[Symbol.iterator](){
    for(let i=0;i < this.books.length; i++){
        yield this.books[i];
    }
}

This would produce the same result as the function we wrote earlier; but in this case we got the result with less code.

Further Reading:

ECMAScript 6 – New Objects and Updates to Existing Objects

Modules in ECMAScript 6 (ES6)

Asynchronous Programming in ES6 Using Generators and Promises

Meta Programming in ES6 using Symbols

Conclusion

ES6 comes with a number of features that makes JavaScript a better language to work with in large scale applications and adds a lot of useful syntactic sugar at the same time. Many companies (including AngularJS team) have already started using ES6 on a daily basis. Being web developers, we cannot keep waiting for the final call to learn new features of our favorite programming language. This article covered features of ES6 that makes JavaScript look and feel better; we will explore the new APIs and some more features of ES6 in our forthcoming articles. Stay tuned!

Download the entire source code from our GitHub Repository at bit.ly/dncm16-ecmascript

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like AngularJS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (Visual Studio and Dev Tools) and DZone MVB awards for his contribution to the community


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Chris on Monday, February 23, 2015 10:14 AM
Thanks for your article. It made everything pretty much clear. It will be like c# for client side :)

Chris
http://www.flybreak.com
Comment posted by flyingbeagle70 on Tuesday, March 3, 2015 12:26 AM
Excellent article. Ravi  Can't wait to use some of the stuff we've had in C# for a long time.  Nice to see real classes.