in production
@bardguyver
JavaScript in 2016
ES6/2015
browser support
Babel
New features
Updating ES5 library
ES7/2016 and beyond
Final thoughts
arrows
classes
enhanced object literals
template strings
destructuring
default + rest + spread
let + const
iterators + for..of
generators
unicode
modules
modules
module loaders
map + set
weakmap + weakset
proxies
symbols
subclassable built-ins
promises
library APIs
binary and octal literals
reflect api
let, const
destructuring
arrow functions
blocks
template strings
modules
def. parameters
concise methods
for of
classes
object.assign
promises
strings
bonus level
final exams
parameters
Image source: https://blog.formidable.com/using-react-is-a-business-decision-not-a-technology-choice-63c4641c5f7#.2crsz2ky1
(messaging app)
module.exports = leftpad;
function leftpad (str, len, ch) {
str = String(str);
var i = -1;
if (!ch && ch !== 0) ch = ' ';
len = len - str.length;
while (++i < len) {
str = ch + str;
}
return str;
}
JavaScript
1995
2009
ECMAScript
(liveScript)
1996
ECMA
jScript
ES2, ES3
98/99
ActionScript
VBScript
JavaScript
ES5
2015
ES6
TC39
ES3.1
ES.next
Harmony
ES4
ES2016
ES2017
ES7
ES8
One of the things the committee really focuses on, and this is something that's been really healthy, is primitives.
We're looking not necessarily to innovate. We're looking to find what people are really doing out there on the web. We're looking to enable them to build features themselves.
`let` and `const` are new block-scoped variable declaration statements.
// Hoisting 101
function hoistMe() {
var x = 31;
var x = 71;
if (true) {
console.log(x);
}
console.log(x);
}
// Hoisting 101
function hoistMe() {
var x = 31;
if (true) {
var x = 71;
console.log(x);
}
console.log(x);
}
// Hoisting 101
function hoistMe() {
var x = 31;
if (true) {
var x = 71;
console.log(x); // 71
}
console.log(x); // 71
}
// var, let and const are available in all sub-blocks
var varName = 'Angus';
let letName = 'Angus';
const constName = 'Angus';
if (true){
console.log(varName); // Angus
console.log(letName); // Angus
console.log(constName); // Angus
varName = 'Donna';
letName = 'Donna';
constName = 'Donna'; // ERROR!
}
console.log(varName); // Donna
console.log(letName); // Donna
console.log(constName); // Angus
// But only var is available outside of block scope
if (true){
var varName = 'Angus';
let letName = 'Angus';
const constName = 'Angus';
}
console.log(varName); // Angus
console.log(letName); // letName is not defined
console.log(constName); // constName is not defined
for (let i = 0; i > 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
for (let i = 0; i > 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
console.log(i); // ReferenceError: i is not defined
if (true){
let letName = 'fritz';
}
console.log(letName); // ReferenceError: i is not defined
// const with int value
const year = 2016;
year = 2017; // Error!
const person = {};
person.name = 'Ingrid';
person.age = 32;
person = { 'name' : 'Ingrid' }; // Error!
person = 'test'; // Error!
New default variable declaration statement
Use to indicate variable will change over time
Use to indicate legacy code
const is the new var.
But: Don't blindly refactor all var instances as this can create side effects. (hoisting)
Template strings allow you to mix strings, variables and functions.
// ES5
var person = {
name: 'Jon Snow',
status = 'dead'
}
console.log('I\'m pretty sure ' + person.name + ' is ' + person.status);
// ES6
const person = {
name: 'Jon Snow',
status = 'alive'
}
console.log(`I've read online that ${person.name} is ${person.status}`);
// No more newline characters
// ES5
console.log('string text line 1\n\ string text line 2');
// ES6
console.log(
`string text line 1
string text line 2`
);
Allows parameters to be given a default value.
// Basic example
function add(x=0, y=0) {
return x + y;
}
add(); // 0
add(1); // 1
add(2, 3); // 5
// ES5
var datingProfile = function(name, interests) {
if (name === undefined) {
name = 'John';
}
if (interests === undefined) {
interests = 'most forms of data processing';
}
return 'Hi, I\'m ' + name + ', and I like ' + interests;
};
// ES6
const datingProfile = function(name = 'Sal', interests = 'eels') {
return `Hi, I'm ${name}, and I like ${interests}`;
};
Remove boilerplate
Arrow functions are function shorthands
Arrow functions doesn't create a new 'this' context.
// ES5 function
const person = {
greet: function(name) {
return `hi, ${name}`;
}
}
// Arrow function
const person = {
greet: (name) => {
return `hi, ${name}`;
}
}
// Implicit return
const person = {
greet: (name) => `hi, ${name}`
}
// ES5 - Movie plot generator
const plots = [
'avenge dead wife',
'use skillset to eliminate threat to nation',
'save daughter'
];
const createMoviePitch = function(plot) {
console.log(`Liam Neeson has to ${plot}`);
};
// Make it rain!
plots.map( function(plot) {
return createMoviePitch(plot)
});
// ES6 - Movie plot generator
const plots = [
'avenge dead wife',
'use skillset to eliminate threat to nation',
'save daughter'
];
const createMoviePitch = function(plot) {
console.log(`Liam Neeson has to ${plot}`);
};
// Make it rain!
plots.map( (plot) => {
return createMoviePitch(plot)
});
// ES6 - Movie plot generator
const plots = [
'avenge dead wife',
'use skillset to eliminate threat to nation',
'save daughter'
];
const createMoviePitch = (plot) => {
console.log(`Liam Neeson has to ${plot}`);
};
// Make it rain!
plots.map( plot => createMoviePitch(plot));
// Passing arguments
() => { ... } // no argument
x => { ... } // one argument
(x, y) => { ... } // several arguments
// Function bodies
x => { return x * x } // block
x => x * x // expression, equivalent to previous line
// ES5
function sneeze() {
var self = this;
console.log('ahh.. ahh.. ahh..');
this.choo = function(){
console.log('CHOO!');
};
window.setTimeout( function() {
self.choo();
}, 1000);
}
// ES6
function sneeze() {
console.log('ahh.. ahh.. ahh..');
this.choo = () => console.log('CHOO!');
window.setTimeout( () => {
this.choo();
}, 1000);
}
Easily extract values from data stored in objects and Arrays
// ES5 object creation
const person = {
name: 'Tim Samson',
features: {
face: 'round',
arms: 'sort',
hair: false
}
}
// Imagine if you had to do this instead:
const person = {};
person.name = 'Tim Samson';
person.features = {};
person.features.face = 'round';
person.features.arms = 'sort';
person.features.hair = false;
// ES5 data extraction
const person = {
name: 'Tim Samson',
features: {
face: 'round',
arms: 'sort',
hair: false
}
}
// To get values out with ES5 you had to dig!
const name = person.name;
const face = person.features.face;
const arms = person.features.arms;
const hair = person.features.hair;
// ES6 data extraction
const person = {
name: 'Tim Samson',
features: {
face: 'round',
arms: 'sort',
hair: false
}
}
// To get values out with ES5 you had to dig!
const name = person.name;
const face = person.features.face;
const arms = person.features.arms;
const hair = person.features.hair;
// Not anymore!
const { name, features: { face, arms, hair } } = person;
// Useful when you only care about certain parameters
const menu = {
appetiser: 'flea soup'
mains: 'locust eggs'
dessert: 'goblin sorbet'
}
function orderDessert({dessert}){
console.log(`Can I please have a bucket of ${dessert}, please?`);
}
orderDessert(menu); // Can I please have a bucket of goblin sorbet, please?
// Provide a default value if property doesn't exist
const person = {
name: 'Draco',
age: 673
}
const { name = 'Ricardo', height = '180cm' } = person;
console.log(name); // Draco
console.log(age); // 673
console.log(height); // 180cm
// Variable unpacking - common react pattern
// ES5
var author = this.props.author;
var posts = this.props.posts;
var bio = this.props.bio;
// ES6
const { author, posts, bio } = this.props;
Modules bring module loading to native javaScript
/**
* CommonJS (Node)
*/
var _ = require('lodash');
_.join(['a', 'b', 'c'], '~'); // → 'a~b~c'
/**
* AMD (RequireJS)
*/
define(['lodash'] , function (_) {
return function () {
_.join(['a', 'b', 'c'], '~'); // → 'a~b~c'
};
});
/**
* ES6 modules
*/
import _ from 'lodash';
_.join(['a', 'b', 'c'], '~'); // → 'a~b~c'
// --- animalFarm.js --- //
export const napoleon = {
species = 'pig',
speak() => { console.log('Four legs good, two legs bad'); }
};
export const boxer = {
species = 'horse',
speak() => { console.log('I will work harder'); }
};
// --- main.js --- //
// Named imports
import { napoleon, snowball } from 'animalFarm';
napoleon.speak();
// Import all the things
import * as animalFarm from 'animalFarm';
animalFarm.boxer.speak();
// --- helpers.js --- //
// Default exports
var helpers = {
generateRandom: function() {
return Math.random();
},
sum: function(a, b) {
return a + b;
}
};
export default helpers;
// --- main.js --- //
import helpers from 'helpers';
the End of the IIFE
// ES5
(function () {
var secret = 'Soylent green is people!';
}());
console.log(secret); // Reference Error
// ES6
{
let secret = 'Bush faked the moon landing';
}
console.log(secret); // Reference Error
Object literals have been given more functionality with ES6.
// ES5
var person = {
name: 'Doyle',
poke: function() { console.log('Stop that!'); },
greet: function() { console.log('Ahoi-hoi!'); }
};
// ES6
var person = {
name: 'Doyle',
poke() { console.log('Stop that!'); },
greet() { console.log('Ahoi-hoi!'); }
};
// Computed property names (ES6)
const prefix = 'half';
const people = {
[prefix + 'man']: 'Tyrion',
[prefix + 'hand']: 'Qhorin',
[prefix + 'mast']: true
};
console.log(people.halfman); // → Tyrion
console.log(people.halfhand); // → Qhorin
console.log(people.halfmast); // → true
There's a few new ways of managing parameters in ES6:
const arguments = ['test', 'test2', 'okay', 2, {}, null];
// ES5
function printAllArguments1() {
for (var i=0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
printAllArguments1(); // → test, test2, okay, 2, {}, null
// ES6
function printAllArguments2(...things) {
things.forEach( thing => {
console.log(thing);
});
}
printAllArguments1(); // → test, test2, okay, 2, {}, null
var args = [0, 1, 2];
function myFunction(x, y, z) { }
// ES5
myFunction.apply(null, args);
// ES6
myFunction(...args);
// Or even
myFunction(...[1,2,3]);
A better apply
P
Promises are perhaps the most important new feature.
A promise is a representation of the eventual result of an async computation
Why are they needed?
// ES5
func1(function (value1) {
func2(value1, function (value2) {
func3(value2, function (value3) {
func4(value3, function (value4) {
// Do something with value 4
});
});
});
});
// ES6
func1(value1)
.then(func2)
.then(func3)
.then(func4, value4 => {
// Do something with value 4
});
// Promise creation
promise = new Promise(function(resolve, reject) {});
A promise is a special kind of javascript object which contains another object.
const promise = new Promise(
function (resolve, reject) {
// Do something async here!
setTimeout(() => resolve({name: 'Bård'}), 2000)
// Or, with ES5 syntax:
setTimeout( function(){
return resolve({name: 'Bård'})
}, 2000)
}
);
.then() is used to get the data stored in a promise
getName.then(function(user) {
// process result
console.log(user.name)
}, function(err) {
// handle error
console.log('error! error!')
});
Promises start out in pending state and are settled when they’re either fulfilled or rejected
.then() is the most important method
// Basic usage
.then(successFn, errorFn)
// Chaining
getSomeData()
.then(filterTheData)
.then(processTheData)
.then(displayTheData);
// Syntax
Promise.all(promiseArray).then(function(resultsArray) {
//...
});
// Example:
Promise.all([
queryUserToken(),
queryUserDetails()
]).then(function() {
renderProfiles();
});
keep your Promise chains flat with a trailing .catch() to properly handles errors
// Error catching
getSomeData()
.then(doSomething)
.then(doSomethingElse)
.catch(function(err) {
// handle all errors
});
P
works like jquery.extend
// Syntax
Object.assign(targetObj, source, source2, source3);
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const newObj = Object.assign(o1, o2, o3);
console.log(newObj); // { a: 1, b: 2, c: 3 }
function UIModal(userOptions) {
const DOM;
const defaults = {
modal: '.js-modal',
openBtn: '.js-modal-btn',
closeBtn: '.js-modal-close-btn',
overlay: '.js-modal-overlay',
showOverlay: true
};
// Combine defaults with passed in options
const settings = Object.assign(defaults, userOptions);
// ...
}
Like for-in, but returns object, not index.
// ES5
var sum1 = 0;
var arr1 = [1, 2, 3];
for (var index in arr1) {
sum += arr1[index];
}
// ES6
let sum2 = 0;
let arr2 = [1, 2, 3];
for (let value of arr2) {
sum += value;
}
// Strings
let iterable = "Jammie Dodgers";
for (let value of iterable) {
console.log(value);
}
// J
// a
// m
// m
// i
// e
//
// D
// o
// d
// g
// e
// r
// s
The string native has been extended with some very useful new features
// ES5
console.log('devSouthCoast'.indexOf('Coast') > -1);
// true
// ES6
console.log('ryanGoslin'.includes('Goblin'));
// false
// ES5
function repeat(string, count) {
var strings = [];
while(strings.length < count) {
strings.push(string);
}
return strings.join('');
}
repeat('chill', 3); // 'chillchillchill'
// ES6
'go'.repeat(3); // 'gogogo'
Simple sugar over the prototype-based OO pattern
// Basic syntax
class className {
// Constructor
constructor() {}
// Method
methodName(){}
// Super call
super.superMethod();
}
// ES5
function Person(name, age, species) {
this.name = name;
this.age = age;
this.species = species;
}
Person.prototype.greet = function () {
return 'Greetings, I am ' + this.name + '!';
};
var cindy = new Person('Cindy', 98, 'klingon');
console.log(cindy.greet()); // Greetings, I am Cindy!
// ES5
class Person {
constructor(name, age, species) {
this.name = name;
this.age = age;
this.species = species;
}
greet() {
return 'Greetings, I am ' + this.name + '!';
}
}
const cindy = new Person('Cindy', 98, 'klingon');
console.log(cindy.greet()); // Greetings, I am Cindy!
P
What's next!?
That's it!
// Example
['a', 'b', 'c'].includes('a'); // true
['a', 'b', 'c'].includes('d'); // false
// Almost the same Async
['a', 'b', 'c'].indexOf('a') >= 0
// But not quite
[NaN].includes(NaN) // true
[NaN].indexOf(NaN) // -1
// ES5
Math.pow(x, y);
// ES6
x ** y;
// Example
const twoTimesTwo = 2 ** 2; // 4
const fourTimesFourTimesFour = 4 ** 3; // 64
What else?
WebAssembly fills in the gaps that would be awkward to fill with JavaScript
Other upcoming ES features?
Write syncronous code, make it act asynchronous
// Async function
function getStarship() {
return fetch('URL').then( response => {
return response.json().name;
})
}
// Sync function
function printStarship() {
const starship = getStarship();
console.log(starship);
}
printStarship(); // undefined
// Async function
function getStarship() {
return fetch('URL').then( response => {
return response.json().name;
})
}
// Async await function
async function printStarship() {
const starship = await getStarship();
console.log(starship);
}
printStarship(); // Death Star
Async Await functions in exactly the same way as calling `.then()` on a promise
(but without requiring any callback function)
// Promise
function getDailyJoke() {
fetch('http://joke.org', {}).then(function(response) {
return response.body.joke;
});
}
// Async await
async function getDailyJoke() {
let response = await fetch('http://joke.org', {});
return response.body.joke;
}
Is ES6 worth the effort?
When uncertain, chances are you probably should default to ES5 and older syntax instead of adopting ES6 just because you can.
By this I don’t mean that using ES6 syntax is a bad idea – quite the opposite, see I’m writing an article about ES6!
My concern lies with the fact that when we adopt ES6 features we must do it because they’ll absolutely improve our code quality, and not just because of the "cool factor" – whatever that may be.
Don't get fatigued! Keep it simple!
There are only two kinds of languages: the ones people complain about and the ones nobody uses
- Bjarne Stroustrup
all material & slides: