EcmaScript unchained

50
A new @eiximenis film… EcmaScript 6 Unchained #GAPAND2015 proudly presentand

Transcript of EcmaScript unchained

A new @eiximenis film…

EcmaScript 6 Unchained

#GAPAND2015 proudly present…

and

Based on the novel written by

Brendan Eich@BrendanEich

Directed by

Eduard Tomàs@eiximenis

Recorded in the studios of

Plain Conceptshttp://

www.plainconcepts.com

Before the show begins…Please don’t talk while the show is going on.Just sit down, eat popcorns and do not interrupt.

Thanks.

Before the show begins...Interrupt me. Ask any question you have (no answered guaranteed :P)

Let’s enjoy together!

EcmaScript 2015 top new features

Let’s talk about the new features of EcmaScript 2015

• let allows declaration of variable with no hoisting and with block visibility!

• const allows single assignment of a variable

Let and const

Let and constfunction foo() { console.log(varidx); // Prints undefined as varidx is hoisted for (var varidx = 0; varidx < 10; varidx++) {; } console.log(varidx); // Prints 10 varidx = 100;}

function foolet() { console.log(letidx); // Reference error for (let letidx = 0; letidx < 10; letidx++) {; } console.log(letidx); // Reference error letidx = 100;}function fooconst() { console.log(max); // Error (access to const before is initialized) const max = 10; max = 0; // Syntax error!}

• EcmaScript has functions as 1st class Language

• Passing functions to functions is very natural and common

• But syntaxis is horrible and has its drawbacks

Arrow functions

Functions as parameters…var Brewery = function(name) { this.name = name; this.beers = [ {name: "Punk IPA", style:"ipa"}, {name: "5 am", style: "pale ale"}, {name: "Tokio", style: "stout"}];

this.beersByType = function(type) { return this.beers.filter(function (element) { return element.style == type; }); } this.getBeersWithFullName = function() { return this.beers.map(function(element) { return { name: element.name + " by " + this.name, style: element.style}; }); }}

Classic (ES5) code. Very verbose syntax

Arrow functions allow cleaner syntax

The new arrow syntax

var Brewery = function(name) { this.name = name; this.beers = [ {name: "Punk IPA", style:"ipa"}, {name: "5 am", style: "pale ale"}, {name: "Tokio", style: "stout"}];

this.beersByType = function(type) { return this.beers.filter(e => e.syle == type); }

this.getBeersWithFullName = function() { return this.beers.map(function(element) { return { name: element.name + " by " + this.name, style: element.style}; }); }}

this problem. What is the value of this inside the inner function?

The “this” problemMany ways to address itUse “self” or “that”

this.getBeersWithFullName = function () { var self = this; return this.beers.map(function (element) { return { name: element.name + " by " + self.name, style: element.style}; }); }

Need to remember use the “self” variable instead of “this” in the inner function.Unnatural way to do things.

The “this” problemMany ways to address itUse call/apply... or bind

this.getBeersWithFullName = function () { return this.beers.map(function (element) { return { name: element.name + " by " + this.name, style: element.style }; }.bind(this)); }

Just need to call bind in the inner function to tie it to the previous this context.More elegant than using self, but still unnatural...

Arrow functions solve the “this” problemMany ways to address itOr forget about the this problem with an arrow function

Arrow functions do what ES should be done from the beginning: preserve the “this” context.

this.getBeersWithFullName = function () { return this.beers.map(e => ({ name: e.name + " by " + this.name, style: e.style })); }

Arrow functions solve the “this” problemMany ways to address itUse call/apply... or bind

Arrow functions do what ES should be done from the beginning: preserve the “this” context.

this.getBeersWithFullName = function () {

return this.beers.map(e => ({ name: e.name + " by " + this.name, style: e.style

})); }

Bonus: Do you notice the parens? ES parser needs them if arrow function returns a object in notation-object form.

• Very easy way to create strings with many values

• No more need for string concatenation

Template strings

String interpolationNo more string concatenation

ES5 Standard code, using string concatenation to create the name property

this.getBeersWithFullName = function () { return this.beers.map(e => ({ name: e.name + " by " + this.name, style: e.style })); }

String interpolationNo more string concatenation

ES2015 code with string interpolation

this.getBeersWithFullName = function () { return this.beers.map(e => ({ name: `${e.name} by ${this.name}`, style: e.style })); }

Yes... We had two markers for strings in ES (single quotes and double quotes) but for string interoplation we will use a new third marker.

Bonus: String interpolation works on multi-line strings. You can still use \n if prefer, of course.

• We can specify the prototype with objet literal

• Shorthand syntax if property name equals to variable having the value

• Dynamically generated property names

Enhanced object literals

Enhanced object literalsMuch more power with a very compact syntacvar question = 'The answer to life the universe and everything';var answer = 42;

var base = { question, answer, toString: (() => `${this.question} is ${this.answer}`)};

var der = { __proto__: base, ['answer_' + (() => this.answer)()]: this.answer}

• Unique and immutable data type that can be used as an identifier for object properties

• Used in ways that previously we used strings

• Not private!

Symbol

SymbolStrings are not good for identifiers

We can use strings for object properties as usual

We can create a Symbol using Symbol.for(“name”) or Symbol(“name”)And we can use this newly Symbol to create a new property for the object

var obj = {};

obj[Symbol("a")] = "a";obj[Symbol.for("b")] = "b";obj["c"] = "c";obj.d = "d";

for (var i in obj) { console.log(i); // logs "c" and "d"}

But are not private! Welcome to Object.getOwnPropertySymbols()

Properties stored in símbols don’t appear in for..in iterations nor in Object.getOwnPropertyNames()

SymbolThere are local and global symbols

Of course... No string coertion exists on Symbols

Symbol("a") == Symbol("a") // falseSymbol("a") === Symbol("a") // falseSymbol("a") == Symbol("a") // trueSymbol.for("a") === Symbol.for("a") // true

Symbol("a") == "a" // falseSymbol.for("a") == "a" // false

SymbolPrepare your mind for a new set of tweets complaining about new JavaScript #WTFs

But you know the truth...

Any sufficiently advanced programming technique is indistinguishable from WTF

Sir Arthur C. Clarke

• A mechanism for obtaining parts of objects or arrays and assign them to variables

Destructuring

DestructuringYou have an array with two values. How to assign these two values to two independent variables?

var values = [42, 69];var x = 42;var y = 69;

ES5 classic way of doing this

var values = [42, 69];var [x,y] = values; Same with ES2015

destructuring

DestructuringThink a minute... How to swap two variables in one single line of code?

var x = 42;var y = 69;

We want x = 69 and y = 42, but with one simple line of code...

var x = 42;var y = 69;[x, y] = [y, x];

Destructuring – Spread operatorA new operator come to the game: The spread operator allows you to get the rest values of an array

This declares three variables, called c, d and e.Value of c is first variable of arr (4)Value of d is second variable of arr (8)Value of e is the rest values of arr (an array with 15, 16, 23 and 42)

var arr = [4, 8, 15, 16, 23, 42];var [c,d,...e ] = arr

Destructuring – Spread operatorSpread operator helps dealing with functions that have a variable number of arguments.

function foo(name, options, ...other) { // Some code}

Inside foo:

name is value of 1st parameter (or undefined)options is value of 2nd parameter (or undefined)other is an array with all the others paràmetres (or empty array but never undefined)

Destructuring – Spread and applySpread operator make easy call functions with various parameters that we have in array. It’s like using apply but without using apply (and more flexible)

Just keep in mind that x.f(...a) is equivalent to f.apply(x, a);

• Objects that enable custom iterations over collections

• Allow creation of “never-ending” collections with lazy evaluation

Iterators and for..of

IteratorsEvery object can express itself as a collection

An iterator is a function with a specific name [Symbol.iterator].This function mush return an object with one method next().

The method next() must return an object with two properties:

value: corrent value of iterationdone: boolean that equals true if the last element has been reached.

let fibonacci = { [Symbol.iterator]() { let pre = 0, cur = 1; return { next() { [pre, cur] = [cur, pre + cur]; return { done: false, value: cur } } } }}

for (var n of fibonacci) {// truncate the sequence at 1000 if (n > 1000) break; console.log(n);}

Notice the new for..of to iterate over all the values of the iterator of an object

• Allow easy creation of iterators

• Generators are co-routines

Generators

GeneratorsIterators made easy

Use of function* to create the generator.Generators return iterators ([Symbol.iterator])

Use of yield to return “the next” value of the iterator

If a generator never exits it returns a “never ending” iterator. Of course you must stop iterating it when no more vàlues are needed.

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

for (var n of fibonacci) { // truncate the sequence at 1000 if (n > 1000) break; console.log(n);}

• Syntax sugar

• JavaScript is still dynamic• JavaScript inheritance mechanism is still the prototype

• JavaScript classes have little in common with Java or C# classes

Classes

ClassesPrototype inheritance with a new syntax

This is equivalent to the constructor function pattern in ES5

class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; }}

Objects must be created with new and classes do not exist in runtime (typeof Point is ‘function’)

ClassesClasses allow:

• Properties (using the same ES5 get/set syntax)• Inheritance (class Point3D extends Point)• Prototype of Point3D objects will be a Point object

• Static methods• Classes define the constructor pseudo-method

(invoked when object is creatred). • Derived constructors must call base to call the base constructor

before using this.• Except if base constructor override its return, returning something

Classesclass Point { constructor(x, y) { this.x = x; this.y = y; } // More stuff} class ColorPoint extends Point { constructor(x, y, color) { super(x, y); this.color = color; } // more stuff} let cp = new ColorPoint(25, 8, 'green');

It’s our beloved prototype inheritance! Nothing really new.

• Finally we have a standard and common module system

• If you prefer CommonJS over AMD you’ll like the ES6 module system

• If you prefer AMD over CommonJS you’ll like the ES6 module system

Modules

ModulesSimilar to CommonJS modules because• Compact syntax• Prefer single export• Support for cyclic dependences

Similar to AMD modules because• Support for asynchronous module loading• Configurable module loading

ModulesNamed 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));}

Any declaration prefixed by “export” keyword is an export of the module

Using import keyword we can choose what of the exports of the module we want to include to the global namespace

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

ModulesNamed 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));}

Any declaration prefixed by “export” keyword is an export of the module

Using import keyword we can choose what of the exports of the module we want to include to the global namespace

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

But everybody says...

Two days ago in BCN (before #LeyMordaza)...

ModulesImporting in custom namespace//------ 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));}

Any declaration prefixed by “export” keyword is an export of the module

You can, of course, specify a namespace for the import, keeping the global namespace clean (at least all clean that the w3c guys allow us :P)...

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

Did you notice the use of * to import all the exports of a module?

ModulesSingle export

Module exports a class (note the use of default).Default exports must be named on import

Module export just a function

//------ MyClass.js ------export default class { ... }; //------ main2.js ------import MyClass from 'MyClass';let inst = new MyClass();

//------ myFunc.js ------export default function () { ... }; //------ main1.js ------import myFunc from 'myFunc';myFunc();

ModulesModules are statically defined and imported. CommonJS allows for:

This is not allowed in ES6Less flexibility but no need to execute the code to find the imports or exports of a module. Can be optimized.

var mylib;if (Math.random()) { mylib = require('foo');} else { mylib = require('bar');}

The End

Questions?

Eduard Tomàs i Avellana@[email protected]

http://www.plainconcepts.com

#GAPAND2015

Time for a beer?