Ecmascript 6 and Babel

github.com/guilhermesad/ES6-and-babel-training

gorgulhoguilherme@gmail.com

Course division

  • Introduction
  • ES6 and Babel
  • ES2016

Part 1

Introduction

  • What/Why ES6?
  • ES6 vs ES2016
  • Babel

What is ES6?

  • 6th release of ECMAScript
  • ECMAScript == Javascript
  • ECMA International
  • ES6 == ES2015 == Harmony

More about history:

http://benmccormick.org/2015/09/14/es5-es6-es2016-es-next-whats-going-on-with-javascript-versioning/

Versions

ES1 1997
ES2 1998
ES3 1999
ES4 Abandoned
ES5 2009
ES6 2015
ES7 2016

ES6 in the browser

ES6 in Node.js

Why ES6?

  • Easier Development
  • Easier Migrations
  • Future Proof

Easier Development

  • Writing code more efficiently
  • More readable code
  • JS now used on backend too
  • Using standard conventions

ES5

var link = function (n, color, url) {

    var n = n || 50;

    var color = color || 'red';

    var url = url || 'foo.co';

    ...

} 

ES6

var link = function (n=50, color='red', url='foo.co') {

}

ES5

var body = req.body,

    username = body.username,

    password = body.password;

ES6

var {username, password} = req.body;

Easier Migrations

  • Angular 2 and libraries are in ES6
  • React + Redux
  • Decoupling from Angular

Decoupling from Angular

  • ES6 Module System vs Angular Modules
  • Services and Controllers as Classes

Future proof

  • Libraries and Companies
  • Browsers
  • Developer Support

Who's using ES6 today

  • Angular 2.0
  • Ember
  • React
  • Facebook
  • Google
  • Netflix
  • ...

ES6 Native Browser Support

Developers Support

  • ES6 is what new developers will be (are) using
  • Avoid technical debt
  • ES6 is more fun to write
  • Improved tooling around ES6

ES6 vs ES2016

  • ES2016 == ES7
  • Features released as they are approved
  • Proposals
  • 4 stages for approval

TC39 open proposals:

http://github.com/tc39/ecma262

ES6

var [a, b, ...rest] = [1, 2, 3, 4];

console.log(rest) // 3,4

ES7

var {a, b, ...rest} = {a: 1, b: 2, c: 3, d: 4};

console.log(JSON.stringify(rest)) // {c: 3, d: 4}

Babel

  • Javascript compiler
  • Used as a build step
  • Transforms ES6/ES7 in ES5

in.js 

[1, 2, 3].map(n => n + 1);

out.js

[1, 2, 3].map(function(n) {

   return n + 1;

});

=>

Babel

also

  • Supports JSX
  • Built out of plugins
  • Used by facebook, mozilla, npm...

Part 2

ES6 and Babel

  • ES6 features
  • ES6 in JS frameworks
  • Babel
  • let
  • const
  • Destructuring
  • Default
  • Template strings
  • Rest parameters
  • Spread
  • Arrows
  • Enhanced object literals
  • Classes
  • Promises
  • for..of

ES6 features

  • Unicode
  • Module loaders
  • Map / Set
  • Proxies
  • Symbols
  • Subclassable Built-ins
  • Primitive types APIs
  • Binary and Octal Literals
  • Reflect API
  • Iterators / Generators

What we won't cover

You can learn them on:

https://babeljs.io/docs/learn-es2015/

let

// ES5
if (userCreated) {
  var successMsg = "Welcome!";
}
console.log(successMsg); // Welcome!

// ES6
if (userCreated) {
  let successMsg = "Welcome!";
}
console.log(successMsg); // ReferenceError: successMsg is not defined


// ES5
for (var i = 0; i < 10; i++) {
  console.log(i);
}
console.log(i); // Prints 9

// ES6
for (let i = 0; i < 10; i++) {
  console.log(i);
}
console.log(i); // ReferenceError: i is not defined

Block

Scope

Lexical

Environment

const

let x; // x === undefined
const z; // SyntaxError: const declarations must have an initializer

