Advertisement

Eight Cool Features Coming in ES6

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

ES6 is coming and with it brings many great things that will help us develop better and faster web applications. Let's have a look at the most significant ones.


Syntax Sugar

In ES6 there is a lot of syntax sugar that will shorten development time and speed up our applications. Let's have a taste, shall we?

Destructuring

This one is really interesting. For example, you can destructure an object into separate variables:

var point = { x: 1, y: 2 };
var [ x, y ] = point;

console.log(x); // 1
console.log(y); // 2

Another use for it would be returning a few different values in a function - no need to return an object anymore. We can also omit some of the returned values if we need to. So this code:

function returnsFewValues() {
	return {
		x: 1,
		y: 2
	}
}

Can be replaced with this:

function returnsFewValues() {
	return [ 1, 2 ];
}

Let and Const

These two are really useful. Both of them allow us to lock a variable in scope, so it will be inaccessible outside of that scope: let is for normal variables and const is for constants, values that never change (const is an old idea which finally came to JavaScript). We can now forget about using ugly closures:

function x() {
	(function () {
		var a = 2;
		a = a + 3;
	}());
}

console.log(a); // undefined

These can be replaced with more elegant let statements (of course, for more applicable use than this example):

function x() {
	let a = 2;
	a = a + 3;
}

console.log(a); // undefined

For-Of and Array Comprehensions

for-of is the younger brother of the old for-in statement. Using it, we iterate over values, not over keys:

var someArray = [ "a", "b", "c" ];

for (v of someArray) {
	console.log(v);
}

It is also used in array comprehensions. They are inspired by mathematical set notation. For example, if you have an array of names, and you want to grab only the ones that are eight or more letters in length. Before ES6, you would do it like this:

var eightLetterLongNames = [];
someNamesArray.forEach(function (name) {
	if (name.length >= 8) eightLetterLongNames.push(name);
});

But in ES6, it is much easier and cleaner:

var eightLetterLongNames = [ x for (name of someNamesArray) if (name.length >= 8) ];

Arrow Functions

Ever needed to only return a value or log something from a function? Usually, we did it like this:

element.addEventListener('click', function (e) { console.log(e); });

Another bit of syntactic sugar from ES6 are the arrow functions, which define a much better approach for one-line functions:

element.addEventListener('click' (e) => console.log(e));

Spread Operator, Rest Parameter, and Default Arguments

Lets start with the last one. We can finally define default arguments and forget about the old argument = argument || "default value" incantation. Just define the function like this:

function (arg1 = "default value", arg2 = false, arg3) {
	// function body
}

Spread operator allows us to pass an array like separate arguments to a function. Instead of this:

var someArray = [ "a", "b", "c" ];

console.log.apply(window, someArray);

You can now do it like so:

var someArray = [ "a", "b", "c" ];

console.log(...someArray);

Rest parameter is a perfect solution if you want your function to accept a few named arguments and later accept an unlimited number of them. The syntax is pretty simple and saves us from doing this, the ugly way:

function logWithTag(tag) {
	var logs = [].slice.call(arguments, 1);
	logs.forEach(function (log) {
		console.log(tag + ': ' + log);
	});
}

The above syntax is what we're used to doing, but the new one, is much more readable:

function logWithTag(tag, ...logs) {
	logs.forEach(function (log) {
		console.log(tag + ': ' + log);
	});
}

Or even shorter, now with arrow functions:

var logWithTag = (tag, ...logs) => logs.forEach((log) => console.log(tag + ': ' + log));

Classes

Finally, ES is turning into a true object-oriented language. With the introduction of classes, we can write our code in a clean and structured way. It will remove the need for the current coded solutions present in nearly every popular library:

// ExtJS
var SimpleClass = new Ext.Class({
	name: 'John Doe',
    initialize: function(name) {
		this.name = name;
	},
    say: function (message) {
        return this.name + ': ' + message;
    }
});

// PrototypeJS
var Person = Class.create();
Person.prototype = {
	name: 'John Doe',
	initialize: function(name) {
		this.name = name;
	},
	say: function (message) {
		return this.name + ': ' + message;
	}
};

These can be replaced with pure JavaScript:

class Person {
	public name: 'John Doe'
	
	initialize(name) {
		this.name = name;
	}
	
	say(message) {
		return this.name + ': ' + message;
	}
}

It also supports inheritance and extending classes, so you can use it to produce fully object-oriented applications like you do in Java. This way, your code gets much cleaner and easier to read.


Modules

Another exciting feature of ES6 are modules. For now, there are few alternative solutions to modularize your app, mainly CommonJS and AMD. Here's some examples:

/* CommonJS */

// point.js
function Point(x, y) {
	this.x = x;
	this.y = y;
}
module.exports = Point;

// myapp.js
var Point = require("point");
var origin = new Point(0, 0);
console.log(origin);

/* AMD */

// point.js
define("point", function() {
	function Point(x, y) {
		this.x = x;
		this.y = y;
	}
	return Point;
});

// myapp.js
define("myapp", ["point"], function(Point) {
	var origin = new Point(0, 0);
	console.log(origin);
});

