ES6 essentials – ES2015 – ES Harmony



ES6 essentials – ES2015 – ES Harmony

1 0


es2015-essentials-talk


On Github kirilknysh / es2015-essentials-talk

ES6 essentials

ES2015

ES Harmony

by Kiril Knysh

I'd like to talk to you about ES6... also known as ES2015... also known as ES Harmony... So why do we pay so much attention to this ES release? Let's take a look at other languages releases plan ->

PL releases

Here's release dates for some popular PL. Compare to ES Why is it still important to talk about ES2015? ->

ES2015 browsers support

March 2016

Desktop

91%
85%
53%
79%

Mobile

29%
53%
A year ago the best ES2015 support for ES2015 was in Edge and FF (69%) Noone could decide if he\she can use ES2015 Since then the situation changed But still not ideal. What to do? ->

Trans (comp) pilers

March 2016

We'll have to use compilers in future

Features overview

let / const

block-scoped

for (let i = 0; i < count; i++) {
  ...
}
//i is not defined here
if (value <= maxValue) {
  let newValue = value * 0.1; 
}
//newValue is not defined here

let / const

constrained by TDZ semantics

function execute() {
  console.log(value);
  var value = 10;
  console.log(value);
  ...
}
function execute() {
  console.log(value); //Uncaught ReferenceError: value is not defined
  let value = 10;
  console.log(value);
  ...
}
function execute() {
  let value = 10;
  console.log(value); //10
  ...
}

const

single-assignment

function execute() {
  const value = 10;
  ...
  value = 15; //Uncaught TypeError: Assignment to constant variable
}
function execute() {
  const value = { tax: 10, mult: 0.4 };
  ...
  value.tax = 15; //OK!
}

Template strings(literals)

declared with `backticks`

`I am template`

can be multiline

`I am
template`

allow interpolation

const value = 10;
function getMult() { return 0.4; }

`Value is ${value};
Mult is ${getMult()};
2 + 2 = ${2 + 2}`

Enhanced object literals

ES5function createChannel(id) {
  var channel = {
    id: id,
    toString: function() { return this.id; }
  };
  
  channel[Constants.KEY_1] = "Value 1";
  channel[getKey()] = "Value 2";
  
  return channel;
}
ES2015function createChannel(id) {
  return {
    id,
    toString() { return this.id + super.toString(); }, //id + [object Object]
    [Constants.KEY_1]: "Value 1",
    [getKey()]: "Value 2"
  };
}

Destructuring

ES5var id = channel.id;
var num = channel.num;
var url = channel.pictures[0] || 'default.png';
ES2015const { id, num, pictures: [url='default.png'] } = channel;

works with arrays

const [zero, , two] = [0, 1, 2, 3, 4];
console.log(zero); //0
console.log(two); //2

swap values

[left, right] = [right, left];

Destructuring

in function params

function printChannel({ id, num }) {
  console.log(`ID=${id}, NUM=${num}`);
}

printChannel(channel);
function printChannel({ id='', num=0 } = {}) {
  console.log(`ID=${id}, NUM=${num}`); //ID=, NUM=0
}

printChannel();

spread + rest

spread

function getDistance(x1, y1, x2, y2) {
  return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}

const point1 = [10, 5];
const point2 = [12, 9];
getDistance(...point1, ...point2);

rest

function execute(fun, context, ...params) {
  return fun.apply(context, params);
}

execute(Math.max, Math, 10, 20, 5); //20

Fat-arrow functions

const fetchChannels = (startIndex, count) => {
  return CHANNELS.splice(startIndex, count);
}

shorthands

//no parenthesis for params, implicit return
channels.map(channel => channel.id);

//to return an object implicitly, wrap it in parenthesis
channels.map(channel => ({
  channelId: channel.id,
  poster: channel.pictures[0]
}));

//2 and more params
channels.map((channel, index) => {
  const poster = channel.pictures[0] + `?index=${index}`;

  return { channelId: channel.id, poster };
});

Fat-arrow functions

are bound to their lexical scope

const channel = {
  pictures: ['https://...', 'https://...'],
  size: { w: '300', h: '200' },
  printPicturesUrl() { 
    this.pictures.forEach((url) => {
      console.log(`${url}?w=${this.size.w}&h=${this.size.h}`);
    });
  }
}

don't have names, but

function mapChannels(channels) {
  return channels.reduce((result, channel) => {
    channel.pictures.forEach((picture) => {
      //...
    });
  }, []);
}

Classes

class Animal {
  constructor(name, color) {
    this.name = name;
    this.color = color;
  }

  //static function
  static className() {
    return 'Animal';
  }

  //getter property
  get stringified() {
    return `${this.name} - ${this.color}`;
  }
}

Classes

inheritance

class Dog extends Animal {
  constructor(name, color, birthDay) {
    super(name, color);

    this.birthDay = birthDay;
  }

  static className() {
    return 'Dog';
  }

  get stringified() {
    return `${super.stringified} : ${this.birthDay}`;
  }
}

Modules

files that export an API

with strict mode by default

Modules

exports

default exportexport default function(channel) {
  return channel && channel.logo || null;
}
named exportexport const LOGO_KEY = 'channel-logo-key-1';
multiple exportsfunction getName(channel) { return channel && channel.name || ''; }
const LOGO_KEY = 'channel-logo-key-1';

export { getName, LOGO_KEY };

export default function(channel) {
  return channel && channel.logo || null;
}

Modules

imports

load moduleimport 'channels-helper';
import default asexport default function(channel) {
  return channel && channel.logo || null;
}

