ES6

Same Mary with a different hat?

Const: The Great Pretender

// const: values cannot be changed once set

const queenLeadSinger = 'Freddy Mercury'

queenLeadSinger = 'Justin Bieber'
// throws error

const queenBand = {
  leadSinger: 'Freddy Mercury'
}

queenBand.leadSinger = 'Justin Bieber'
// will NOT throw an error

queenBand = 'The police'
// will throw an error

//const is blocked scoped
{
  // alternate reality block scoping
  const queenNewLeadSinger = 'Justin Bieber'
}
console.log(queenNewLeadSinger)
// undefined

let

// let: same properties as const, but the value can change

// let is also block scoped

// in coffeescript:

for filename in list
  do (filename) ->
    fs.readFile filename, (err, contents) ->
      compile filename, contents.toString()

// in ES5 use IIFE

// in ES6:

for (let filename of list) {
    fs.readFile(filename, (err, contents) =>
      compile(filename, contents.toString())
    )
}


// as a rule, start with const and go for let only on case of emergency





Enhanced Strings

const title = 'We will rock you'


title.startsWith('We')
// true

title.endsWith('you')
// true

title.includes('rock')
// true

title.includes('.')
// false

title.repeat(2); 
// We will rock youWe will rock you


// string templates
// everything inside ${} will be evaluated
// uses back ticks

const band = 'Queen'
const track = `Queen - ${title}`
// Queen - We will rock you

Enhanced Array

const members = ['Freddie Mercury', 'Brian May', 'Roger Taylor', 'John Deacon']

// Array.prototype.find

members.find(member => member.startsWith('B') )  // 'Brian May'
members.find(member => member === 'Justin Bieber')  // undefined

// Array.prototype.findIndex

members.findIndex(member => member === 'Brian May')  // 1
members.findIndex(member => member === 'Justin Bieber')  // -1

// Array.prototype.fill

(new Array(5)).fill(2)  // [2, 2, 2, 2, 2]
let numbers = [1, 2, 3, 4];
numbers.fill(1, 2, 3);
// 1,2,1,4

//  Array.from

const spans = document.querySelectorAll('span')
const names = Array.from(spans, span => span.contentText) 

// Array.prototype.(keys(), values(), entries())
//    - will return an iterator
//    - use Array.from or [...] to get the array

Array.from(members.entries())
// [ [0, 'Freddie Mercury'], [1, 'Brian May'], [2, 'Roger Taylor'], [3, 'John Deacon'] ] 


// Array.of - use this instead of new Array()

const sameMembers = Array.of('Freddie Mercury', 'Brian May', 'Roger Taylor', 'John Deacon')
// ['Freddie Mercury', 'Brian May', 'Roger Taylor', 'John Deacon']

Misc

New Math functions:

- Math.sign, trunc, log10, hypot, ...

 

New integer literals:

- 0b11, 0o10

 

 

Arrow functions


function inc(x) {
  return x + 1
}

const inc = x => x + 1

const sum = (a,b) => a + b

const myConst = () => 42

const logged_inc = x => {
  console.log('before inc: ', x)
  return x + 1
}

this.x = 42
setTimeout( ()=> console.log(this.x)
, 1000)
// 42

Do not forget hoisting

- var, function are hoisted

- let, const, class, arrow functions are NOT hoisted

 

Why it matters?

 

- temporal deadzones where we access before declaration

 

- we cannot start with what the function actually does

Enhanced Object

// Object.assign()

const x = {answer: -1}
const y = Object.assign({}, x, {question: '?', answer: 42}
console.log(x)
// {question: '?', answer: 42} 

// Shorthand assignment

const z= 42
const obj = {z}
// {z: 42}

// Computed properties

const id = 100
const obj2 = {
  [`row_${id}`]: 42
  [`method_${id}`](x) {
    return "Don't stop me know"
  }
}

//  New method definition

const obj3 = {
  log_the_truth() {
    console.log('We are the champions')
  }
}

Destructuring

// object destructuring

const obj = { first: 'Freddy', last: 'Mercury' };
const {first: f, last: l} = obj;
    // f = 'Freddy'; l = 'Mercury'

// {prop} is short for {prop: prop}
const {first, last} = obj;
    // first = 'Freddy'; last = 'Mercury'

// array destructing

const arr = [1, 2, 3, 4]
[x, , y] = arr
// 1, 4

// default parameters
function listen(band = 'Queen') {}

// named parameters : not as cool as python kwargs, but...

function selectEntries({ start=0, end=-1, step=1 } = {}) {}

selectEntries({ start: 10, end: 30, step: 2 });
selectEntries({ step: 3 });
selectEntries({});
selectEntries();

// WHY named parameters?

// from entry code:
saveDatapointChange = (datapoint, reason='')->
    AutoBlankManager.waitToFinishFor(datapoint)
      .then () -> alterDatapointValue datapoint, reason, false, false

Spread/Rest operator

// spread operator: Spread of elements of an iterable collection

Math.max(-1, 5, 11, 3)
// 11
Math.max(...[-1, 5, 11, 3])
// 11

[1, ...[2,3], 4]
// [1, 2, 3, 4]

const str = "foo"
[ ...str ] // [ "f", "o", "o" ]

// redux reducer
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return { ...state, visibilityFilter: action.filter }
    default:
      return state
  }
}