ES6 removes the need for these libraries, as it has everything you need to split your code into smaller pieces. Together with classes, you get clean and maintainable code:

// point.js
module "point" {
	export class Point {
		constructor (x, y) {
			public x = x;
			public y = y;
		}
	}
}

// myapp.js
module point from "/point.js";
import Point from "point";

var origin = new Point(0, 0);
console.log(origin);

Maps and Sets

Maps and sets were long absent from JavaScript. They were imitated by using Objects, but it was quite messy when you wanted to iterate over them - you had to check if the property is not in the prototype, and maybe if it is not a method:

var map = {
	a: 1,
	b: 2,
	c: 3
};

for (var k in map) {
    if (map.hasOwnProperty(k)) {
        console.log(map[k]);
    }
}

for (var k in map) {
    if (map.hasOwnProperty(k)) {
        console.log(k);
    }
}

The ES6 approach is much cleaner:

var map = new Map;
map.set("a", 1);
map.set("b", 2);
map.set("c", 3);

for (var v in map.values()) {
	console.log(v);
}

for (var k in map.keys()) {
	console.log(k);
}

As for sets, they are similar to arrays, but the values are sequenced and can't be duplicated. Here's a small example of how to create a set:

var set = new Set;

set.add(1);
set.add(2);
set.add(3);

for (var v in set.values()) {
	console.log(v);
}

Iterators

Iterators are special functions that will help to iterate over a custom object. They are also called generators, because when used alone, they can generate content (like Fibonacci numbers or random ones). The functions values() and keys() that you saw above are default iterators for Maps. Basically, you define an iterator just like a function, but there's an asterisk between the name and function statement:

function *fibonacci() {
	let [ current, next ] = [ 0, 1 ];
	for (;;) { // infinite loop
		[ current, next ] = [ next, current + next ];
		yeld current;
	}
}

With this approach, the scope is suspended, and you get the next values by calling next() on the returned value or by iterating over it:

var fib = fibonacci();

console.log(fib.next()); // 1
console.log(fib.next()); // 2

for (var v of fib) {
	if (v > 100) break;
	console.log(v); // 3, 5, 8 ...
}

Weak Maps

Memory leaks were always a problem, especially when it comes to web applications. One common source were collections, where keys were not strings or numbers (for example, you may have had objects as keys). Usually the implementation used two arrays - one for keys and one for values. This is bad, as the array of keys is keeping the key objects from being garbage-collected and here is where the memory leaks were produced.

Weak Maps have the advantage of having references to the key objects held weakly, meaning it would not block the garbage collector if other references to the key object would be gone. Check out the example below:

var key1 = window, key2 = document;

var map = new WeakMap(), map2 = new WeakMap();

map.set(key1, "some value");
map.set(key2, key1);
map.set(map2, map); // you can even do a circular reference

Proxies

Data binding is a useful feature of many DOM-manipulation libraries. Now, with the use of Proxies, its syntax and performance can be improved. Also, Proxies can be used to intercept changes on objects and log them, if you need to. The biggest change here is that you don't need to use any get() and set() functions on the spied object, so you can remove the logging without any changes in the later code. Here's an example of a logging proxy:

var logger = function(obj, info) {
	return Proxy.create({
		get: function get(receiver, prop) {
			info('Getting ' + prop);
			return obj[prop];
		},
		set: function set(receiver, prop, value) {
			info('Setting ' + prop + ' to ' + value + '; was ' + obj[prop]);
			debugger;
			obj[prop] = value;
		}
	});
};

var object = { a: 'some value', b: 22 };
// this can be removed later when you don't need to log anything anymore:
object = logger(object, function info(msg) {
	console.log(msg);  
});

document.write(obejct.a);
object.a = 'new value';
document.write(object.a);

Template Strings

Many of us really missed template strings in JavaScript. These not only allow us to easily insert variables in the code, but also to secure the output from various attacks. Template strings are defined using backticks `. Inside, you can use a dollar sign with brackets to insert executable code or variables:

var name = "John", surname = "Doe";
var template1 = `Hello! My name is ${name} ${surname}!`;

console.log(template1); // Hello! My name is John Doe!


var a = 1, b = 2, c = 3;
var template2 = `${a} + ${b} - ${c} / 2 = ${ a + b - c / 2 }`;

console.log(template2); // 1 + 2 - 3 / 2 = 1.5

Tag

You can also define a tag before the template string. Tag is a function which will parse separate parts of the template string, so you can do something with user-generated content. There will be a default set of tags in ES6. The most useful one, I think will be the safehtml tag. In brief, it will detect unsafe content like XSS attacks etc. and change them to innocuous values. Example tag usage:

var template = safehtml`<a href="${someUserdefinedVariable}">Click me!</a>`;

Conclusion

If you didn't know about these features coming with the new ES6 standard, now you probably want them to come as quick as possible! At least, that was my reaction when I discovered them one by one. The examples provided here are only a handful of possible improvements. If you want to know more about ES6, here are some additional resources:

The new standard is most widely supported in new Firefox versions, so if you want to play around with some of these features, give Firefox a try.

Advertisement