Andrei Antal
Angular Workshop
ES6 and TypeScript
function getColor(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
return null;
}
}
ES5 variable hoisting
function getColor(condition) {
var value; // undefined
if (condition) {
value = "blue";
// other code
return value;
} else {
return null;
}
}
function getColor(condition) {
if (condition) {
var value = "blue";
// ...
return value;
} else {
// value = undefined
return null;
}
// value = undefined
}
ES5 variable hoisting
for (var i = 0; i < 10; i++) {
process(items[i]);
}
ES5 variables in loops
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
ES5 functions in loops
console.log(i); // 10
funcs.forEach(function(func) {
func(); // 10, 10, 10....
});
The LET declarations
function getColor(condition) {
if (condition) {
let value = "blue";
// other code
return value;
} else {
// value doesn't exist here - ReferenceError
return null;
}
// value doesn't exist here - ReferenceError
}
Redeclarations
var count = 30;
// Syntax error
let count = 40;
var count = 30;
// Does not throw an error
if (condition) {
let count = 40; // shadows the outer variable inside this scope
// more code
}
The CONST declarations
// Valid constant
const maxItems = 30;
// Syntax error: missing initialization
const name;
var message = "Hello!";
let age = 25;
// Each of these would throw an error.
const message = "Goodbye!";
const age = 30;
const maxItems = 5;
maxItems = 6; // throws error
The CONST declarations
const person = {
name: "Andrei"
};
// works
person.name = "George";
// throws an error - reinitialization
person = {
name: "George"
};
Temporal Dead Zone (TDZ)
if (condition) {
console.log(typeof value); // ReferenceError!
let value = "blue";
}
console.log(typeof value); // "undefined"
if (condition) {
let value = "blue";
}
Loop behaviour - let
for (let i = 0; i < 10; i++) {
process(items[i]);
}
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
// i is not accessible here - throws an error
console.log(i);
funcs.forEach(function(func) {
func(); // 0, 1, 2, 3,...
})
Loop behaviour - const
var funcs = [];
// throws an error after one iteration
for (const i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
var funcs = [],
object = {
a: true,
b: true,
c: true
};
// doesn't cause an error
for (const key in object) {
funcs.push(function() {
console.log(key);
});
}
New string methods :
New string methods
var msg = "Hello world!";
console.log(msg.startsWith("Hello")); // true
console.log(msg.endsWith("!")); // true
console.log(msg.includes("o")); // true
console.log(msg.startsWith("o")); // false
console.log(msg.endsWith("world!")); // true
console.log(msg.includes("x")); // false
console.log(msg.startsWith("o", 4)); // true
console.log(msg.endsWith("o", 8)); // true
console.log(msg.includes("o", 8)); // false
console.log("x".repeat(3)); // "xxx"
console.log("hello".repeat(2)); // "hellohello"
console.log("abc".repeat(4)); // "abcabcabcabc"
Template literals
Template literals
let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); // "string"
console.log(message.length); // 12
Multiline strings
// old syntax bug
var message = "Multiline \
string";
console.log(message); // "Multiline string"
var message = "Multiline \n\
string";
console.log(message); // "Multiline
// string"
var message = [
"Multiline ",
"string"
].join("\n");
let message = "Multiline \n" +
"string";
Multiline strings
let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 31
let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 16
Multiline strings
let message = `
<table>
<tr>Jane</tr>
<tr>Bond&</tr>
<tr>Lars</tr>
<tr>Croft</tr>
</table>`;
var message = "<table>" +
"<tr>Jane</tr>" +
"<tr>Bond&</tr>" +
"<tr>Lars</tr>" +
"<tr>Croft</tr>" +
"</table>";
Substitutions
let name = "Andrei",
message = `Hello, ${name}.`;
console.log(message); // "Hello, Andrei."
let count = 10,
price = 0.25,
message = `${count} items cost $${(count * price).toFixed(2)}.`;
console.log(message); // "10 items cost $2.50."
Tagged Templates
let message = tag`Hello world`;
function tag(literals, ...substitutions) {
// return a string
}
Tagged Templates
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
literals
substitutions
Tagged Templates
let message = tag`Hello world`;
function tag(literals, ...substitutions) {
// return a string
}
Object Literal Syntax Extensions
function createPerson(name, age) {
return {
name: name,
age: age
};
}
function createPerson(name, age) {
return {
name,
age
};
}
Object Literal Syntax Extensions
var person = {
name: "Nicholas",
sayName: function() {
console.log(this.name);
}
};
var person = {
name: "Nicholas",
sayName() {
console.log(this.name);
}
};
Object Literal Syntax Extensions
let person = {
// method
getGreeting() {
return "Hello";
}
};
// not a method
function shareGreeting() {
return "Hi!";
}
Object Literal Syntax Extensions
let person = {
getGreeting() {
return "Hello";
}
};
// prototype is person
let friend = {
getGreeting() {
return super.getGreeting() + ", hi!";
}
};
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // "Hello, hi!"
Object Literal Syntax Extensions
var person = {},
lastName = "last name";
person["first name"] = "Andrei";
person[lastName] = "Antal";
console.log(person["first name"]); // "Andrei"
console.log(person[lastName]); // "Antal"
var person = {
"first name": "Andrei"
};
console.log(person["first name"]); // "Andrei"
var suffix = " name";
var person = {};
person["first" + suffix] = "Andrei";
Object Literal Syntax Extensions
var lastName = "last name";
var person = {
"first name": "Andrei",
[lastName]: "Antal"
};
console.log(person["first name"]); // "Andrei"
console.log(person["last name"]); // "Antal"
var suffix = " name";
var person = {
["first" + suffix]: "Andrei",
["last" + suffix]: "Antal"
};
console.log(person["first name"]); // "Andrei"
console.log(person["last name"]); // "Antal"
New methods
console.log(+0 == -0); // true
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
console.log(5 == 5); // true
console.log(5 == "5"); // true
console.log(5 === 5); // true
console.log(5 === "5"); // false
console.log(Object.is(5, 5)); // true
console.log(Object.is(5, "5")); // false
New methods
Object.assign(target, ...sources)
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.
Object.assign()
var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };
var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
Arrow function syntax
var reflect = value => value;
var reflect = (value) => { return value };
// ES5 equivalent function
var reflect = function(value) {
return value;
};
var reflect = (value) => { return value };
Arrow function syntax
var getName = () => "Andrei";
// ES5 equivalent function
var getName = function() {
return "Andrei";
};
var sum = (num1, num2) => num1 + num2;
// ES5 equivalent function
var sum = function(num1, num2) {
return num1 + num2;
};
Arrow function syntax
var getTempItem = id => ({ id: id, name: "Temp" });
// effectively equivalent to:
var getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
var comparator = (a, b) => a - b;
console.log(typeof comparator); // "function"
console.log(comparator instanceof Function); // true
Binding to this
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", function(event) {
this.doSomething(event.type);
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
PageHandler.init();
Binding to this
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click", (function(event) {
this.doSomething(event.type); // no error
}).bind(this), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
PageHandler.init();
Binding to this
function UiComponent {
var that = this;
var button = document.getElementById(#myButton);
button.addEventListener(click,
function () {
console.log(CLICK);
that.handleClick();
});
}
UiComponent.prototype.handleClick = function () { ... };
Binding to this
var PageHandler = {
id: "123456",
init: function() {
document.addEventListener("click",
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log("Handling " + type + " for " + this.id);
}
};
Arrow function in array methods
var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b); // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46]
var sum = arr.reduce(function (a, b) {
return a + b;
}); // 66
var even = arr.filter(function (v) {
return v % 2 == 0;
} // [6, 0, 18]
var double = arr.map(function (v){
return v * 2;
)} // [10, 12, 26, 0, 2, 36, 46]
ES5 way of making "classes"
function Person(name) {
this.name = name;
}
PersonType.prototype.sayName = function() {
console.log(this.name);
};
let person = new Person("Andrei");
person.sayName(); // outputs "Andrei"
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
console.log(typeof Person); // "function"
Prototypes
ES6 Classes
class PersonClass {
constructor(name) { // same as Person constructor function
this.name = name;
}
sayName() { // same as Person.prototype.sayName
console.log(this.name);
}
}
let person = new PersonClass("Andrei");
person.sayName(); // outputs "Andrei"
console.log(person instanceof PersonClass); // true
console.log(person instanceof Object); // true
console.log(typeof PersonClass); // "function"
console.log(typeof PersonClass.prototype.sayName); // "function"
ES5 equivalent of a class
let Person = (function() {
"use strict";
const Person = function(name) {
if (typeof new.target === "undefined") {
throw new Error("Constructor must be called with new.");
}
this.name = name;
}
Object.defineProperty(Person.prototype, "sayName", {
value: function() {
if (typeof new.target !== "undefined") {
throw new Error("Method cannot be called with new.");
}
console.log(this.name);
},
enumerable: false,
writable: true,
configurable: true
});
return Person;
}());
Differences between classes and functions:
Class expressions
let PersonClass = class {
// equivalent of the PersonType constructor
constructor(name) {
this.name = name;
}
// equivalent of PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};
let person = new PersonClass("Andrei");
person.sayName(); // "Andrei"
console.log(person instanceof PersonClass); // true
console.log(person instanceof Object); // true
console.log(typeof PersonClass); // "function"
console.log(typeof PersonClass.prototype.sayName); // "function"
Class expressions
let PersonClass = class PersonClass2 {
// equivalent of the PersonType constructor
constructor(name) {
this.name = name;
}
// equivalent of PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};
console.log(typeof PersonClass); // "function"
console.log(typeof PersonClass2); // "undefined"
Accessor properties
class Person {
constructor(name) {
this.name = name;
}
get name() {
return this.name;
}
set name(value) {
this.name = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(Person.prototype, "name");
console.log("get" in descriptor); // true
console.log("set" in descriptor); // true
console.log(descriptor.enumerable); // false
Accessor properties
let Person = (function() {
"use strict";
const Person = function(name) {
if (typeof new.target === "undefined") {
throw new Error("Constructor must be called with new.");
}
this.name = name;
}
Object.defineProperty(CustomHTMLElement.prototype, "name", {
enumerable: false,
configurable: true,
get: function() {
return this.name;
},
set: function(value) {
this.name= value;
}
});
return Person;
}());
Static methods
function PersonType(name) {
this.name = name;
}
// static method
PersonType.create = function(name) {
return new PersonType(name);
};
// instance method
PersonType.prototype.sayName = function() {
console.log(this.name);
};
var person = PersonType.create("Andrei");
Static method
class PersonClass {
constructor(name) { // same as PersonType constructor
this.name = name;
}
sayName() { // same as PersonType.prototype.sayName
console.log(this.name);
}
// equivalent of PersonType.create
static create(name) {
return new PersonClass(name);
}
}
let person = PersonClass.create("Andrei");
Inheritance - ES5
function Rectangle(length, width) {
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function() {
return this.length * this.width;
};
function Square(length) {
Rectangle.call(this, length, length);
}
Square.prototype = Object.create(Rectangle.prototype, {
constructor: {
value:Square,
enumerable: true,
writable: true,
configurable: true
}
});
var square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
var square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
Inheritance - Classes
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
constructor(length) {
super(length, length); // same as Rectangle.call(this, length, length)
}
}
var square = new Square(3);
console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
Shadowing methods
class Square extends Rectangle {
constructor(length) {
super(length, length);
}
// override and shadow Rectangle.prototype.getArea()
getArea() {
return this.length * this.length;
}
}
class Square extends Rectangle {
constructor(length) {
super(length, length);
}
// override, shadow, and call Rectangle.prototype.getArea()
getArea() {
return super.getArea();
}
}
Abstract classes
// 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
Destructuring - the process of breaking a data structure down into smaller parts
let toDoItem = {
name: "Walk dog",
isDone: false
};
// extract data from the object
let name = toDoItem.name;
let isDone = toDoItem.isDone;
Destructuring objects
let toDoItem = {
name: "Walk dog",
isDone: false
};
let { name, isDone } = toDoItem;
console.log(name); // "Walk dog"
console.log(isDone); // false
// syntax error!
let { name, isDone };
Default values
let toDoItem = {
name: "Walk dog",
isDone: false
};
let { name, isDone, tag } = toDoItem;
console.log(name); // "Walk dog"
console.log(isDone); // false
console.log(tag); // undefined
let toDoItem = {
name: "Walk dog",
isDone: false
};
let { name, isDone, tag = "Home" } = toDoItem;
console.log(name); // "Walk dog"
console.log(isDone); // false
console.log(tag); // "Home"
Value names
let toDoItem = {
name: "Walk dog",
isDone: false
};
let { name: toDoName, isDone: doneState } = toDoItem;
console.log(toDoName); // "Walk dog"
console.log(doneState); // false
let toDoItem = {
name: "Walk dog"
};
let { name: toDoName, isDone: doneState = false } = toDoItem;
console.log(toDoName); // "Walk dog"
console.log(doneState); // false
Multilevel destructuring
let toDoItem = {
details: {
name: "Walk dog",
dueDate: "11.03.2017"
},
isDone: false
};
// extract toDoItem.details.name
let { details: { name: toDoName } } = toDoItem;
console.log(toDoName) // "Walk dog"
Array destructuring - works with position in array rather than name
let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
let colors = [ "red", [ "green", "lightgreen" ], "blue" ];
// later
let [ firstColor, [ secondColor ] ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
Array destructuring
let colors = [ "red", "green", "blue" ];
let [ , , thirdColor ] = colors;
console.log(thirdColor); // "blue"
let a = 1,
b = 2;
[ a, b ] = [ b, a ];
console.log(a); // 2
console.log(b); // 1
Mixed destructuring
let mixed = {
one: 1,
two: 2,
values: [3, 4, 5]
};
let { one: a, two: b, values: [c, , e] } = mixed;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(e); // 5
function con() {
console.log("TEST");
return 10;
}
let [x = con()] = []; // "TEST"
let [x = con()] = [1]; // ""
Lazy defaults
Destructured Function Parameters
const animal = {
name: 'Dog',
sound: 'wof'
};
function makeSound(options) {
options.name = options.name || 'animal';
console.log(`The ${options.animal} goes ${options.sound}`)
}
makeSound(animal);
const animal = {
name: 'Dog',
sound: 'wof'
};
function makeSound({name = 'animal', sound}) {
console.log(`The ${name} goes ${sound}`)
}
makeSound(animal);
Destructured Function Parameters
function setCookie(name, value,
{
secure = false,
path = "/",
domain = "example.com",
expires = new Date(Date.now() + 360000000)
} = {}
) {
// ...
}
Unnamed Parameters in ES5
function pick(object) {
let result = Object.create(null);
// start at the second parameter
for (let i = 1, len = arguments.length; i < len; i++) {
result[arguments[i]] = object[arguments[i]];
}
return result;
}
let book = {
title: "Understanding ECMAScript 6",
author: "Nicholas C. Zakas",
year: 2015
};
let bookData = pick(book, "author", "year");
console.log(bookData.author); // "Nicholas C. Zakas"
console.log(bookData.year); // 2015
Rest Parameter - is indicated by three dots (...) preceding a named parameter
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;
}
Rest Parameter restrictions
function pick(object, ...keys, last) {
let result = Object.create(null);
for (let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}
let object = {
set name(...value) {
// do something
}
};
Rest items
let colors = [ "red", "green", "blue" ];
let [ firstColor, ...restColors ] = colors;
console.log(firstColor); // "red"
console.log(restColors.length); // 2
console.log(restColors[0]); // "green"
console.log(restColors[1]); // "blue"
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;
console.log(clonedColors); //"[red,green,blue]"
Rest properties (ES2018)
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
Spread operator
function logNumbers(a, b, c) {
console.log(a)
console.log(b)
console.log(c)
}
const array = [1,2,3];
logNumbers(...array); // 1 2 3
var args = [0, 1, 2];
logNumbers.apply(null, args);
Spread operator - array
const array = [1,2,3];
console.log([...array, 4, 5]); // [1, 2, 3, 4, 5]
let set = new Set([1, 2, 3, 3, 3, 4, 5]),
array = [...set];
console.log(array); // [1,2,3,4,5]
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Spread operator - array
Array operations
const arr1 = [1,2,3];
const arr2 = [2,3,4];
const arr3 = [...arr1, 8, ...arr2];
console.log(arr3); // [1, 2, 3, 8, 2, 3, 4]
const arr1 = [1,2,3];
const arr2 = [...arr1]
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false
Spread properties (ES2018)
const obj1 = {a: 'a', b: 'b'};
const obj2 = {c: 'c', ...obj1};
console.log(obj2); // {a: 'a', b: 'b', c: 'c'}
const obj1 = {a: 'a', b: 'b', c: 'c'};
const obj2 = {c: 'd', ...obj1};
console.log(obj2); // {a: 'a', b: 'b', c: 'd'}
Modules - ES6 way to solve the "everything is in one scope" problem
Exporting - using the export keyword
export var color = "red";
export let person = { name : "Andrei" };
export const value = 7;
export function sum(num1, num2) {
return num1 + num1;
}
export class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
}
Exporting - using the export keyword
// this function is private to the module
function subtract(num1, num2) {
return num1 - num2;
}
function multiply(num1, num2) {
return num1 * num2;
}
export { multiply };
Importing - using the import keyword
import { sum } from "./example.js";
console.log(sum(1, 2)); // 3
sum = 1; // error
import * as example from "./example.js";
console.log(example.sum(1, example.number)); // 8
console.log(example.multiply(1, 2)); // 2
import { sum, multiply, number } from "./example.js";
console.log(sum(1, number)); // 8
console.log(multiply(1, 2)); // 2
Importing - module gets executed only once
import { sum } from "./example.js";
import { multiply } from "./example.js";
import { number } from "./example.js";
if (flag) {
export flag; // syntax error
}
function tryImport() {
import flag from "./example.js"; // syntax error
}
Modifying import bindings
// example.js
export var name = "Andrei";
export function setName(newName) {
name = newName;
}
import { name, setName } from "./example.js";
console.log(name); // "Andrei"
setName("Alex");
console.log(name); // "Alex"
name = "Nicholas"; // error
Renaming Exports and Imports
function sum(num1, num2) {
return num1 + num2;
}
export { sum as add };
import { add as sum } from "./example.js";
console.log(typeof add); // "undefined"
console.log(sum(1, 2)); // 3
Default values
export default function(num1, num2) {
return num1 + num2;
}
function sum(num1, num2) {
return num1 + num2;
}
export { sum as default };
import sum from "./example.js";
console.log(sum(1, 2)); // 3
export let color = "red";
export default function(num1, num2) {
return num1 + num2;
}
import sum, { color } from "./example.js";
console.log(sum(1, 2)); // 3
console.log(color); // "red"
Importing Without Bindings
// example.js
Array.prototype.pushAll = function(items) {
// items must be an array
if (!Array.isArray(items)) {
throw new TypeError("Argument must be an array.");
}
// use built-in push() and spread operator
return this.push(...items);
};
import "./example.js";
let colors = ["red", "green", "blue"];
let items = [];
items.pushAll(colors);
Using Modules in Web Browsers
<!-- load a module JavaScript file -->
<script type="module" src="module.js"></script>
<!-- include a module inline -->
<script type="module">
import { sum } from "./example.js";
let result = sum(1, 2);
</script>
The for loop
var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i]);
}
Iterators
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
};
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// for all further calls
console.log(iterator.next()); // "{ value: undefined, done: true }"
Generators - functions that return an iterator
// generator
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
// generators are called like regular functions but return an iterator
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
// for all further calls
console.log(iterator.next().value); // undefined
Generator Function Expressions
let createIterator = function *(items) {
for (let i = 0; i < items.length; i++) {
yield items[i];
}
};
let iterator = createIterator([1, 2, 3]);
var o = {
createIterator: function *(items) {
for (let i = 0; i < items.length; i++) {
yield items[i];
}
}
};
let iterator = o.createIterator([1, 2, 3]);
Generator Object Methods
The for-of loop
let values = [1, 2, 3];
for (let num of values) {
console.log(num);
}
// 1
// 2
// 3
let divs = document.getElementsByTagName("div"); // NodeList Iterator
for (let div of divs) {
console.log(div.id);
}
Accessing the Default Iterator
let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
function isIterable(object) {
return typeof object[Symbol.iterator] === "function";
}
console.log(isIterable([1, 2, 3])); // true
console.log(isIterable("Hello")); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false
Detecting if an object is iterable
Creating iterables - make an object iterable by creating a Symbol.iterator property containing a generator
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}
// 1
// 2
// 3
Passing Arguments to Iterators
function *createIterator() {
let first = yield 1;
let second = yield first + 2; // 4 + 2
yield second + 3; // 5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.next(5)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
Throwing Errors in Iterators
function *createIterator() {
let first = yield 1;
let second = yield first + 2; // yield 4 + 2, then throw
yield second + 3; // never is executed
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // error thrown from generator
Generator Return Statements
function *createIterator() {
yield 1;
return;
yield 2;
yield 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
function *createIterator() {
yield 1;
return 42;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 42, done: true }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
Asynchronous task running
run(function *() {
const url = 'https://jsonplaceholder.typicode.com/';
let response = yield fetch(`${url}posts/1`);
const post = yield response.json();
const userId = post.userId;
response = yield fetch(`${url}users/${userId}`);
const user = yield response.json();
return user.name;
})
.then(name => console.log(name));
function run(generator) {
const iterator = generator();
function iterate(iteration) {
if(iteration.done) return iteration.value;
const promise = iteration.value;
return promise.then( p => iterate(iterator.next(p)) );
}
return iterate(iterator.next());
Async await (ES2016)
async function getName() {
const url = 'https://jsonplaceholder.typicode.com/';
let response = await fetch(`${url}posts/-1`);
const post = await response.json();
const userId = post.userId;
response = await fetch(`${url}users/${userId}`);
const user = await response.json();
return user.name;
}
getName()
.then(name => console.log(name))
.catch(error => console.log('error'));
let isDone: boolean = false;
let height: number = 6;
let name: string = "bob";
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
class Person {
nickName?: string;
private name: string;
public title: string;
protected furColor: Color;
public static maximumAge: number = 85;
constructor(name: string) {
...
}
}
const p = new Person("Andrei");
interface ILog {
LogMessage(message: string) : void;
}
class ConsoleLog implements ILog {
public LogMessage(message: string) : void {
console.log(message);
}
}
class Calculator {
private log: ILog;
constructor(log: ILog) {
this.log = log;
}
add(x: number, y: number) : number {
this.log.LogMessage(`Adding {x} and {y}.`);
return x+y;
}
}
class GenericQueue<T> {
enqueue(value: T) : void {/*enqueue value */}
dequeue() : T {/*dequeue value*/}
}
let q2 = new GenericQueue<string>();
q2.enqueue("Hello");
q2.enqueue(17); // compilation error
let q3 = new GenericQueue<Animal>();
q3.enqueue(new Animal("Max"));
q3.enqueue("Hello"); // compilation error
function ClassDecoratorParams(param1: number, param2: string) {
return function(
target: Function // The class the decorator is declared on
) {
console.log("ClassDecoratorParams(" + param1 + ", '" +
param2 + "') called on: ", target);
}
}
@ClassDecoratorParams(1, "a")
@ClassDecoratorParams(2, "b")
class ClassDecoratorParamsExample {
}
ClassDecoratorParams(2, 'b') called on: function ClassDecoratorParamsExample() {
}
ClassDecoratorParams(1, 'a') called on: function ClassDecoratorParamsExample() {
}
Values end up in resulting JavaScript, types don't
let animal; // declares only a value
interface Mamal {} // declares a type
class Dog {} // declares a value and a type
Structural typing
interface Person {
age: number;
}
class Andrei implements Person {
constructor(public age: number) {}
}
let pers1: Person = new Andrei(32);
let pers2: Andrei = {age: 32};
All types are expressible as interfaces
interface Node {
(x: string): string; // callable
new (label: string): Node; // constructor
children: Node[]; // property
numChildren(): number; // method
}
Index signatures
interface Data {
[key: string]: boolean;
}
const d: Data = {};
d['key1'] = true;
console.log(d['key1']); // true
Union: the union of the posible values
type primitive = string | number | boolean;
Intersection: the intersection of possible values
type MyDiv = HTMLDivElement & {customDivAttr: string};
Narrowing: avioding casts
type el = HTMLAnchorElement | HTMLImageElement;
const location = (d: el) => {
if(d instanceof HTMLAnchorElement) {
return d.href;
}
return d.src;
}
Optional arguments
function one(x: string | undefined) { ... }
one();
// Error: requires one argument
function two(x?: string) { ... }
two();
// It's ok
Enums assign a numbers/strings to each symbol
enum Card {
CLUBS = 1, // Subsequent values are incrm. from 1
DIAMONDS,
HEARTS,
SPADES
}
enum Colors {
RED = '#FF0000',
GREEN = '#00FF00',
BLUE = '#0000FF'
}
let myCard = Card.HEARTS; // 3
let red = Colors.RED; // #FF0000
class GenericQueue<T> {
enqueue(value: T) : void {/*enqueue value */}
dequeue() : T {/*dequeue value*/}
}
let q2 = new GenericQueue<string>();
q2.enqueue("Hello");
q2.enqueue(17); // compilation error
let q3 = new GenericQueue<Animal>();
q3.enqueue(new Animal("Max"));
q3.enqueue("Hello"); // compilation error