// rest operator: aggregate of remaining arguments into single parameter

function f (x, y, ...a) {
    return (x + y) * a.length
}
f(1, 2, "hello", true, 7) === 9

for - of iterator

// for-of is a new loop in ES6 that replaces both for-in and forEach()
// Use it to loop over iterable objects (Arrays, strings, Maps, Sets, etc.

const iterable = ['a', 'b'];
for (const x of iterable) {
    console.log(x);
}

// break and continue work inside for-of loops:

for (const x of ['a', '', 'b']) {
    if (x.length === 0) break;
    console.log(x);
}

const map = new Map([
    [false, 'no'],
    [true, 'yes'],
]);
for (const [key, value] of map) {
    console.log(`${key} => ${value}`);
}

Promises



asyncWhereDoWeEat()
.then(value => Promise.resolve('Libanez and icecream') )  // like $q.when(value) in Angular
.then(value => Promise.resolve('Misto') )
.then(value => Promise.resolve('Merlot') )
.catch(error => { console.log('No time! Sodexo') });

Promise.all( [promise1, promise2] ).then(([result1, result2]) => {
    ···
})

Promise.race( [promise1, promise2] )

Modules

// There can be multiple named exports:

//------ 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';

//You can also import the complete module:
//------ main.js ------
import * as lib from 'lib';

// default export
//------ lib.js ------
export default const (x,y) => x + y
//------ main.js ------
import mainFunction, { square, diag } from 'lib';

// OR when using React:
import React, { PropTypes } from 'react'

Classes

class BetweenFilterEditor extends React.Component {

  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleDateChange = this.handleDateChange.bind(this)
  }

  getDate(fieldType, boundry, otherBoundry) {  }

  getTime(boundry, otherBoundry) { }

  getInput(fieldType, boundry, otherBoundry) { }

  handleChange() { }

  handleDateChange(type, boundry, otherBoundry, value) { }

  render() {
    return (
      <div>
        {this.getInput(this.props.filter.fieldType, 'lower', 'upper')}
        <span>and</span>
        {this.getInput(this.props.filter.fieldType, 'upper', 'lower')}
      </div>
    )
  }
}

Maps

The keys of a Map can be arbitrary values:

> const map = new Map(); // create an empty Map
> const KEY = {};

> map.set(KEY, 123);
> map.get(KEY)
123
> map.has(KEY)
true
> map.delete(KEY);
true
> map.has(KEY)
false

// WeakMaps: 
//   - A WeakMap is a Map that doesn’t prevent its keys from being garbage-collected. That means that you can associate data with objects 
//   without having to worry about memory leaks.
//   - used with listeners, DOM references where memory leaks might exist

Sets

let s = new Set()
s.add("hello").add("goodbye").add("hello")
s.size === 2
s.has("hello") === true
for (let key of s.values()) // insertion order, not arbitrary order
    console.log(key)


//  initialize a Set with elements when handing to the constructor an iterable
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]

Symbols

// Unique and immutable data type to be used as an identifier for object properties. Symbol can have an optional description, but for //debugging purposes only.

Symbol("foo") !== Symbol("foo")
const foo = Symbol()
const bar = Symbol()
typeof foo === "symbol"
typeof bar === "symbol"
let obj = {}
obj[foo] = "foo"
obj[bar] = "bar"
JSON.stringify(obj) // {}
Object.keys(obj) // []
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [ foo, bar ]

// define an iterator property for an object
const iterableObject = {
    [Symbol.iterator]() { // (A)
        const data = ['hello', 'world'];
        let index = 0;
        return {
            next() {
                if (index < data.length) {
                    return { value: data[index++] };
                } else {
                    return { done: true };
                }
            }
        };
    }
}
for (const x of iterableObject) {
    console.log(x);
}
// Output:
// hello
// world

// In ES6, you can use symbols and be sure that they are always unique:

const COLOR_RED    = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN  = Symbol('Green');
const COLOR_BLUE   = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');

function getComplement(color) {
    switch (color) {
        case COLOR_RED:
            return COLOR_GREEN;
        case COLOR_ORANGE:
            return COLOR_BLUE;
        case COLOR_YELLOW:
            return COLOR_VIOLET;
        case COLOR_GREEN:
            return COLOR_RED;
        case COLOR_BLUE:
            return COLOR_ORANGE;
        case COLOR_VIOLET:
            return COLOR_YELLOW;
        default:
            throw new Exception('Unknown color: '+color);
    }
}

Generators

Generators, a new feature of ES6, are functions that can be paused and resumed (think cooperative multitasking or coroutines). That helps with many applications. Two important ones are:

 

- implementing iterables

- blocking on async function calls

Generators

// implement iterators

let fibonacci = {
    *[Symbol.iterator]() {
        let pre = 0, cur = 1
        for (;;) {
            [ pre, cur ] = [ cur, pre + cur ]
            yield cur
        }
    }
}

for (let n of fibonacci) {
    if (n > 1000)
        break
    console.log(n)
}

// handle async function calls

// worker Saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: "USER_FETCH_SUCCEEDED", user: user});
   } catch (e) {
      yield put({type: "USER_FETCH_FAILED", message: e.message});
   }
}

ES6 presentation

By vmadincea

ES6 presentation

  • 977