const y = 10;
y = 20; // SyntaxError: Assignment to constant variable

const o = {};
o.a = 10; // ok!

Read

Only

Used

whenever possible

let / const

  • Block-scoped
  • Keep code simple and readable
  • var still exists

Destructuring

let [x, y, z] = [1, 2, 3];

// is equivalent to...

let x = 1, y = 2, z = 3;



let [x, y] = [1, 2];
console.log(x, y); // 1, 2

[x, y] = [y, x]; // Swap the values of x and y
console.log(x, y); // 2, 1



let [x, [y]] = [1, [2]]; // Nested arrays
console.log(x, y); // 1, 2

Easily

access

arrays

elements

Destructuring

let { a: x, b: y } = { a: 1, b: 2 };
console.log(x, y); // 1, 2

let { a: x, b: { c: y } } = { a: 1, b: { c: 2 } }; // Nested objects
console.log(x, y); // 1, 2


// Using for multiple return values
function example() {
  return [1, 2, 3];
}

let [a, b, c] = example();
console.log(a, b, c); // 1, 2, 3

Easily

access

objects

elements

Default

// ES5
function multiply(x, y) {
  y = y || 1; // Default to 1 if falsy
  return x * y;
}

// ES6
function multiply(x, y = 1) {
  return x * y;
}

// Reference args to the left
(function example(x, y = x * 2) {
    console.log(x, y); // 2, 4
}(2));

Template strings

