Modern JavaScript

Eirik Vullum

- Scandinavia Online

- Consulting / Training

eiriklv@github
eiriklv@twitter
http://vullum.io

ES6 / ES2015

Like before

good parts and bad parts

let / const

  • no more var!

  • block scoping

  • non-reassignment

let

// old style var
if (true) {
  var a = 5;
}

console.log(a)

// >> 5

// new let block scope
if (true) {
  let b = 5;
}

console.log(b)

// >> ReferenceError: b is not defined...

let

// old style var
for (var i = 0; i < 10; i++) {...}

console.log(i);
// >> 10

// new let block scope
for (let j = 0; j < 10; j++) {...}

console.log(j);
// >> ReferenceError: j is not defined..

const

// old var style
var a = 5;
a = 6;

console.log(a)
// >> 6;

// new const assignment
const b = 5;
b = 6;

console.log(b)
// >> 5;

// new const assignment (no reassignment)
const b = 5;
const b = 6;

// >> TypeError: Identifier 'b' has already been declared..

const

// no immutability beyond primitives..
const a = {};
a.name = 'John';

console.log(a);
// >> { name: 'John' }

Template literals

no more concatenation

Template literals

old ugly style



let greeting = 'Good morning ' + firstName + ' ' + lastName; 

Template literals

new nice style

Note: these are back-ticks - not quotes



let greeting = `Good morning ${firstName} ${lastName}`; 

Template literals

any js expression


let names = ['John', 'Frank', 'Bob', 'Alice'];
let greeting = `Good morning ${names.join(' ')}`;

// >> Good morning John Frank Bob Alice

Template literals

old multiline style

let title = 'My awesome site';
let markup =
        '<!doctype html>\n' +
        '<html>\n' +
        '<head>\n' +
        '    <meta charset="UTF-8">\n' +
        '    <title>' + title + '</title>\n' +
        '</head>\n' +
        '<body>\n' +
        '</body>\n' +
        '</html>\n';

Template literals

old multiline style

let title = 'My awesome site';
let markup = '\
        <!doctype html>\n\
        <html>\n\
        <head>\n\
            <meta charset="UTF-8">\n\
            <title>' + title + '</title>\n\
        </head>\n\
        <body>\n\
        </body>\n\
        </html>';

Template literals

new multiline style

let title = 'My awesome site';
let markup = `
        <!doctype html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>${title}</title>
        </head>
        <body>
        </body>
        </html>
`;

Arrow functions

Arrow functions



let square = function(a) { return a * a };
let square = (a) => { return a * a };
let square = (a) => a * a;
let square = a => a * a;

syntax sugar for anonymous functions

Arrow functions



let myFunc = () => {...}
let myFunc = function() {...}.bind(this)

implicit bind to the outer 'this'

Arrow functions

no more .bind(this), var self, etc.

function Dog(name) {
  this.name = name;

  this.bark = function() {
    let self = this;

    setTimeout(function() {
      setTimeout(function() {
        console.log(`${self.name} barks!`);
      }, 1000);
    }, 1000);
  };
}

let Fido = new Dog('Fido');
Fido.bark();

Arrow functions

no more .bind(this), var self, etc.

function Dog(name) {
  this.name = name;

  this.bark = function() {
    setTimeout(function() {
      setTimeout(function() {
        console.log(`${this.name} barks!`);
      }.bind(this), 1000);
    }.bind(this), 1000);
  };
}

let Fido = new Dog('Fido');
Fido.bark();

Arrow functions

no more .bind(this), var self, etc.

function Dog(name) {
  this.name = name;

  this.bark = () => {
    setTimeout(() => {
      setTimeout(() => {
        console.log(`${this.name} barks!`);
      }, 1000);
    }, 1000);
  };
}

let Fido = new Dog('Fido');
Fido.bark();

Destructuring

Destructuring

Avoid intermediate variables + nicer syntax

Destructuring

Objects (old style)

let person = {
  name: 'John Doe',
  age: 30
};

let name = person.name;
let age = person.age;
// name = 'John Doe', age = 30

Destructuring

Objects (new style)

let person = {
  name: 'John Doe',
  age: 30
};

let { name, age } = person;
// name = 'John Doe', age = 30

Destructuring

Objects (return multiple)

function createConfig() {
  return {
    host: 'localhost',
    port: 3000
  };
}