import getChannelLogo from 'channels-helper';
import named exportsexport const LOGO_KEY = 'channel-logo-key-1';

import { LOGO_KEY } from 'channels-helper';
import { LOGO_KEY as channelLogoKey } from 'channels-helper';

Modules

imports

mixed importfunction getName(channel) { return channel && channel.name || ''; }
const LOGO_KEY = 'channel-logo-key-1';
export { getName, LOGO_KEY };
export default function(channel) {
  return channel && channel.logo || null;
}

import getChannelLogo, { getName as getChannelName } from 'channels-helper';
import namespacefunction getName(channel) { return channel && channel.name || ''; }
const LOGO_KEY = 'channel-logo-key-1';
export { getName, LOGO_KEY };

import * as ChannelsHelper from 'channels-helper';

Promises

const p = new Promise((resolve, reject) => {
  //do some async work
  //...
  if (result) {
    resolve(result);
  } else {
    reject(error);
  }
});

follow Promises/A+ spec

p.then(
  (result) => {
    //handle success
  },
  (error) => {
    //process error
  }
);

Promises

support chaining

fetchChannels()
  .then((channels) => {
    return fetchEvents(channels);
  }).then((events) => {
    return processEvents(events);
  }).catch((error) => {
    log.error(error); //catches Promise reject + exceptions
  });

static methods

//create resolved Promise
Promise.resolve(value);
//create rejected Promise
Promise.reject(error);
//wait for all Promises to be resolved or at least one rejected
Promise.all([fetchChannels(), fetchProfile()]);
//wait for the 1st resolved/rejected Promise
Promise.race([fetchChannels1(), fetchChannels2()]);

Iterators

objects that know how to access items from a collection

implement the @@iterator method (property with a Symbol.iterator key)

const channels = {};
channels[Symbol.iterator] = function* () {
  yield 'BBC';
  yield 'MTV';
  yield 'Fashion TV';
}

[...channels]; //['BBC', 'MTV', 'Fashion TV']
for (let channel of channels) { console.log(channel); }
//BBC
//MTV
//Fashion TV

Generators

special type of functions that work as a factory for iterators

function* getChannelsIterator() {
  yield* ['BBC', 'MTV', 'Fashion TV'];
}

const channels = getChannelsIterator();

channels.next(); //{value: "BBC", done: false}
channels.next(); //{value: "MTV", done: false}
channels.next(); //{value: "Fashion TV", done: false}
channels.next(); //{value: undefined, done: true}

Generators

internal state

function* getUniqId() {
  let current = 0;

  while (true) {
    let reset = yield current;
    current = reset ? 0 : ++current;
  }
}

const generator = getUniqId();
generator.next(); //{value: 0, done: false}
generator.next(); //{value: 1, done: false}
generator.next(); //{value: 2, done: false}
generator.next(true); //{value: 0, done: false}
generator.next(); //{value: 1, done: false}

Map

const requestParams = { w: 300, h: 200 };
const response = fetch(requestParams);

const map = new Map([ ['key-1', 'value-1'], [requestParams, response] ]);

map.set('key-2', 'value-2');
map.get(requestParams) === response;
map.delete('key-1');
map.has('key-1') === false;

iterate

map.forEach((value, key) => { ... });
for (let [key, value] of map) { ... }
Array.from(map).forEach(([key, value], index) => { ... });
_.forEachRight([...map], ([key, value], index) => { ... });

WeakMap

const cache = new WeakMap();

cache.set(channel, getMetaData(channel));
cache.get(channel);
  • similar to Map but:
  • weak references to keys
  • keys must be reference types
  • not iterable

Set

const set = new Set([1, 2, 3]);

set.size; //3
set.add(2);
set.size; //3
set.has(1) === true;

iterate

set.forEach((value) => { ... });
for (let value of set) { ... }
Array.from(set).forEach((value, index) => { ... });
_.forEachRight([...set], (value, index) => { ... });

WeakSet

const cache = new WeakSet();

cache.add(channel);
cache.has(channel) === true;
  • similar to Set but:
  • weak references to values
  • values must be reference types
  • not iterable

Proxies

useful for interception, logging/profiling, data mocks, etc

const channel = { id: 'mtv', num: 12 };
const handler = {
  get: (receiver, name) => {
    if (name === 'num') {
      return `${receiver.id} - ${receiver.num}`;
    }

    return receiver[name];
  }
};

const prox = new Proxy(channel, handler);

prox.id === 'mtv';
prox.num === 'mtv - 12';

Proxies

freeze objects

const channel = { id: 'mtv', num: 12 };

function preventChange() { throw new Error('Object is frozen'); }
const handler = {
  set: preventChange,
  defineProperty: preventChange,
  deleteProperty: preventChange,
  preventExtensions: preventChange,
  setPrototypeOf: preventChange
};

const prox = new Proxy(channel, handler);

prox.id = 'bbc'; //Uncaught Error: Object is frozen

Proxies

  • traps
  • getproxy.prop, proxy['prop']
  • setproxy.prop = value, proxy['prop'] = value
  • hasin operator
  • deletePropertydelete operator
  • applyfunction calls
  • constructnew operator
  • definePropertyObject.defineProperty, declarative alternatives
  • enumeratefor..in loops
  • ownKeysObject.keys and related methods
  • ...
©aptain America ©aptain America

Thank you!

Kiril Knysh
ES6 essentials ES2015 ES Harmony by Kiril Knysh I'd like to talk to you about ES6... also known as ES2015... also known as ES Harmony... So why do we pay so much attention to this ES release? Let's take a look at other languages releases plan ->