- ECMAScript 6 — New Features: Overview & Comparison
- Standard ECMA-262
- ECMAScript 2015 (ES6) in Node.js
- ECMAScript 2015 (ES6) compatibility table
Support for constants (also known as "immutable variables"), i.e. variables which cannot be re-assigned new content.
// Base const declaration
const CHUNK_SIZE = 1024;
// Try declarete one const twice
const MAX_SIZE = 10;
const MAX_SIZE = 20; // SyntaxError: Identifier 'MAX_SIZE' has already been declared
// Try rewrite const
const RETRY_COUNT = 10;
RETRY_COUNT = 100; // TypeError: Assignment to constant variable.
// Important: rewrite const as object
const USER_SETTINGS = { size: 1024 };
USER_SETTINGS.size = 1;
console.log(USER_SETTINGS); // { size: 1 }
- Remember constants created using
const
keyword haveblock
level scope!
Creating a constant object in which new properties could not be added, modified or removed.
// Freeze const as object
const USER_SETTINGS = { size: 1024 };
Object.freeze(USER_SETTINGS);
USER_SETTINGS.size = 1; // TypeError: Cannot assign to read only property 'size' of #<Object>
// Important: not freeze recursive
const REFERENCE_OBJECT = { a: { b: true } };
Object.freeze(REFERENCE_OBJECT);
REFERENCE_OBJECT.a.c = false;
console.log(REFERENCE_OBJECT); // { a: { b: true, c: false } }
- Note that
values that are objects can still be modified
, unless they are also frozen.
Block-scoped variables without hoisting
.
// ES6
for (let i = 0; i < 10; i++) {
let user = { index: i };
}
console.log(user); // ReferenceError: user is not defined
// ES5
for (var i = 0; i < 10; i++) {
var user = { index: i };
}
console.log(user); // { index : 9 }
// ES6
if (true) {
let count = 1;
}
console.log(count); // ReferenceError: count is not defined
// ES5
if (true) {
var count = 1;
}
console.log(count); // 1
Block-scoped function definitions.
function getUser() { return 'USER A'; }
console.log(getUser()); // 'USER A'
if (true) {
function getUser() { return 'USER B'; }
console.log(getUser()); // 'USER B'
}
console.log(getUser()); // 'USER A'
Notes:
- In Node.js supports in
strict mode
only [reference]
var users = ['Ann', 'Valentino', 'Scott'];
// ES5
var namesLength = users.map(function(user) { return user.length });
// ES6
let namesLength = users.map( user => user.length );
// ES6
let getTimestamp = () => Date.now();
// ES5
var getTimestamp = function() {
return Date.now();
}
// ES6
let getNext = index => index + 1;
// ES6
let getNext = (index) => index + 1;
// ES5
var getNext = function(index) {
return index + 1;
}
// ES6
let getSum = (num1, num2) => num1 + num2;
// ES6
let getSum = (num1, num2) => { return num1 + num2 };
// ES5
var getSum = function(num1, num2) {
return num1 + num2;
}
// ES5
var getFirstArg = function() { return arguments[0]; }
// ES6, Not works in Node 4.2
let getFirstArg = (...args) => args[0];
// ES6
let getUser = id => ({ id: id, app: 'ProTools' });
// ES5
var getUser = function(id) {
return { id: id, app: 'ProTools' };
}
// ES6
let delay = callback => {
setTimeout(callback, 200);
};
// ES5
var delay = function(callback) {
setTimeout(callback, 200);
};
// ES5 (with error)
function Counter() {
this._count = 0;
setInterval(function() {
console.log('Iteration:', ++this._count); // Iteration: NaN, NaN...
}, 1000);
}
var counter = new Counter();
// ES5 (with fix by `self` variable)
function Counter() {
var self = this;
self._count = 0;
setInterval(function() {
console.log('Iteration:', ++this._count); // Iteration: 1, 2, 3...
}, 1000);
}
var counter = new Counter();
// ES5 (with fix by `bind`)
function Counter() {
this._count = 0;
setInterval(function() {
console.log('Iteration:', ++this._count); // Iteration: 1, 2, 3...
}.bind(this), 1000);
}
var counter = new Counter();
// ES6
function Counter() {
this._count = 0;
setInterval(() => {
console.log('Iteration:', ++this._count); // Iteration: 1, 2, 3...
}, 1000);
}
let counter = new Counter();
function() { return 10; }
() => { return 10; }
() => 10
function(a) { return a * 5; }
(a) => { return a * 5; }
(a) => a * 5
a => a * 5
function(a, b) { return a * b; }
(a, b) => { return a * b; }
(a, b) => a * b
function() { return arguments[0]; }
(...args) => args[0]
() => {} // undefined
() => ({}) // {}
- Arrow functions capture the
this
value of the enclosing context
Simple and intuitive default values for function parameters.
// ES6, Not works in Node 4.2
function createUser(name, age = 18, isExisted = true) {
return {
name,
age,
isExisted
};
}
// ES5
function createUser(name, age, isExisted) {
return {
name: name,
age: age || 18,
isExisted: isExisted === undefined ? true : isExisted
};
}
// ES6
function add(value, cache = []) {
cache.push(value);
return cache;
}
add(1); // [1]
add(2); // [2]
// Not works in Node 4.2
function createAsset() {
return { alias: 'default' };
}
function createUploader(path, asset = createAsset()) {
...
}
let uploader = createUploader('/tmp/music.mp3');
// Not works in Node 4.2
function getMessage(userId, userName = 'User:' + userId) {
...
}
Intuitive expression interpolation for single-line and multi-line strings.
// ES6
let customer = { name: 'John' };
let message = `Hello, ${customer.name}! How are you?`;
console.log(message); // Hello, John! How are you?
// ES6
let car = {
vendor: 'BMW',
model: 'M4',
power: 300
};
let text = `New car ${car.vendor} ${car.model}
with engine ${car.power} hp`;
console.log(text);
// New car BMW M4
// with engine 300 hp
// ES5
parseInt('10000000000', 2) === 1024; // true
parseInt('2000', 8) === 1024; // true
// ES6
0b10000000000 === 1024; // true
0o2000 === 1024; // true
let target = { a: 100 };
let first = { a: 1, b: { c: true } };
let second = { b: { c: false, d: 'hello' } };
// target <- first <- second
console.log(Object.assign(target, first, second)); // { a: 1, b: { c: false, d: 'hello' } }
console.log(target); // { a: 1, b: { c: false, d: 'hello' } }
let first = { a: 1, b: { c: true } };
let clone = Object.assign({}, first); // { a: 1, b: { c: true } }
console.log(Object.is({ b: 1 }, { b: 1 })); // false
console.log({ b: 1 } === { b: 1 }); // false
console.log(Object.is(+0, -0)); // false
console.log(+0 === -0); // true
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false
let arr = [1, 2, 3];
for (let value of arr) {
console.log(value);
}
// 1
// 2
// 3
let str = 'Hello';
for (let value of str) {
console.log(value);
}
// H
// e
// l
// l
// o
let obj = { a: 1, b: 2, c: 3 };
for (let value of Object.keys(obj)) {
console.log(value);
}
// a
// b
// c
// Without `new` as primitive
let sym = Symbol();
console.log(typeof sym); // symbol
console.log(Symbol('id') == Symbol('id')); // false
let id = Symbol.for('id');
console.log(Symbol.for('id') === id); // true
console.log(Symbol.keyFor(id)); // 'id'
let revision = Symbol('revision');
let privateId = Symbol('privateId');
let globalZone = Symbol.for('zone');
let document = {
id: '123',
[revision]: 10,
[privateId]: '123abc',
[globalZone]: 'US'
};
console.log(document); // { id: '123' }
console.log(JSON.stringify(document)); // '{"id":"123"}'
console.log(document[revision]); // 10
console.log(document[privateId]); // '123abc'
console.log(document[Symbol.for('zone')]); // 'US'
class Edge {
constructor(source, target) {
this.source = source;
this.target = target;
}
}
let edge = new Edge('A', 'B');
// Class declarations are not hoisted!
let edge = new Edge(); // ReferenceError: Edge is not defined
class Edge {}
// Unnamed
let Edge = class {
constructor(source, target) {
this.source = source;
this.target = target;
}
}
// Named
let Edge = class Edge {
constructor(source, target) {
this.source = source;
this.target = target;
}
}
class Edge {
constructor(source, target) {
this.source = source;
this.target = target;
}
get title() {
return this.getTitle();
}
getTitle() {
return this.source + '->' + this.target;
}
}
let edge = new Edge('A', 'B');
console.log(edge.title); // A->B
class Edge {
constructor(source, target) {
this.source = source;
this.target = target;
}
static hasCommonVertex(edge1, edge2) {
return edge1.source === edge2.source || edge1.target === edge2.target;
}
}
let edge1 = new Edge('A', 'B');
let edge2 = new Edge('A', 'C');
console.log(Edge.hasCommonVertex(edge1, edge2)); // true
class BaseEdge {
constructor(source, target) {
this.source = source;
this.target = target;
}
isValid() {
return typeof this.source === 'string' && typeof this.target === 'string';
}
}
class Edge extends BaseEdge {
static hasCommonVertex(edge1, edge2) {
return edge1.source === edge2.source || edge1.target === edge2.target;
}
}
let edge1 = new Edge('A', 'B');
let edge2 = new Edge('A', 'C');
console.log(Edge.hasCommonVertex(edge1, edge2)); // true
console.log(edge1.isValid()); // true
class BaseEdge {
constructor(source, target) {
this.source = source;
this.target = target;
}
isValid() {
return typeof this.source === 'string' && typeof this.target === 'string';
}
}
class Edge extends BaseEdge {
isValid() {
return super.isValid() && this.source !== this.target;
}
}
let edge = new Edge('A', 'B');
console.log(edge.isValid()); // true
Class declarations
are not hoisted!
let store = new Map();
store.set('ABC', 'done');
store.set('123', { user: '123' });
console.log(store.size); // 2
console.log(store.has('123')); // true
console.log(store.get('123')); // { user: '123' }
store.delete('123');
console.log(store.has('123')); // false
console.log(store.get('123')); // undefined
console.log(store.size); // 1
store.clear();
console.log(store.size); // 0
let store = new Map();
store.set(1, 'one');
store.set(2, 'two');
store.set(3, 'three');
for (let key of store.keys()) {
console.log(key);
}
// 1
// 2
// 3
for (let value of store.values()) {
console.log(value);
}
// 'one'
// 'two'
// 'three'
// Not works in Node 4.2
for (let [key, value] of store.entries()) {
console.log(key + ' => ' + value);
}
// Not works in Node 4.2
for (let [key, value] of store) {
console.log(key + ' => ' + value);
}
// 1 => one
// 2 => two
// 3 => three
let store = new Map();
store.set(1, 'one');
store.set(2, 'two');
store.set(3, 'three');
store.forEach(function(value, key) {
console.log(key + ' => ' + value);
}, store);
// 1 => one
// 2 => two
// 3 => three
let sourceData = [[1, 'one'], [2, 'two'], [3, 'three']];
let store = new Map(sourceData);
store.forEach(function(value, key) {
console.log(key + ' => ' + value);
}, store);
// 1 => one
// 2 => two
// 3 => three
let set = new Set();
set.add('001');
set.add('002');
set.add('003');
set.add('003');
set.add('002');
set.add('001');
console.log(set.size); // 3
console.log(set.has('001')); // true
set.delete('001');
console.log(set.size); // 2
console.log(set.has('001')); // false
set.clear();
console.log(set.size); // 0
let activeJobs = [
{ title: 'create', id: '001' },
{ title: 'update', id: '002' },
{ title: 'delete', id: '003' }
];
let weakMap = new WeakMap();
weakMap[activeJobs[0]] = { user: 'User A' };
weakMap[activeJobs[1]] = { user: 'User B' };
weakMap[activeJobs[2]] = { user: 'User A' };
console.log(weakMap[activeJobs[0]]); // { user: 'User A' }
activeJobs.splice(0, 1); // should return 2 records in weakMap
activeJobs.splice(0, 1); // should return 1 record in weakMap
let promise = new Promise(function(resolve, reject) {
...
});
// onFulfilled - on resolve
// onRejected - on reject
promise.then(onFulfilled, onRejected);
// or
promise
.then(onFulfilled)
.catch(onRejected);
let checker = new Promise((resolve, reject) => {
throw new Error('Not found!');
});
checker.catch(err => console.log(err)); // [Error: Not found!]
let timer = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Date.now());
}, 1000);
});
timer.then(
result => console.log('Success:', result), // 'Success: 1453944667280'
error => console.log('Error:', error)
);
let timer = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Critical error!'));
}, 1000);
});
timer.then(
result => console.log('Success:', result),
error => console.log('Error:', error) // Error: [Error: Critical error!]
);
function createDelay(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new Date().toISOString());
}, delay);
});
}
let timer = createDelay(1000);
timer.then(
result => console.log('Success:', result), // 'Success: 2016-01-28T01:35:56.541Z'
error => console.log('Error:', error)
);
function createDelay(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(delay + 100);
}, delay);
});
}
createDelay(1000)
.then(createDelay)
.then(createDelay)
.then(createDelay)
.then(result => console.log('Success:', result)); // 'Success: 1400'
createDelay(1000)
.then(createDelay)
.then(result => result + 500)
.then(createDelay)
.then(result => result + 500)
.then(createDelay)
.then(result => console.log('Success:', result)); // 'Success: 2400'
function createDelay(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(delay + 100);
}, delay);
});
}
Promise.all([
createDelay(1000),
createDelay(1200),
createDelay(1400)
]).then(results => console.log('Success:', results)); // 'Success: [ 1100, 1300, 1500 ]'
function createDelay(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(delay + 100);
}, delay);
});
}
Promise.race([
createDelay(1000),
createDelay(1200),
createDelay(1400)
]).then(result => console.log('Success:', result)); // 'Success: 1100'
function* generateSteps() {
yield { action: 'create' };
yield { action: 'check' };
return { action: 'send' };
}
let steps = generateSteps();
console.log(steps.next()); // { value: { action: 'create' }, done: false }
console.log(steps.next()); // { value: { action: 'check' }, done: false }
console.log(steps.next()); // { value: { action: 'send' }, done: true }
function* generateSteps() {
yield { action: 'create' };
yield { action: 'check' };
return { action: 'send' };
}
let steps = generateSteps();
for (let value of steps) {
console.log(value);
}
// { action: 'create' }
// { action: 'check' }
function* runner() {
let result = yield 'Antonio';
console.log(result);
}
let generator = runner();
let value = generator.next().value;
setTimeout(() => generator.next(`Hello, ${value}! Current time ${new Date().toISOString()}`), 1000);
function createDelay(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(delay + 100);
}, delay);
});
}
function* taskExample() {
let firstTime = yield createDelay(1000);
console.log(Date.now(), firstTime);
let secondTime = yield createDelay(firstTime);
console.log(Date.now(), secondTime);
return secondTime * 10;
}
function runner(generator, yieldValue) {
let next = generator.next(yieldValue);
if (!next.done) {
next.value.then(
result => runner(generator, result),
err => generator.throw(err)
);
} else {
console.log(next.value);
}
}
runner(taskExample());
// '1453947173485 1100'
// '1453947174592 1200'
// 12000