ES6/ES2015
Crash Course
let / const
are the new var
let - Better scoping
function varTest() {
var x = 1;
if (true) {
// same variable!
var x = 2;
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
// different variable
let x = 2;
console.log(x); // 2
}
console.log(x); // 1
}
let - Better scoping
var a = b + 1;
var b = 2;
console.log(a);
// NaN
let a = b + 1;
let b = 2;
console.log(a);
// ReferenceError: b is not defined
const
Not reassignable variables
const a = 2;
a = 100;
// TypeError: Assignment to constant variable.
const
NOT Immutable!
const foo = {
bar: 2
};
foo.bar = 100;
console.log(foo);
// => { bar: 100 }
Arrow functions
Shorter way to write functions
// ES5 function declaration
function add(a, b) {
return a + b;
}
// ES6 arrow functions
const add = (a, b) => a + b;
Useful for small operations
array.filter(function(item) {
return item.value > 100;
})
.map(function(item) {
return item.value * item.value;
})
.reduce(function(res, value) {
return res + value;
});
// versus
array
.filter(item => item.value > 100)
.map(item => item.value * item.value)
.reduce((res, value) => res + value);
Arrow functions syntax
// Parens needed when 0 or several parameters
() => 2;
(a, b) => a + b;
// Parens optional with one parameter
f => f(2);
(f) => f(2);
// Implicit return or block statement with explicit return
() => 2
() => { const a = 2; return a; }
// Can't name a function, but can assign to a variable (stack traces :+1:)
function foo() {}
const foo = () => {}
const foo = () => {
a: 1,
b: 2
};
foo() // => ?
Arrow functions trap
const foo = () => {
a: 1,
b: 2
};
foo()
// => SyntaxError: Unexpected token :
const foo = () => ({
a: 1,
b: 2
});
Bound to parent `this`
function foo() {
this.bar = 1;
const fn = () => {
this.bar++;
};
fn();
console.log(this.bar);
// => 2
}
foo()
Other subtleties
- Calling with `.call`/`.apply` does not change `this`
- No `arguments` object
- Will throw an error when called with `new`
Spread operator
and
Rest parameters
Spread operator
// Passes multiple args to a function
fn(...[1, 2, 3, 'foo']) === fn(1, 2, 3, 'foo');
// Simpler than `apply`
fn.apply(this, [1, 2, 3, 'foo']);
// Concatenates arrays
[1, 2, ...[3, 4, 5], 6]
// => [1, 2, 3, 4, 5, 6]
// ES proposal: merge objects
const object = { a: 1, b: 2 };
{...object, a: 500, z: 26}
// => { a: 500, b: 2, z: 26 }
Rest parameters
Simpler `arguments`
foo(1, 2, 3, 4);
function foo(...args) {
// args === [1, 2, 3, 4]
}
function foo(head, ...tail) {
// head === 1
// tail === [2, 3, 4]
}
// rest parameter is always last
function foo(...allButLast, last) {} // No
Template Literals
No more string concatenation
let type = 'normal';
'This is a ' + type + ' string';
type = 'template literal';
`This is a ${type} string`;
// => This is a template literal string
// Expressions
`Number ${n + 1}`;
Multiline
console.log(`
Lorem ipsum dolor sit amet,
eum ea mucius causae reprimique,
quo iusto nominati id.
`);
//
// Lorem ipsum dolor sit amet,
// eum ea mucius causae reprimique,
// quo iusto nominati id.
//
As functions
function format(template, ...expressions) {
console.log(template);
console.log(expressions);
return {a: 1};
}
const food = 'Fish & Chips', n = 3;
format`Eat ${food} ${n} times a week`
// [ 'Eat ', ' ', ' times a week' ]
// [ 'Fish & Chips', 3 ]
// => {a: 1}
As functions
// SQL query template
const query = SQL`SELECT * FROM books`
if (params.name) {
query.append(SQL` WHERE name = ${params.name}`)
}
// GraphQL query template
const query = gql`
{
user(id: 5) {
firstName
lastName
}
}
`
Destructuring
Get field values from objects
const object = {
a: 1,
b: 2
};
const {a} = object;
console.log(a);
// => 1
// Equivalent to
const a = object.a;
// Multiple destructuration
const {a, b, c} = object;
console.log(a, b, c);
// => 1, 2, undefined
More in depth
// Rename fields - key AS name
const {a: variable} = object;
// Nesting
const {a: {b}} = {a: {b: 100}}
// === const b = 100;
// Will error as usual when accessing undefined values
const {c: {b}} = {a: {b: 100}} // BOOM
// Works for parameters too
const shoutUserName = ({name}) => name.toUpperCase();
const [a, b] = [1, 2];
// a = 1, b = 2;
// Skip elements
const [a, , b] = [1, 2, 3, 4, 5];
// a = 1, b = 3;
// With spread operator
const [a, ...rest] = [1, 2, 3, 4, 5];
// a = 1, rest = [2, 3, 4, 5];
// Swap values
[a, b] = [b, a]
Destructuring with arrays
Object literal syntax & computed property names
// Object literal syntax
const foo = 1, bar = 2;
const object = { foo, bar };
// Equivalent to
const object = { foo: foo, bar: bar };
// Also for functions
const object = {
bar() {
// do stuff
}
}
object.bar();
Object literal syntax
// Computed property names
const key = 'ok';
const object = {
[key]: 100;
};
// Equivalent to
const object = {};
object[key] = 100;
Computed property names
Iterators
Iterators
const iterator = ...;
iterator.next(); // {value: 1, done: false};
iterator.next(); // {value: 2, done: false};
iterator.next(); // {value: 3, done: false};
iterator.next(); // {done: true};
iterator.next(); // {done: true};
// ad vitam æternam...
Iterators
const array = [];
for (let i of iterator) {
array.push(i);
}
array
// [1, 2, 3]
[...iterator]
// [1, 2, 3]
for loops
for (let i = 0; i < a.length; i++) {} // Manual
for (let i of iterator) {} // On iterators
// On keys of an object / items of an array
for (let key in object) {}
for each (let value in object) {} // On values of an object
Create your own iterator?
const object = {
__iterator__() {
// ...
}
};
// What if someone overrides that field...?
Symbols
Symbols
const object = {
array: [3],
[Symbol.iterator]() {
let nextIndex = 0;
return {
next() {
if (nextIndex < this.array.length) {
return {value: array[nextIndex++], done: false}
}
return {done: true};
}
};
}
};
object.iterator // undefined
object[Symbol.iterator] // the iterator
for(let i of object) {
// ...
}
Symbols
const times2Symbol = Symbol('times2');
const object = {
n: 3,
[times2Symbol]() {
return this.n * 2;
}
};
object.times2Symbol; // undefined
object[times2Symbol](); // 6
Generators
Functions that return multiple values
function* generator() {
yield 1;
yield 2;
return 3;
}
const g = generator();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: true }
g.next(); // { value: undefined, done: true }
Functions that return multiple values
function* generator() {
yield 1;
console.log(100);
yield 2;
console.log(200);
}
for (let item of generator()) {
console.log(item);
}
// 1
// 100
// 2
// 200
Functions that return multiple values
function* generator() {
yield 1;
yield 2;
return 3;
}
[...generator()] // [1, 2]
Proxies
const object = {};
const handler = {};
const proxy = new Proxy(object, handler);
proxy.a = 2;
console.log(object);
// => {a: 2}
Proxies
const object = {
a: 50
};
const handler = {
get(target, key) {
if (target[key] === undefined) {
return 100;
}
return target[key];
}
};
const proxy = new Proxy(object, handler);
proxy.a // => 50
proxy.b // => 100
Proxies
const object = {
a: 50
};
const handler = {
set(target, key, value) {
if (value >= 0) {
target[key] = value;
return true;
}
return false;
}
};
const proxy = new Proxy(object, handler);
proxy.a = -5
proxy.a // => 50
Proxies
const array = [1, 2, 3, 4, 5];
const handler = {
get(target, index) {
index = parseInt(index);
if (index < 0) {
index = target.length + index;
}
return target[index];
}
};
const proxy = new Proxy(array, handler);
proxy[-1] // => 5
proxy[-2] // => 4
Proxies
Map / Set
WeakMap / WeakSet
const user = {
name: 'Some Name'
};
const userAgeMap = new Map()
userAgeMap.set(user, 20);
// => Map { { name: 'Some Name' } => 20 }
userAgeMap.get(user);
// => 20
[...userAgeMap]
// => [ [ { name: 'Some Name' }, 20 ] ]
Map
const user = {
name: 'Some Name'
};
const set = new Set();
set.add(user);
set.add(1);
set.add(user);
set.has(user);
// => true
[...set]
// => [ { name: 'Some Name' }, 1 ]
Set
var map = new WeakMap()
// Only references, no primitives
map.set(1, 2)
// TypeError: 1 is not an object!
// Items can be removed by the garbage collector
WeakMap / WeakSet
Mo mo modules!
Static dependencies
import foo from './foo.js';
import {bar, baz} from './foo.js'
import fs, {readFile, writeFile as wf} from 'fs';
// foo.js
export default function() {}
export const baz = 2;
export {
bar: 1,
bloo: 2
};
Module import
// a.js
export {
foo: 2
};
// b.js
// Wrong
import A from './a.js';
A.foo()
// Right
import {foo} from './a.js';
foo()
Module
// Always top-level
import fs from 'fs';
// No conditional
if (condition) {
import x from 'x'; // BOOM
}
// No variables (no loops)
import x from importName; // BOOM
Module constraints
And a lot more
Native Promise
// Create a new Promise from scratch
new Promise(function(resolve, reject) {
resolve('foo');
})
.then(function(value) { ... })
.catch(function(error) { ... });
// Resolve with value `foo`
Promise.resolve('foo');
// Reject with error `foo`
Promise.reject(new Error('foo'));
// Resolve when all promises have resolved
Promise.all([promises]);
// Resolve when the first promises resolves
Promise.race([promises]);
function foo(a=1) {}
(a=1) => a + 1;
// Complex stuff!
function foo({ a=1, b=2 } = {a: 3}) {
console.log(a, b);
}
foo() // 3, 2
foo({}) // 1, 2
foo({a: 100}) // 100, 2
Default parameter value
class Ship extends Mothership {
constructor() {
this.message = 'classes';
}
// No commas!
print() {
return `I don't like ${this.message}.`;
}
}
Classes in Java(Script)
Object.assign({}, {a: 1, b: 2}, {a: 100})
// => {a: 100, b: 2}
array.find((n) => n === 2);
array.findIndex((n) => n === 2);
// Iterators
array.values(); // Not in Node v6
array.keys();
array.entries();
New builtin methods
// Already in Node v6
array.includes(2) // === (array.indexOf(2) !== -1)
// Not in Node v6
2 ** 3 // === Math.pow(2, 3)
a **= 2 // === a = Math.pow(a, 2)
// That's it
ES7/ES2016
More reading
ES6 Crash Course
By Jeroen Engels
ES6 Crash Course
- 812