Presented by Leon Plata
1
Compatibility Table
Traspilers
io.js and node.js
2
3
let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope
Unlike most programming languages, JavaScript does not have block-level scope (variables scoped to surrounding curly brackets); instead, JavaScript has function-level scope. Variables declared within a function are local
function varTest() {
var x = 31;
if (true) {
var x = 71; // same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // different variable
console.log(x); // 71
}
console.log(x); // 31
}
for (let i = 0; i<10; i++) {
console.log(i); // 0, 1, 2, 3, 4 ... 9
}
console.log(i); // i is not defined
This declaration creates a constant that can be global or local to the function in which it is declared. Constants are block-scoped. The value of a constant cannot change through re-assignment, and a constant cannot be re-declared. An initializer for a constant is required. A constant cannot share its name with a function or a variable in the same scope.
// Assigning to A const variable is a syntax error
const A = 1; A = 2;
// const requires an initializer
const FOO; // SyntaxError: missing = in const declaration
4
Object destructuring assignment syntax uses an object literal on the left side of an assignment operation
var o = {p: 42, q: true};
var {p, q} = o;
console.log(p); // 42
console.log(q); // true
// Assign new variable names
var {p: foo, q: bar} = o;
console.log(foo); // 42
console.log(bar); // true
Similarly, you can destructure arrays using array literal syntax on the left side of an assignment operation
var foo = ["one", "two", "three"];
// without destructuring
var one = foo[0];
var two = foo[1];
var three = foo[2];
// with destructuring
var [one, two, three] = foo;
// swapping variables
var a = 1;
var b = 3;
[a, b] = [b, a];
// Ignoring some values
function f() {
return [1, 2, 3];
}
var [a, , b] = f();
console.log("A is " + a + " B is " + b);
It’s possible to mix objects and arrays together in a destructuring assignment expression using a mix of object and array literals
var options = {
repeat: true,
save: false,
colors: [ "red", "green", "blue" ]
};
var { repeat, save, colors: [ firstColor, secondColor ]} = options;
console.log(repeat); // true
console.log(save); // false
console.log(firstColor); // "red"
console.log(secondColor); // "green"
var metadata = {
title: "Scratchpad",
translations: [
{
locale: "de",
localization_tags: [ ],
last_edit: "2014-04-14T08:43:37",
url: "/de/docs/Tools/Scratchpad",
title: "JavaScript-Umgebung"
}
],
url: "/en-US/docs/Tools/Scratchpad"
};
var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata;
console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
var people = [
{
name: "Mike Smith",
family: {
mother: "Jane Smith",
father: "Harry Smith",
sister: "Samantha Smith"
},
age: 35
},
{
name: "Tom Jones",
family: {
mother: "Norah Jones",
father: "Richard Jones",
brother: "Howard Jones"
},
age: 25
}
];
for (var {name: n, family: { father: f } } of people) {
console.log("Name: " + n + ", Father: " + f);
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
For loop iteration
function userId({id}) {
return id;
}
function whois({displayName: displayName, fullName: {firstName: name}}){
console.log(displayName + " is " + name);
}
var user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
console.log("userId: " + userId(user)); // "userId: 42"
whois(user); // "jdoe is John"
Function parameter
5
// ECMAScript 5
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
// the rest of the function
}
// ECMAScript 2015
function makeRequest(url, timeout = 2000, callback = function() {}) {
// the rest of the function
}
// ECMAScript 5
function pick(object) {
let result = Object.create(null);
for (let i = 1, len = arguments.length; i < len; i++) {
result[arguments[i]] = object[arguments[i]];
}
return result;
}
// ECMAScript 2015
function pick(object, ...keys) {
let result = Object.create(null);
for (let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}
Allows you to specify that multiple independent arguments should be combined into an array
// Normal way
let value1 = 25,
value2 = 50;
console.log(Math.max(value1, value2)); // 50
// Better
let values = [25, 50, 75, 100]
console.log(Math.max.apply(Math, values)); // 100
// Much better
let values = [25, 50, 75, 100]
// equivalent to
// console.log(Math.max(25, 50, 75, 100));
console.log(Math.max(...values)); // 100
Allows you to specify an array that should be split and have its items passed in as separate arguments to a function
6
// ECMAScript 5
function createPerson(name, age) {
return {
name: name,
age: age
};
}
// ECMAScript 2015
function createPerson(name, age) {
return {
name,
age
};
}
// ECMAScript 5
var person = {
name: "Nicholas",
sayName: function() {
console.log(this.name);
}
};
// ECMAScript 2015
var person = {
name: "Nicholas",
sayName() {
console.log(this.name);
}
};
var suffix = " name";
var person = {
["first" + suffix]: "Nicholas",
["last" + suffix]: "Zakas"
};
console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"
7
Arrow functions are, as the name suggests, functions defined with a new syntax that uses an “arrow” (=>)
// inline
var reflect = value => value;
// block
var reflect = value => {
return value;
};
// effectively equivalent to:
var reflect = function(value) {
return value;
};
// parenthesis
var sum = (num1, num2) => num1 + num2;
// empty
var nothing = () => {};
// return object inline
var createObj = () -> ({id: 1, value: 10});
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", (function(event) {
this.doSomething(event.type); // no error
}).bind(this), false);
},
doSomething: function(type) {
var self = this;
setTimeout(function() {
console.log("Handling " + type + " for " + self.id);
});
},
getFirstArgFunc: function() {
var args = arguments;
return function() {
return args[0];
}
}
};
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
setTimeout(() => console.log("Handling " + type + " for " + this.id));
},
getFirstArgFunc: function() {
return () => arguments[0];
}
};
8
ES6 transpilers compile ES6 modules to ES5. Due to the completely new way of passing on data (via bindings), you should expect the ES5 version to not always be completely compliant with the ES6 spec. Things are even trickier when transpiled ES6 code has to interoperate with native CommonJS or AMD modules.
//------ 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';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5
Importing the whole module
//------ myFunc.js ------
export default function () { ... };
//------ main1.js ------
import myFunc from 'myFunc';
myFunc();
9
// class expressions do not require identifiers after "class"
let PersonClass = class {
// equivalent of the PersonType constructor
constructor(name) {
this.name = name;
}
// equivalent of PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};
function createObject(classDef) {
return new classDef();
}
let obj = createObject(class {
sayHi() {
console.log("Hi!");
}
});
obj.sayHi(); // "Hi!"
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}("Nicholas");
person.sayName(); // "Nicholas"
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
class PersonClass {
// equivalent of the PersonType constructor
constructor(name) {
this.name = name;
}
// equivalent of PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
// equivalent of PersonType.create
static create(name) {
return new PersonClass(name);
}
}
There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property
Class properties and prototype data properties need be created outside the declaration.
Properties specified in a class definition are assigned the same attributes as if they appeared in an object literal.
class declarations to declare and define the capabilities of a class. Not its members. An ES6 class declaration defines its contract for its user.
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
constructor(length) {
// same as Rectangle.call(this, length, length)
super(length, length);
}
}
var square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
// abstract base class
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error("This class cannot be instantiated directly.")
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
this.length = length;
this.width = width;
}
}
var x = new Shape(); // throws error
var y = new Rectangle(3, 4); // no error
console.log(y instanceof Shape); // true
10
ECMAScript 6 symbols began as a way to create private object members.
The focus was around creating properties that were not identified by string names.
Any property with a string name was easy picking to access regardless of the obscurity of the name.
The initial “private names” feature aimed to create non-string property names. That way, normal techniques for detecting these private names wouldn’t work.
var firstName = Symbol("first name");
var lastName = Symbol();
var person = {};
person[firstName] = "Nicholas";
person[lastName] = "Zakas";
console.log("first name" in person); // false
console.log(person[firstName]); // "Nicholas"
console.log(firstName); // "Symbol(first name)"
console.log(person[lastName]); // "Zakas"
var uid = Symbol.for("uid");
var object = {
[uid]: "12345"
};
console.log(object[uid]); // "12345"
console.log(uid); // "Symbol(uid)"
var uid2 = Symbol.for("uid");
console.log(uid === uid2); // true
console.log(object[uid2]); // "12345"
console.log(uid2); // "Symbol(uid)"
var Person = (function() {
var nameSymbol = Symbol('name');
function Person(name) {
this[nameSymbol] = name;
}
Person.prototype.getName = function() {
return this[nameSymbol];
};
return Person;
}());
var p = new Person('John');
console.log('Person 3 name: ' + p.getName());
delete p.name;
console.log('Person 3 name: ' + p.getName() + ' — stays private.');
console.log('Person 3 properties: ' + Object.getOwnPropertyNames(p));
In addition to your own symbols, JavaScript has some built-in symbols which represent internal language behaviors which were not exposed to developers in ECMAScript 5 and before
11
let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 16
let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 31
let count = 10,
price = 0.25,
message = `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
12
Very quick reference
Any kind of object or primitive type can be used as key
Let's store unique values of any type of data
Map
Set
var key = {};
var map = new Map();
map.add(key, 'this is a value');
var set = new Set();
map.add(101);
map.add(101);
for (var value of set) {
console.log(value);
} // 101 once
13
The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced. The keys must be objects and the values can be arbitrary values
new WeakMap([iterable])
// WeakMap.prototype.delete(key)
// WeakMap.prototype.get(key)
// WeakMap.prototype.has(key)
// WeakMap.prototype.set(key, value)
new WeakSet([iterable])
// WeakSet.prototype.add(value)
// WeakSet.prototype.delete(value)
// WeakSet.prototype.has(value)
WeakSets are collections of objects only and not of arbitrary values of any type.
The WeakSet is weak: References to objects in the collection are held weakly. If there is no other reference to an object stored in the WeakSet, they can be garbage collected.
WeakSets are not enumerable.
14
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} : {done: true};
}
}
}
An object is an iterator when it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence
The for...of statement creates a loop Iterating over iterable objects
let arr = [3, 5, 7];
arr.foo = "hello";
for (let i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // logs "3", "5", "7"
}
This is really just an optimization. Instead of heaving to return a full array, you can just return a generator which lazily gives individual values back at each iteration. This reduces memory and allocation. Since no array allocation is necessary, you can also express infinite data structures.
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);
}
The Symbol.iterator well-known symbol specifies the default iterator for an object. Used by for...of.
var myIterable = {}
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
15
A generator is a special type of function that works as a factory for iterators
function* idMaker(){
var index = 0;
while(true)
yield index++;
}
var gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...
The yield keyword causes generator function execution to pause and the value of the expression following the yield keyword is returned to the generator's caller. It can be thought of as a generator-based version of the return keyword.
16
Very quick reference
var promise = new Promise(
function (resolve, reject) { // (A)
...
if (...) {
resolve(value); // success
} else {
reject(reason); // failure
}
});