Brief History
For years, many enterprise developers have doubted if JavaScript could serve as a workable programming language and no doubt, early JavaScript had its quirks. But all that has changed significantly now, all thanks to ES6 (also known as ECMAScript 2015). ES6 brought with it, many much-needed improvements and new features and is considered a significant move forward for JavaScript.
Even though ES6 was released back in 2015, the browsers took time to add support for the new specs. As of this writing, most major browsers support most ES6 features out-of-the-box. If you still want to check browser support for a specific feature, you can check it on MDN Web Docs. If you’re using Typescript, you don’t have to worry about browser support anyway since your code will eventually be transpiled to a lower ES version.
In this article, we’re going to see the key ES6/ECMAScript 2015 features/improvements. Alright then, let’s get started…
ES6 Features
- Arrow Functions
- Classes
- Template Literals / Template Strings
- Block-Scoped
let
andconst
keywords - Enhanced Object Properties
- Default Parameters
- Rest Parameters
- Spread Operator
- Destructuring Assignment
- Iterator &
for…of
Operator - Promises
- Modules
- Map/Set & WeakMap/WeakSet
- Symbol Type
1. ES6 Arrow Functions
Since you’ve been working with JavaScript, you probably already know that the this
keyword refers to the containing object, in case of functions, the this
keyword inside a function body refers to the function itself. You’d sometimes want the this
keyword to refer to a consistent parent. Arrow functions allow you to do just that and also helps you reduce the code by a few lines in the process.
Arrow function expressions are a function shorthand that uses the arrow (=>
) syntax. They support both statement block bodies as well as expression bodies which return the value of the expression. Unlike regular functions, arrows share the same lexical this
as their surrounding code.
// Expression bodies var odds = evens.map(v => v + 1); var nums = evens.map((v, i) => v + i); var pairs = evens.map(v => ({even: v, odd: v + 1})); // Statement bodies nums.forEach(v => { if (v % 5 === 0) { fives.push(v); } }); // Lexical this var bob = { _name: "Bob", _friends: [], printFriends() { // this refers to the parent object `bob` this._friends.forEach(f => console.log(this._name + " knows " + f)); } }
2. ES6 Classes
If you love object-oriented programming (OOP), then you’ll love this feature. The new JavaScript class syntax brings you closer to the classic Object Oriented programming paradigm. However, the point to be noted is that JavaScript classes are primarily syntactical sugar over JavaScript’s existing prototype-based inheritance pattern. The class syntax does not introduce a new object-oriented inheritance model to JavaScript.
In short, JavaScript does not actually support classes the same way as, say, Java does. Classes in JavaScript are in fact “special functions”, and just as you can define function expressions and function declarations, the class syntax has two components: class expressions and class declarations.
Class Definition
// Class Declaration class Rectangle { constructor(width, height) { this._width = width; this._height = height; } } // OR // Class Expression let Rectangle = class { constructor(height, width) { this.height = height; this.width = width; } }; // A new instance of this class can be created with const rect = new Rectangle(100, 200); console.log(rect._width); // Prints 100
Getters / Setters
Getters and Setters allow getting and setting values of class properties. A Setter function accepts the new value as parameter while a Getter does not accept any parameters and returns the current value of the class property.
class Rectangle { constructor(width, height) { this._width = width; this._height = height; } get width() { return this._width } set width(val) { this._width = val; } } // A new instance of this class can be created with const rect = new Rectangle(100, 200); console.log(rect.width); // Prints 100 rect.width = 200; console.log(rect.width); // Prints 200
Static Methods
Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application. Generally speaking, any class method which does not have reference to this
can be tagged as static method.
class Rectangle { constructor(width, height) { this._width = width; this._height = height; } area() { return this._width * this._height; } static printArea(area) { console.log('The area of the rectangle is ' + area); } } const rect = new Rectangle(100, 200); const area = rect.area(); // non-static method invocation Rectangle.printArea(area); // static method invocation
Inheritance
The extends
keyword is used in class declarations or class expressions to create more intuitive, OOP-style and boilerplate-free inheritance.
class Rectangle extends Shape { constructor (id, x, y, width, height) { super(id, x, y) this.width = width this.height = height } }
3. Template Literals / Template Strings
Template strings provide syntactic sugar for constructing strings. Optionally, a tag can be added to allow the string construction to be customized, avoiding injection attacks or constructing higher level data structures from string contents.
// Basic literal string creation `In JavaScript '\n' is a line-feed.` // Multi-line strings `In JavaScript this is not legal.` // String interpolation var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // Construct an HTTP request // is used to interpret the replacements and construction POST`http://foo.org/bar?a=${a}&b=${b} Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}`(myOnReadyStateChangeHandler);
4. Block-Scoped let
and const
keywords
let
and const
provide block-scoped binding constructs. let
is the new var
. It declares a block scope local variable, optionally initializing it to a value. The value of a variable declared with let statement can be re-assigned. const, on the other hand, is single-assignment. The value of a const can’t be changed through reassignment, and it can’t be re-declared. Static restrictions prevent use before the assignment.
function f() { { let x; { // okay, block scoped name const x = "sneaky"; // error, cannot re-assign const x = "foo"; } // error, already declared in block let x = "inner"; } }
5. Enhanced Object Properties
Object literals are extended to support setting the prototype at construction, shorthand for foo: foo
assignments, defining methods, making super calls, and computing property names with expressions. Together, these also bring object literals and class declarations closer together and let object-based design benefit from some of the same conveniences.
Property Shorthand
Very handy if you want to define an object who’s keys have the same name as the variables passed-in as properties, you can use the shorthand and simply pass the key name.
var x = 0, y = 0; const obj = { x: x, y: y }; // old const obj = { x, y }; // new
Computed (dynamic) property names
ES6 Computed property names allow you to dynamically create property names using an expression in brackets ( [ ] ) similar to property accessors.
// old way var obj = { foo: "bar" }; obj[ "baz" + quux() ] = 42; // now possible let obj = { foo: "bar", [ "baz" + quux() ]: 42 }
Method Properties
Allows method notation in object property definitions, for both regular functions and generator functions.
// old way obj = { foo: function (a, b) { … }, bar: function (x, y) { … }, // quux: no equivalent in ES5 … }; // now possible obj = { foo(a, b) { … }, bar(x, y) { … }, * quux(x, y) { … } }
6. Default Parameters
Default Parameters allow you to set default values for the function parameters if no value is passed or if undefined is passed. Default parameters are a great way to make your methods more organized, and in some cases even shorter. Overall, default function parameters help you focus more on the actual purpose of the method without the distraction of lots of default preparations and if statements.
// old way function multiply(a, b) { if (b === undefined) { b = 1; } return a * b; } // New way - with default parameter function multiply(a, b = 1) { return a * b; } console.log(multiply(5, 2)); // expected output: 10 console.log(multiply(5)); // expected output: 5
7. Rest Parameters
The rest parameter syntax allows us to represent an indefinite number of arguments as an array. Spread Operator helps bind trailing parameters to an array. It replaces the need for arguments
and addresses common cases more directly.
function f(x, ...y) { // y is an Array ["hello", true] return x * y.length; } f(3, "hello", true) == 6
8. Spread Operator
The spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
function f(x, y, z) { return x + y + z; } // Pass each element of array as argument f(...[1,2,3]) == 6
9. Destructuring Assignment
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. Destructuring allows binding using pattern matching, with support for matching arrays and objects. Destructuring is fail-soft, similar to standard object lookup foo["bar"]
, producing undefined
values when not found.
Basic Example
// Old way var tmp = getASTNode(); var op = tmp.op; var lhs = tmp.lhs; var rhs = tmp.rhs; // Now possible var { op, lhs, rhs } = getASTNode();
More Examples
// list/array matching var [a, , b] = [1,2,3]; // object matching var { op: a, lhs: { op: b }, rhs: c } = getASTNode(); // object matching shorthand // binds `op`, `lhs` and `rhs` in scope var {op, lhs, rhs} = getASTNode(); // Can be used in parameter position function g({name: x}) { console.log(x); } g({name: 5}); // Fail-soft destructuring var [a] = []; a === undefined; // Fail-soft destructuring with defaults var [a = 1] = []; a === 1;
10. Iterator & for...of
Operator
Adds support for the “iterable” protocol to allow objects to customize their iteration behavior. Additionally, adds support for the “iterator” protocol to produce a sequence of values (either finite or infinite). Finally, provide convenient of
operator to iterate over all values of an iterable object.
The for...of
statement is the latest addition to the list of iteration controls in JavaScript that can create a loop iterating over iterable objects, including built-in String
, Array
, array-like objects (e.g., arguments
or NodeList
), TypedArray
, Map
, Set
, and user-defined iterables.
// Iterator let fibonacci = { [Symbol.iterator]() { let pre = 0, cur = 1; return { next() { [pre, cur] = [cur, pre + cur]; return { done: false, value: cur } } } } } // for...of for (var n of fibonacci) { // truncate the sequence at 1000 if (n > 1000) break; console.log(n); }
11. ES6 Promises
Promises in ES6 are a library for asynchronous programming. A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation. Promises are used in many existing JavaScript libraries.
function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration); }) } var p = timeout(1000).then(() => { return timeout(2000); }).then(() => { throw new Error("hmm"); }).catch(err => { return Promise.all([timeout(100), timeout(200)]); })
12. ES6 Modules
As you may know, there was no native modules support in JavaScript before ES6. People came up with AMD, RequireJS, CommonJS and other workarounds. Now there are modules with import
and export
operands. With ES6 there are now built-in modules with import and export operands. ES6 modules provide support for exporting/importing values from/to modules without polluting the global namespace.
// lib/math.js export function sum(x, y) { return x + y; } export var pi = 3.141593; // app.js import * as math from "lib/math"; alert("2π = " + math.sum(math.pi, math.pi)); // otherApp.js import {sum, pi} from "lib/math"; alert("2π = " + sum(pi, pi));
13. Map/Set & WeakMap/WeakSet
Provides efficient data structures for common algorithms.
The Map
object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.
The Set
object lets you store unique values of any type, whether primitive values or object references.
The WeakMap
object is a collection of key/value pairs in which the keys are weakly referenced. The keys must be objects and the values can be arbitrary values.
The WeakSet
object lets you store weakly held objects in a collection.
// Sets var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true; // Maps var m = new Map(); m.set("hello", 42); m.set(s, 34); m.get(s) == 34; // Weak Maps var wm = new WeakMap(); wm.set(s, { extra: 42 }); wm.size === undefined // Weak Sets var ws = new WeakSet(); ws.add({ data: 42 }); // Because the added object has no other references, it will not be held in the set
14. ES6 Symbol Data Type
Symbols allow properties to be keyed by either string
(as in ES5) or symbol
. Symbols are a new primitive type. Symbols are unique and immutable data type that are to be used as an identifier for object properties. Symbol can have an optional description, but for debugging purposes only.
var MyClass = (function() { // module scoped symbol var key = Symbol("key"); function MyClass(privateData) { this[key] = privateData; } MyClass.prototype = { doStuff: function() { ... this[key] ... } }; return MyClass; })(); var c = new MyClass("hello") c["key"] === undefined
I hope you enjoyed the article. If you want some practice, you can use the sandbox for the learning process: https://es6console.com/. If you need more information, you can find it here:
You may also like: JavaScript Style Guide: Key takeaways from Google’s Style Guide
Be sure to follow us on the social media to get notified about the latest posts as soon as they’re published
Featured Image Credit: Photo by You X Ventures on Unsplash.