// Interpolate variable bindings
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?` // Hello Bob, how are you today?


// Multiline strings
`In ES5 this
 is
 not legal.`

Arrows

// ES5
function square(x) { 
  return x * x;
};


// ES6
const square = (x) => {
  return x * x;
};

// Simplifying
const square = (x) => (
  x * x;
);

// Simplifying even more
const square = x => x * x;

Less

Verbose

More

Readable

Arrows

const arr = [1, 2, 3];

// ES5
var squares = arr.map(function (x) { return x * x });

// ES6
let squares = arr.map(x => x * x);


// ES5
var reduxMiddleware = function(store) {
  return function(next) {
    return function(action) {
      return next(action)(store);
    }
  }
}

// ES6
let reduxMiddleware = store => next => action => {
  return next(action)(store);
};

Less

Verbose

More

Readable

Arrows

// ES5
function UiComponent() {
  var button = document.getElementById('myButton');
  var self = this;
  button.addEventListener('click', function() {
    console.log('CLICK');
    self.handleClick();
  });
}

// ES6
function UiComponent() {
  const button = document.getElementById('myButton');
  button.addEventListener('click', () => {
    console.log('CLICK');
    this.handleClick(); // lexical `this`
  });
}

// Also lexical
arguments
super
new.target

Lexical

this

No binding

needed

Exercise

function pow(base, exponent) {
  var result = 1;
  var exponent = exponent || 2;
  for (var i = 0; i < exponent; i++) {
    result = result * base;
  }
  return result;
}

function printPow(numbers) {
  var result = pow(numbers.base, numbers.exponent);
  console.log("result: " + result);
}

printPow({base: 2, exponent: 3}); // => 8

Use your new powers to transform the code below into ES6

Exercise

const pow = (base, exponent = 2) => {
  let result = 1;
  for (let i = 0; i < exponent; i++) {
    result = result * base;
  }
  return result;
};

const printPow = ({base, exponent}) => {
  const result = pow(base, exponent);
  console.log(`result: ${result}`);
};

printPow({base: 2, exponent: 3}); // => 8

Use your new powers to transform the code below into ES6

Answer

arrow

arrow

default

let

const

destructuring

template strings

Rest parameters

 

// ES5
function logEach() {
  var things = Array.prototype.slice.call(arguments);
  things.forEach(function (thing) {
    console.log(thing);
  });
}
logEach("a", "b", "c");


// ES6
function logEach(...things) {
  things.forEach(function (thing) {
    console.log(thing);
  });
}
logEach("a", "b", "c");

Spread

// ES5
function example(a, b, c) {
  console.log(a, b, c); // 1, 2, 3
}
var args = [1, 2, 3];
example.apply(null, args);


// ES6
function example(a, b, c) {
  console.log(a, b, c); // 1, 2, 3
}
let args = [1, 2, 3];
example(...args);


let parts = ["shoulder", "knees"];
let lyrics = ["head", ...parts, "toes"]; // ["head", "shoulder", "knees", "toes"]

Enhanced Object Literals

 

var obj = {

  // __proto__
  __proto__: theProtoObj,

  // Does not set internal prototype
  '__proto__': somethingElse,

  // Shorthand for ‘handler: handler’
  handler,

  // Methods
  toString() {
    // Super calls
    return "d " + super.toString();
  },

  // Computed (dynamic) property names
  [ "prop_" + (() => 42)() ]: 42
};

Closer to class declarations

Benefit

from OOP 

for..of

for (let value of [2,3,7]) {
  console.log(value);
}
// 2
// 3
// 7

for (let value of "foo") {
  console.log(value);
}
// "f"
// "o"
// "o"

for (let i in [2,3,7]) {
  console.log(i);
}
// 0
// 1
// 2

Iterate over any iterable

Iterables can be created

More powerfull than for..in

Exercise

function giveSuperPowers(hero) {
  var specialPowers = Array.prototype.slice.call(arguments, 1);

  for (var i = 0; i < specialPowers.length; i++) {
    console.log("Adding " + specialPowers[i] + "!");
  }

  return {
    name: hero.name,
    speed: hero.speed * 3,
    specialPowers: specialPowers,
    run: function() {
      console.log("Running!");
    }
  };
}

var luke = { name: "Luke", speed: 3 };
var superLuke = giveSuperPowers(luke, "XRay", "Invisibility", "Teleport");

Use the latest features you've learned to transform the code below into ES6

Exercise

const giveSuperPowers = ({name, speed}, ...specialPowers) => {
  for (let specialPower of specialPowers) {
    console.log(`Adding ${specialPower}!`);
  }

  return {
    name,
    speed: speed * 3,
    specialPowers,
    run() {
      console.log("Running!");
    }
  };
};

const luke = { name: "Luke", speed: 3 };
const superLuke = giveSuperPowers(luke, "XRay", "Invisibility", "Teleport");

Use the latest features you've learned to transform the code below into ES6

Answer

let

arrow

for..of

template strings

enhanced object literals

Rest parameters

const

Destructuring

Classes

class SkinnedMesh extends THREE.Mesh {

  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    //...
  }

  update(camera) {
    //...
    super.update();
  }

  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

const geometry = 6.7;
const materials = ['trace', 'config'];
skinnedMesh = new SkinnedMesh(geometry, materials);

Syntactic sugar over JS

prototype-based OO

Exercise

var ExampleComponent = React.createClass({

  render: function() { 
    return (
      <div onClick={this._handleClick}>
        Hello, world.
      </div>
    );
  },

  _handleClick: function() {
    console.log(this);
  }

});

Convert the following React component to an ES6 Class.

Use React.Component as your base class.

Exercise

class ExampleComponent extends React.Component {

  render() { 
    return (
      <div onClick={this._handleClick}>
        Hello, world.
      </div>
    );
  }

  _handleClick() {
    console.log(this);
  }

}

Convert the following React component to an ES6 Class.

Use React.Component as your base class.

Answer

Promises

// An immediately resolved promise
let p = Promise.resolve("foo"); 

// Can get it after the fact, unlike events
p.then((res) => console.log(res)); 

let p2 = new Promise(function(resolve, reject) {  
   setTimeout(() => resolve(5), 2000);
});

// Handler can't change promise, just value
p2.then((res) => {  
  res += 2;  
  console.log(res);
});

// Still gets 4
p2.then((res) => console.log(res));  

Async programming

Used by many JS libs

Modules

 

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5


// Import complete module:
//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

Modules

 

// There can be a single default export. For example, a function:

//------ myFunc.js ------
export default function () { ··· } // no semicolon!

//------ main1.js ------
import myFunc from 'myFunc';
myFunc();


// Or a class:

//------ MyClass.js ------
export default class { ··· } // no semicolon!

//------ main2.js ------
import MyClass from 'MyClass';
const inst = new MyClass();

Modules

  • Similarly to CommonJS: they have a compact syntax, a preference for single exports and support for cyclic dependencies.
  • Similarly to AMD: they have direct support for asynchronous loading and configurable module loading.

Exercise

var fruits = require("./fruits");
var blend = require("./blend");

var makeRedJuice = function() {
  return blend(fruits.strawberry, fruits.raspberry, fruits.plum);
};

module.exports = makeRedJuice;

Transform the following CommonJS snippet using ES6 modules

Exercise

import { strawberry, raspberry, plum } from "./fruits";
import blend from "./blend";

export default function() {
  return blend(strawberry, raspberry, plum);
};

Transform the following CommonJS snippet using ES6 modules

Answer

ES6 in JS frameworks

  • React
  • Redux
  • AngularJS
  • Backbone.js

ES6 in React

Modules

consts

Class and inheritance

constructor

super

destructuring

ES6 in Redux

Destructuring

Enhanced object literals

Enhanced object literals

Modules

ES6 in AngularJS

Template strings

Controllers as classes

Services in constructor

Let

ES6 in Backbone.js

Classes are everywhere!

Babel

in.js 

[1, 2, 3].map(n => n + 1);

out.js

[1, 2, 3].map(function(n) {

   return n + 1;

});

=>

Babel

Babel CLI

$ npm install --global babel-cli

Babel CLI: simple way to compile files from the command line

$ babel my-file.js

We can compile it like so:

$ babel example.js -o compiled.js

To output to a file:

Babel CLI

$ npm install --save-dev babel-cli

Babel CLI locally:

  • Different projects / Babel versions, same machine
  • Portable project and easier to setup
  {
    "name": "my-project",
    "version": "1.0.0",
    "scripts": {
      "build": "babel src -d lib"
    },
    "devDependencies": {
      "babel-cli": "^6.0.0"
    }
  }

package.json

Plugins and Presets

Configuring Babel:

  • Running babel alone just copy files around
  • plugins specify to Babel what to do
  • presets are groups of plugins
{
  "presets": [],
  "plugins": []
}

.babelrc

Plugins and Presets

$ npm install --save-dev babel-preset-es2015

babel-preset-es2015

Gathers plugins for all ES2015 transformations

(compiles ES6 to ES5)

Install

  {
    "presets": [
      "es2015"
    ],
    "plugins": []
  }

.babelrc

Plugins and Presets

$ npm install --save-dev babel-preset-react

babel-preset-react

Gathers plugins for all React transformations

Install

  {
    "presets": [
      "react"
    ],
    "plugins": []
  }

.babelrc

Plugins and Presets

$ npm install --save-dev babel-preset-stage-2

babel-preset-stage-x

  • Follows the JS stages standard through the TC39's process
  • Each stage (0-4) requires all its following stages

Install (example)

  {
    "presets": [
      "stage-2"
    ],
    "plugins": []
  }

.babelrc

Babel with Gulp

$ npm install --save-dev gulp-babel babel-preset-es2015

Install

const gulp = require('gulp');
const babel = require('gulp-babel');
 
gulp.task('default', () => {
  return gulp.src('src/app.js')
    .pipe(babel({
      presets: ['es2015']
    }))
    .pipe(gulp.dest('dist'));
});

Usage

$ npm install --save-dev grunt-babel babel-preset-es2015

Install

require('load-grunt-tasks')(grunt); // npm install --save-dev load-grunt-tasks

grunt.initConfig({
  babel: {
    options: {
      sourceMap: true,
      presets: ['es2015']
    },
    dist: {
      files: {
        'dist/app.js': 'src/app.js'
      }
    }
  }
});

grunt.registerTask('default', ['babel']);

Usage

Babel with Grunt

Babel with Webpack

npm install babel-loader babel-core babel-preset-es2015 --save-dev

Install

module: {
  loaders: [
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel', // 'babel-loader' is also a legal name to reference
      query: {
        presets: ['es2015']
      }
    }
  ]
}

Usage

Part 3

ES2016

  • ES2016 features
  • ES2016 in real world
  • Array.prototype.includes
  • Exponentiation operator
  • Rest / Spread in objects
  • Decorators

ES2016 features

  • Async / Await functions
  • Class static properties
  • Instance fields

Array.prototype.includes

['a', 'b', 'c'].includes('c');
// <- true

[{}, {}].includes({}); // strictly a reference comparison
// <- false

var a = {};
[{}, a].includes(a);
// <- true

// Using fromIndex param
['a', 'b', 'c', 'd'].includes('b', 2);
// <- false

Exercise

const a1 = ['a', 'b', 'c'];
const a2 = ['d', 'y', 'a', 't', 'c', 'c'];

whichIncluded(a1, a2); // => [false, false, true, false, true, true];

Write a function that takes two arrays and returns an array of booleans telling which elements of the second array are included in the first one. Example:

Exercise

const whichIncluded = (a1, a2) => a2.map(v => a1.includes(v));

Write a function that takes two arrays and returns an array of booleans telling which elements of the second array are included in the first one.

Answer

Exponentiation operator

2 ** 3
// <- 8

1 ** 2 === Math.pow(1, 2)
// <- true

let a = 2;
a **= 3; // equivalent to a = Math.pow(a, 3)
console.log(a);
// <- 8

Rest/Spread in objects

// Rest
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1 
y; // 2 
z; // { a: 3, b: 4 }

// Spread
let n = { x, y, ...z };
n; // { x: 1, y: 2, a: 3, b: 4 }

Exercise

sumObj({ v1: 1, v2: 2, v3: 3 }); // => 6

Write a function that takes an object and returns the sum of the squares of its properties named base1 and base2 plus the sum of all its other elements.

You can use the following function, which returns the sum of all the object's values

specialSum({ base1: 3, base2: 3, value1: 7 }); // => 25
specialSum({ base1: 2, base2: 3, value1: 3, value2: 4 }); // => 20

Exercise

Write a function that takes an object and returns the sum of the squares of its properties named base1 and base2 plus the sum of all its other elements.

const specialSum = ({base1, base2, ...rest}) => {
  return base1 ** 2 + base2 ** 2 + sumObj(rest);
}

Solution

Decorators

class Cat {
  meow() { return `${this.name} says Meow!`; }
}

Evaluating this class results in installing the meow function onto `Cat.prototype`, roughly like this:

Object.defineProperty(Cat.prototype, 'meow', {
  value: specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
});

Imagine we want to mark a property or method name as not being writable.

function readonly(target, key, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

Decorators

We can then add it to our meow property as follows:

class Cat {
  @readonly
  meow() { return `${this.name} says Meow!`; }
}

Now, before installing the descriptor onto `Cat.prototype`, the engine first invokes the decorator:

let descriptor = {
  value: specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
});

// The decorator has the same signature as Object.defineProperty and has an
// opportunity to intercede before the relevant defineProperty actually occurs
descriptor = readonly(Cat.prototype, 'meow', descriptor) || descriptor;
Object.defineProperty(Cat.prototype, 'meow', descriptor);

Decorators

function readonly(target, name, descriptor){
  descriptor.writable = false;
  return descriptor;
}

function deprecate(target, name){
  console.warn(`${name} is deprecated`);
  return false;
}

class Person {
  @readonly   
  name() { return `${this.first} ${this.last}` }

  @deprecate
  age() { return `${this.age}` }
}

Extend function behavior

Keep function internals

Decorators

function superhero(target) {
  target.isSuperhero = true;
  target.power = 'flight';
}

@superhero
class MySuperHero() {}

console.log(MySuperHero.isSuperHero); // true

Extend class behavior

Keep class internals

Decorators

  • Higher-order functions
  • github.com/jayphelps/core-decorators.js
  • Used in many JS frameworks

Learn more:

https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.ootqplafi

Exercise

class Pikachu {
  useTackle() { console.log('Pikachu used tackle!'); }

  @superEffective
  useThunderbolt() { console.log('Pikachu used thunderbolt!'); }
}

myPikachu = new Pikachu();

myPikachu.useTackle();
// Pikachu used tackle!

myPikachu.useThunderbolt();
// Pikachu used thunderbolt!
// ...and it's super effective!!

Create a decorator function that behaves like so:

Exercise

function superEffective(target, name, descriptor){
  const func = descriptor.value;
  descriptor.value = function() {
    func();
    console.log("...and it's super effective!!");
  };
  return descriptor;
}

Solution

Create a decorator function that behaves like so:

// ES6
function doAsyncOp () {
  return asynchronousOperation().then(function(val) {
    console.log(val);
    return val;
  });
};

// ES7
async function doAsyncOp () {
  let val = await asynchronousOperation();
  console.log(val);
  return val;
};

console.log('before')
doAsyncOp();
console.log('after');

// Suppose asynchronousOperation is a setTimeout:
// "before"
// "after"
// val

Async / Await functions

async function getUser () {
  console.log("Request initiated...");
  const user = await http.get("/user");
  console.log("Request complete!");
  return user;
};

async function getUserAndPrintName() {
  const user = await getUser();
  console.log(user.name);
};

getUserAndPrintName();
console.log("Do some other thing");

// Request initiated...
// Do some other thing
// Request complete!
// User name: Amir

Async / Await functions

Async / Await functions

// ES6
function loadStory() {
  return getJSON('story.json').then(function(story) {
    addHtmlToPage(story.heading);

    return story.chapterURLs.map(getJSON)
      .reduce(function(chain, chapterPromise) {
        return chain.then(function() {
          return chapterPromise;
        }).then(function(chapter) {
          addHtmlToPage(chapter.html);
        });
      }, Promise.resolve());
  }).then(function() {
    addTextToPage("All done");
  }).catch(function(err) {
    addTextToPage("Argh, broken: " + err.message);
  }).then(function() {
    document.qSelector('.spinner').style.display = 'none';
  });
}
// ES7
async function loadStory() {
  try {
    let story = await getJSON('story.json');
    addHtmlToPage(story.heading);
    for (let chapter of story.chapterURLs.map(getJSON)) {
      addHtmlToPage((await chapter).html);
    }
    addTextToPage("All done");
  } catch (err) {
    addTextToPage("Argh, broken: " + err.message);
  }
  document.qSelector('.spinner').style.display = 'none';
}

Class static properties

class MyClass {
  static myStaticProp = 42;

  constructor() {
    console.log(MyClass.myStaticProp); // Prints '42'
  }
}

Instance fields

class MyClass {
  myProp = 42;

  constructor() {
    console.log(this.myProp); // Prints '42'
  }
}

Exercise

async function getUser () {
  console.log("Request initiated...");
  const user = await http.get("/user");
  console.log("Request complete!");
  return user;
};

async function getUserAndPrintName() {
  const user = await getUser();
  console.log(user.name);
};

getUserAndPrintName();
console.log("Do some other thing");

// Request initiated...
// Do some other thing
// Request complete!
// User name: Amir

Be a human compiler and transform the code below we've seen into ES6

Exercise

function getUser () {
  console.log("Request initiated...");
  return http.get("/user").then(function(user) {
    console.log("Request complete!");
    return response;
  });
}

function getUserAndPrintName() {
  getUser().then(function(user) {
    console.log(user.name);
  });
};

getUserAndPrintName();
console.log("Do some other thing");

Solution

Be a human compiler and transform the code below we've seen into ES6

ES2016 in real world

Decorators

Object spreads

Class static properties

Instance fields

Instance fields

$ npm install --save-dev babel-preset-stage-0

Compiling with Babel

Babel stages presets (babel-preset-stage-x) are used to compile features still in process of being accepted in the next standard (ES2016) 

Install (example)

  {
    "presets": [
      "stage-0"
    ],
    "plugins": []
  }

.babelrc

ES2016 in real world

Learn more

  • exploringjs.com
  • leanpub.com/understandinges6
  • 2ality.com
  • babeljs.io/docs/learn-es2015

gorgulhoguilherme@gmail.com

github.com/guilhermesad

Made with Slides.com