github.com/guilhermesad/ES6-and-babel-training
gorgulhoguilherme@gmail.com
More about history:
http://benmccormick.org/2015/09/14/es5-es6-es2016-es-next-whats-going-on-with-javascript-versioning/
ES1 | 1997 |
ES2 | 1998 |
ES3 | 1999 |
ES4 | Abandoned |
ES5 | 2009 |
ES6 | 2015 |
ES7 | 2016 |
var link = function (n, color, url) {
var n = n || 50;
var color = color || 'red';
var url = url || 'foo.co';
...
}
var link = function (n=50, color='red', url='foo.co') {
}
var body = req.body,
username = body.username,
password = body.password;
var {username, password} = req.body;
TC39 open proposals:
http://github.com/tc39/ecma262
var [a, b, ...rest] = [1, 2, 3, 4];
console.log(rest) // 3,4
var {a, b, ...rest} = {a: 1, b: 2, c: 3, d: 4};
console.log(JSON.stringify(rest)) // {c: 3, d: 4}
[1, 2, 3].map(n => n + 1);
[1, 2, 3].map(function(n) {
return n + 1;
});
You can learn them on:
https://babeljs.io/docs/learn-es2015/
// 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
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 [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
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
// 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));
// 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.`
// 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
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
// 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
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
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
// 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");
// 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"]
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 (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
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
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
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
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.
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
// 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
//------ 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
// 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();
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
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
Modules
consts
Class and inheritance
constructor
super
destructuring
Destructuring
Enhanced object literals
Enhanced object literals
Modules
Template strings
Controllers as classes
Services in constructor
Let
Classes are everywhere!
[1, 2, 3].map(n => n + 1);
[1, 2, 3].map(function(n) {
return n + 1;
});
$ 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:
$ npm install --save-dev babel-cli
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"build": "babel src -d lib"
},
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
package.json
{
"presets": [],
"plugins": []
}
.babelrc
$ npm install --save-dev babel-preset-es2015
Gathers plugins for all ES2015 transformations
(compiles ES6 to ES5)
Install
{
"presets": [
"es2015"
],
"plugins": []
}
.babelrc
$ npm install --save-dev babel-preset-react
Gathers plugins for all React transformations
Install
{
"presets": [
"react"
],
"plugins": []
}
.babelrc
$ npm install --save-dev babel-preset-stage-2
Install (example)
{
"presets": [
"stage-2"
],
"plugins": []
}
.babelrc
$ 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
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
['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
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:
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
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
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 }
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
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
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;
}
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);
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
function superhero(target) {
target.isSuperhero = true;
target.power = 'flight';
}
@superhero
class MySuperHero() {}
console.log(MySuperHero.isSuperHero); // true
Extend class behavior
Keep class internals
Learn more:
https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.ootqplafi
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:
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 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
// 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 MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // Prints '42'
}
}
class MyClass {
myProp = 42;
constructor() {
console.log(this.myProp); // Prints '42'
}
}
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
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
Decorators
Object spreads
Class static properties
Instance fields
Instance fields
$ npm install --save-dev babel-preset-stage-0
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