Last updated at Fri, 08 Dec 2023 18:19:20 GMT
ECMAScript 6 brings powerful new capabilities and some tasty syntactical sugar to the ubiquitous Javascript language, as it continues to grab an ever increasing slice of developer mindshare.
Now that the ES6 feature set is frozen and just minor refinements will be made over the coming months, much of this new functionality has already landed in modern browsers, and will continue to roll out with each new browser update.
For the impatient, there are also now several excellent precompile / polyfill / shim tools available, so you can start using these new features immediately; safely knowing that your code will soon run ‘natively’ on every Javascript platform.
Javascript has become far more successful and ubiquitous than Brendan Eich or Netscape ever imagined, and several of the significant changes in the new spec, are designed with server side scripting platforms in mind (eg Node.js, and various scriptable database engines…).
This is an overview of some of the key changes in ES6
Block Bindings
A new let
keyword declares a variable (like var
) but restricts its scope to the current code block (unlike var
which is only scoped to function block); let
also does not share var
hoisting characteristics, so a let
variable cannot be used until after its declaration (or it will throw an error).
function foo(){
let x = 123;
if(true){
let x = 456; // different variable
console.log(x); // 456
}
console.log(x); // 123
}
var i=0;
for(let i=i; i<10; i++){
console.log(i); // 0 1 2 3 4 5 6 7 8 9
}
console.log(i) // 0
const
is similar to let except once initialized, cannot be changed. const
declarations share the same block scoping as let
.
const PI = 3.14159265359;
let circumference = 2 * PI * r;
Arrow functions
Syntactical sugar (mostly) to facilitate simple function declaration, which is particularly handy for anonymous functions.
// these are equivalent
var sq = x => x * x
var sq = function(x){ return x * x }
However, there are some important differences with standard function declarations:
- The value of
this
inside an Arrow function is always determined by where it is defined, not where it is used*(avoiding common “var self = this” shuffle)* - Arrow functions cannot change
this
within the function - Arrow functions cannot be used with
new
as constructors - Arrow functions have no
arguments
object, so must use standard named or new rest parameters
var sum = (a,b) => a + b;
var getName = () => "Pablo"; // empty parenthesis needed when no params
var foo = (a,b) => {
let c = a + b;
console.log(a,b,c); // 1 2 3
}
Because curly braces are used for the function’s body, to return an object literal (outside of a function body) you must wrap the literal in parentheses.
var getObj = id => ({ id:id, name:"ObjA" });
Destructured assignment
Several variables can be initialized in a single statement and the following are equivalent:
var [d, m, y] = [13, 2, 1963];
var d = 13, m = 2, y = 1963;
// useful for swapping
[x, y] = [y, x]
// or multiple return values
function now(){ return [13, 2, 2013, 8, 0]; }
var [d, m] = now(); // d = 13, m = 2
var [,,y] = now(); // y = 2013
Destructuring also works with objects.
function today() { return { d:13, m:2, y:1963 }; }
var { m:month, y:year } = today(); // month = 2, year = 1963
// shorthand
var { m, y } = today(); // m = 2, y = 1963
Destructuring assignments can also work with nested elements.
Parameters
There are several new conveniences for working with parameters.
Default Parameters
You can now assign default values to function parameters, which will be used if the parameter is not formally passed.
function myFunc( url, timeout=3000, callback=function(){} )
Rest Parameters
Designed to replace use of arguments
when working with variable numbers of parameters, and indicated by three dots ...
preceding a named parameter. The named parameter then becomes an array containing the rest of the parameters (passed but not specified).
function sum(first, second, ...others){
console.log(others.length);
}
Spread Operator
Spread is the opposite of Rest parameters and splits an array into individual parameters.
var a = [1,2,3,4,5]
max = Math.max(...a)
// equivalent to Math.max.apply(null,a) or Math.max(1,2,3,4,5)
Spread can also be used in conjunction with other regular parameters.
Destructured Parameters
Similar to a destructured assignment (see above).
function setCookie( name, val, { secure, path, domain, expires })
Iterators and Generators
Iterators are objects that have a next()
method which returns a results object with two properties, value
and done
. Iterators offer a collection of values one at a time through successive calls to next()
returning the appropriate value or result with done==true
if no more values to read.
ES6 collection objects (like arrays, maps and sets) all have three default iterators. These are entries()
, values()
and keys()
and cover most common iteration needs.
var a=['one','two','three']
for(let e of a.entries()) console.log(e) // [0,'one'] [1,'two'] [2,'three']
for(let v of a.values()) console.log(v) // one two three
for(let k of a.keys()) console.log(k) // 0 1 2
for..of
ES6 also introduces a new syntax for dealing with iterators. The for-of
loop which is used for looping over iterable objects.
for(let i of ['one','two','three') console.log(i)
NOTE: for..of
and for..in
loops look similar, but are fundamentally different. for..in
is for inspecting object properties and looping over the object’s enumerable properties, whereas for..of
is for looping over iterators.
for..of
is also available in array comprehensions.
[ x for (x of a) if (x>5) ]
Generators
A generator is a special kind of function that returns an iterator by inserting a *
after the function
keyword. A new yield
keyword is used inside of generators to specify values that iterator should return when next()
is called.
Generators stop execution after each yield
until next()
is called again, which is a powerful new paradigm (eg for managing asynchronous processes).
function* gen(){
yield 1;
yield 2;
yield 3;
}
let it = gen();
for(let i of it) console.log(i); // outputs 1 2 3
let it2 = gen();
it2.next() // { value:0, done:false }
it2.next() // { value:1, done:false }
it2.next() // { value:2, done:false }
it2.next() // { value:undefined, done:true }
Generators also let us write lazy versions of many functions (eg. filter, map, reduce…) which are very efficient and elegant.
Collections
ES6 introduces some new collection structures.
Sets
Sets are simple data structures similar to arrays where each value is unique.
let s = new Set([1,2,3]);
s.has(4); // false
s.add(4); // [1,2,3,4]
s.add(2); // still [1,2,3,4]
s.delete(3); // [1,2,4]
Maps
Maps are similar to JavaScript object key-value pairs except the key can be any JavaScript data type, not just strings.
m = new Map();
o = {x:'blah'};
m.set(o,'something to store');
m.has(o); // true
console.log(m.get(o));
Maps can also be used with iterators.
for (let [k, v] of m) console.log(k, v);
Symbols
Symbols generate unique inaccessible keys, useful in maps and class private members.
let a = Map();
{
let k = Symbol();
a.set(k, 'value');
// Here, we can get and reset 'value' as a.get(k).
}
// Here, a.get(k) is invalid, a.size is 1, but the key cannot be seen.
WeakMaps and WeakSets
WeakMap
helps developers avoid memory leaks by allowing the garbage collector to dispose objects only referenced by WeakMap. WeakMap keys must be objects and are not able to enumerate their keys.
WeakSet
only objects, which can be collected if there are no other references or mentions.
Template Strings
New String declaration syntax (using back-ticks “) that facilitates simple String literals with embedded expression evaluation.
var name = 'Paul', age = 99;
var s = `$(name), is apparently $(age) years old`
The syntax can also be used fro simple multi-line strings.
var longString = `here we have
a multiline string
using backtick quotes`;
Promises
Promises are a mechanism for handling results (and errors) from asynchronous operations. You can achieve the same thing with callbacks, but promises provide improved readability via method chaining and simple error handling. Promises are already used in many JavaScript libraries.
getJSON("/api/product/1").then( function(p) {
return getJSON(p.description);
}).catch(function(err) {
console.log('Oops..',err)
});
References