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!
• 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...
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
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...
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