On Github kirilknysh / es2015-essentials-talk
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 ->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
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 ... }
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! }
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}`
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" }; }
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];
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
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
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 }; });
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) => { //... }); }, []); }
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}`; } }
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}`; } }
files that export an API
with strict mode by default
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; }
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';
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';
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 } );
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()]);
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
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}
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}
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) => { ... });
const cache = new WeakMap(); cache.set(channel, getMetaData(channel)); cache.get(channel);
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) => { ... });
const cache = new WeakSet(); cache.add(channel); cache.has(channel) === true;
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';
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