let { host, port } = createConfig();
// host = 'localhost', port = 3000

Destructuring

Arrays (old style)

let numbers = [10, 20];

let a = numbers[0];
let b = numbers[1];
// a = 10, b = 20

Destructuring

Arrays (new style)

let numbers = [10, 20];

let [a, b] = numbers;
// a = 10, b = 20

Destructuring

Arrays (return multiple)

function createSomething() {
  return [10, 20];
}

let [a, b] = createSomething();
// a = 10, b = 20;

Destructuring

Nested mix (arbitrarily deep)

let obj = {
    a: [{
        foo: 123,
        bar: 'abc'
    }, {}],
    b: true
};

let { a: [{foo: f}] } = obj;

// f = 123

Rest and Spread

...

Rest parameter



function concat(separator) {
  var params = Array.prototype.slice.call(arguments);
  return params.join(separator);
}

concat('_', 'a', 'b', 'c', 'd');
// >> 'a_b_c_d'

the old way

Rest parameter


function concat(separator, ...params) {
  return params.join(separator);
}

concat('_', 'a', 'b', 'c', 'd');
// 'a_b_c_d'

using ...

Spread operator


let numbers = [1, 2, 3, 4, 5];
let [head, ...tail] = numbers;
// head = 1;
// tail = [2, 3, 4, 5];

for destructuring

Spread operator


let odds = [1, 3];
let evens = [2, 4];
let numbers = [...odds, ...evens];
// numbers = [1, 3, 2, 4]

for merging arrays

Iteration

From for to forEach() to for-of

Iteration

the ES3 way

let person = {
  name: 'John',
  age: 30
};

for (let prop in person) {
  if (person.hasOwnProperty(prop)) {
    console.log(prop + ': ' + person[prop]);
  }
};

// name: John
// age: 30

Iteration

the ES5 way

let person = {
  name: 'John',
  age: 30
};

Object.keys(person).forEach(function(prop) {
  console.log(prop + ': ' + person[prop]);
})

// name: John
// age: 30

Iteration

the ES6 way

let person = {
  name: 'John',
  age: 30
};

for (let prop of person) {
  console.log(prop + ': ' + person[prop]);
}

// name: John
// age: 30

Default values

Default values

old way

function setup(host, port, done) {
  host = host || 'localhost';
  port = port || 3000;
  done = done || function() {};

  ....
}

Default values

new way

function setup(
  host = 'localhost', 
  port = 3000,
  done = function() {}
) {

  ....
}

Named parameters

Named parameters

old way

function setup(options) {
  var host = options.host || 'localhost';
  var port = options.port || 3000;
  var done = options.done || function() {}

  ...
}

Named parameters

function setup({ host, port, done }) {
  host = host || 'localhost';
  port = port || 3000;
  done = done || function() {};

  ....
}

Named parameters

with defaults

function setup({
  port = 3000,
  host = 'localhost',
  done = () => {}
}) {

  ....
}

Named parameters

with defaults (optional)

function setup({
  port = 3000,
  host = 'localhost',
  done = () => {}
} = {}) {

  ....
}

Object shorthands

less writing..

Object shorthands

let name = 'John';
let age = 30;

let person = {
  name: name,
  age: age
};

old way

Object shorthands

let name = 'John';
let age = 30;

let person = {
  name,
  age
};

new way

Object shorthands


let person = {
  name: 'John',
  greet: function() {
    console.log(`${this.name}: hello`)
  }
};

old way

Object shorthands


let person = {
  name: 'John',
  greet() {
    console.log(`${this.name}: hello`)
  }
};

old way

Some more good stuff

  • Generators

  • Modules

  • Classes (...)

  • Map / WeakMap

  • Set / WeakSet

Check out

http://es6katas.org/
http://exploringjs.com/es6/

Beyond ES2015

Even further

ES7

  • function bind

  • async / await

  • and more..

But can i use this stuff..?

Transpilation

Before


let numbers = [1, 2, 3, 4];
let [head, ...tail] = numbers;
let add = (x, y) => x + y;

After

var numbers = [1, 2, 3, 4];
var head = numbers[0];
var tail = numbers.slice(1);

var add = function add(x, y) {
  return x + y;
};

Usage example


# install babel globally
> npm install -g babel

# transpile from ES6+ to ES5
> babel script.js --out-file script-compiled.js

Start using it now!

(Everywhere)

Questions