Andrei Antal
Angular Workshop
ES6 and TypeScript
Angular Workshop
Contents
1.
JAVASCRIPT HISTORY
JavaScript History
- 1991
- CERN - Tim Berners-Lee - WWW
- 1993
- Mosaic - the first modern browser
- 1994
- Mosaic Netscape (codename Mozilla - Mosaic killer)
- Renamed to Netscape Navigator
- 1995
- Brendan Eich - Mocha - first prototype version of JavaScript (done in only 10 days!)
- It first shipped as LiveScript than renamed to JavaScript
- 1996
- JScript - Microsoft's reversed engineered version of JavaScript shipped with Internet Explorer 3
- beginning of the browser wars
- Netscape takes JavaScript to ECMA for standardization
- 1997
- ECMA-262 (ECMAScript)
-
1998
- ECMAScript 2
-
1999
- ECMAScript 3
JavaScript History
- 2004
- Firefox was released by Mozilla
- 2005
- Mozilla (Brendan Eich) and Macromedia began to work on ECMAScript 4 => ActionScript 3
- ES4 was never completed
- Ajax is invented
-
2007
- ECMAScript 3.1
- 2009
- ES5 emerges from previous discussions
JavaScript History
-
2011
- ES5.1
- 2015
- June - ES6 (ES2015)
- 2016
- June - ES2016
To be continued...
JavaScript History
BLOCK SCOPE VARIABLES
Block scope variables
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;
}
}
Block scope variables
function getColor(condition) {
if (condition) {
var value = "blue";
// ...
return value;
} else {
// value = undefined
return null;
}
// value = undefined
}
ES5 variable hoisting
- variables are hoisted to the top of the enclosing function
Block scope variables
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....
});
Block scope variables
- Block scope declarations - variables that are inaccessible outside of a given block scope
-
Block scopes (lexical scopes)
- Inside of a function
- Inside of a block (between { and } )
- ES6 introduces 2 new type of variable declarations:
- let
- const
Block scope variables
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
}
- The variable is only accessible inside its enclosing block
- A ReferenceError is thrown if we try to access the variable outside of its scope
Block scope variables
Redeclarations
var count = 30;
// Syntax error
let count = 40;
- let identifier cannot be redefined inside a scope
- It's ok to make a let declaration if it creates a new variable in the scope
var count = 30;
// Does not throw an error
if (condition) {
let count = 40; // shadows the outer variable inside this scope
// more code
}
- a new count variable is created that shadows the former
Block scope variables
The CONST declarations
// Valid constant
const maxItems = 30;
// Syntax error: missing initialization
const name;
- Variables of which the content (or reference) does not change (not necessarily immutable)
- For this reason they need to be initialized at declaration
- cannot redeclare variables in the same scope
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
Block scope variables
The CONST declarations
const person = {
name: "Andrei"
};
// works
person.name = "George";
// throws an error - reinitialization
person = {
name: "George"
};
- A const declaration prevents modification of the binding and not of the value itself.
Block scope variables
Temporal Dead Zone (TDZ)
if (condition) {
console.log(typeof value); // ReferenceError!
let value = "blue";
}
- A variable declared with either let or const cannot be accessed until after the declaration.
console.log(typeof value); // "undefined"
if (condition) {
let value = "blue";
}
Block scope variables
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);
});
}
- The let declaration creates a new variable i each time , so each function created inside the loop gets its own copy of i
// i is not accessible here - throws an error
console.log(i);
funcs.forEach(function(func) {
func(); // 0, 1, 2, 3,...
})
Block scope variables
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);
});
}
TEMPLATE STRINGS
Template strings
New string methods :
- includes() - check if the given text is found anywhere within the string.
- startsWith() - check if the given text is found at the beginning of the string
- endsWith() - check if the given text is found at the end of the string
- repeat() - returns a new string containing the original string repeated the specified number of times.
Template strings
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 strings
Template literals
- Multiline strings A formal concept of multiline strings.
- Basic string formatting The ability to substitute parts of the string for values contained in variables.
- HTML escaping The ability to transform a string such that it is safe to insert into HTML.
Template strings
Template literals
- strings delimited by backticks (`) instead of double or single quotes.
let message = `Hello world!`;
console.log(message); // "Hello world!"
console.log(typeof message); // "string"
console.log(message.length); // 12
Template strings
Multiline strings
// old syntax bug
var message = "Multiline \
string";
console.log(message); // "Multiline string"
- ES5 multiline strings
var message = "Multiline \n\
string";
console.log(message); // "Multiline
// string"
var message = [
"Multiline ",
"string"
].join("\n");
let message = "Multiline \n" +
"string";
Template strings
Multiline strings
let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 31
- Preserve newlines
- Preserve tabs
let message = `Multiline
string`;
console.log(message); // "Multiline
// string"
console.log(message.length); // 16
Template strings
Multiline strings
- Better HTML strings
- ES5
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>";
- ES6
Template strings
Substitutions
- Substitutions are delimited by ${ and } and can have any JavaScript expression inside.
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."
Template strings
Tagged Templates
let message = tag`Hello world`;
function tag(literals, ...substitutions) {
// return a string
}
- literals - an array containing the literal strings as interpreted by JavaScript
- substitutions - the interpreted value of each substitution
- perform a transformation on the template literal and returns the final string value
Template strings
Tagged Templates
let count = 10,
price = 0.25,
message = passthru`${count} items cost $${(count * price).toFixed(2)}.`;
literals
- The empty string before the first substitution ("")
- The string after the first substitution and before the second (" items cost $")
- The string after the second substitution (".")
substitutions
- 10 (count)
- 2.25 ( (count*price).toFixed(2) )
Template strings
Tagged Templates
let message = tag`Hello world`;
function tag(literals, ...substitutions) {
// return a string
}
ENHANCED OBJECTS
Enhanced objects
Object Literal Syntax Extensions
- Property Initializer Shorthand
- ES5
function createPerson(name, age) {
return {
name: name,
age: age
};
}
-
- ES6
function createPerson(name, age) {
return {
name,
age
};
}
Enhanced objects
Object Literal Syntax Extensions
- Concise Methods
- ES5
var person = {
name: "Nicholas",
sayName: function() {
console.log(this.name);
}
};
-
- ES6
var person = {
name: "Nicholas",
sayName() {
console.log(this.name);
}
};
Enhanced objects
Object Literal Syntax Extensions
- Formal Method Definition
let person = {
// method
getGreeting() {
return "Hello";
}
};
// not a method
function shareGreeting() {
return "Hi!";
}
- A method is a function that has an internal [[HomeObject]] property containing the object to which the method belongs
Enhanced objects
Object Literal Syntax Extensions
- Using super() for prototype calls
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!"
Enhanced objects
Object Literal Syntax Extensions
- Computed property names
- ES5
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";
Enhanced objects
Object Literal Syntax Extensions
- Computed property names
- ES6
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"
Enhanced objects
New methods
- Object.is() - fix the remaining quirks of the identically equals operator
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
Enhanced objects
New methods
- Object.assign() - copy the values of all enumerable own properties from one or more source objects to a target object
Object.assign(target, ...sources)
- Merging objects
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.
Enhanced objects
Object.assign()
- Merging objects with same properties
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 }
- Cloning object
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
ARROW FUNCTIONS
Arrow functions
-
Functions defined with a new syntax that uses an “arrow” (=>)
-
Difference from "classic" functions
- Cannot be called with new - no new.target binding
- No prototype - no super binding
- Can’t change this
- No arguments object
- No duplicate named parameters
- The value of this, super, arguments, and new.target inside of the function is by the closest containing non-arrow function.
Arrow functions
Arrow function syntax
var reflect = value => value;
var reflect = (value) => { return value };
// ES5 equivalent function
var reflect = function(value) {
return value;
};
- no parenthesis if only 1 argument
var reflect = (value) => { return value };
- no return or {} pair if only one statement
Arrow functions
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;
};
- paranthesis needed for 2 or more arguments
- for no arguments, () is requires
Arrow functions
Arrow function syntax
var getTempItem = id => ({ id: id, name: "Temp" });
// effectively equivalent to:
var getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
- if we return the object, we need to wrap it in ( )
var comparator = (a, b) => a - b;
console.log(typeof comparator); // "function"
console.log(comparator instanceof Function); // true
- arrow functions are still functions
Arrow functions
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();
- event listeners usually bind this to the target of the event
Arrow functions
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();
- we can change the this binding (among other ways) by using the .bind() method
Arrow functions
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 () { ... };
- we can also keep a reference to this
Arrow functions
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 functions don't bind to this - the value of this inside an arrow function can only be determined by looking up the scope chain
Arrow functions
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 methods
- Arrow functions
CLASSES
Classes
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"
Classes
Prototypes
Classes
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"
- Own properties (properties that occur on the instance rather than the prototype) can only be created inside a class constructor or method.
Classes
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;
}());
Classes
Differences between classes and functions:
- Class declarations, unlike function declarations, are not hoisted
- All code inside of class declarations runs in strict mode automatically
- All methods are non-enumerable
- All methods will throw an error if you try to call them with new
- Calling the class constructor without new throws an error
- Attempting to overwrite the class name within a class method throws an error
Classes
Class expressions
- Anonymus classes
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"
Classes
Class expressions
- Named 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"
Classes
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
Classes
Accessor properties
- ES5 equivalent
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;
}());
Classes
Static methods
- ES5 functions
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");
Classes
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");
Classes
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
Classes
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
- Derived classes require you to use super() if you specify a constructor
Classes
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();
}
}
- You can call the base class version of the method by using the super
Classes
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
Destructuring
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;
- ES5 - geting data into local variables
Destructuring
Destructuring objects
let toDoItem = {
name: "Walk dog",
isDone: false
};
let { name, isDone } = toDoItem;
console.log(name); // "Walk dog"
console.log(isDone); // false
- ES6
- destructuring requires initialization
// syntax error!
let { name, isDone };
Destructuring
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"
Destructuring
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
- with default values
Destructuring
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"
Destructuring
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"
- nested destructuring
Destructuring
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
- neat trick - swapping values
Destructuring
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
Destructuring
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);
Destructuring
Destructured Function Parameters
function setCookie(name, value,
{
secure = false,
path = "/",
domain = "example.com",
expires = new Date(Date.now() + 360000000)
} = {}
) {
// ...
}
SPREAD AND REST
Spread and rest
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
Spread and rest
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;
}
- The rest parameter becomes an Array containing the rest of the parameters passed to the function
- It allow you to specify that multiple independent arguments should be combined into an array
Spread and rest
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;
}
- Can't use rest parameter in setter
- Can't have a named parameter after rest parameters
let object = {
set name(...value) {
// do something
}
};
Spread and rest
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]"
- Cloning an array
Spread and rest
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 }
- Rest properties collect the remaining own enumerable property keys that are not already picked off by the destructuring pattern
Spread and rest
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
- the spread operator allows you to specify an array that should be split and have its items passed in as separate arguments to a function
- replacement for apply
var args = [0, 1, 2];
logNumbers.apply(null, args);
Spread and rest
Spread operator - array
const array = [1,2,3];
console.log([...array, 4, 5]); // [1, 2, 3, 4, 5]
- Convert a Set into an array
let set = new Set([1, 2, 3, 3, 3, 4, 5]),
array = [...set];
console.log(array); // [1,2,3,4,5]
- You can use the spread operator to work on any iterable objects
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Spread and rest
Spread operator - array
Array operations
- concatenation
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]
- copy array
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 and rest
Spread properties (ES2018)
const obj1 = {a: 'a', b: 'b'};
const obj2 = {c: 'c', ...obj1};
console.log(obj2); // {a: 'a', b: 'b', c: 'c'}
- in case of multiple same-named properties, the last one wins out
const obj1 = {a: 'a', b: 'b', c: 'c'};
const obj2 = {c: 'd', ...obj1};
console.log(obj2); // {a: 'a', b: 'b', c: 'd'}
MODULES
Modules
Modules - ES6 way to solve the "everything is in one scope" problem
- Module code automatically runs in strict mode
- Variables created in the top level of a module aren’t automatically added to the shared global scope.
- The value of this in the top level of a module is undefined.
- Modules must export anything that should be available to code outside of the module.
- Modules may import bindings from other modules.
Modules
Exporting - using the export keyword
export var color = "red";
export let person = { name : "Andrei" };
export const value = 7;
- Export data
export function sum(num1, num2) {
return num1 + num1;
}
- Export functions
export class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
}
- Export functions
Modules
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 };
- Export eferences
Modules
Importing - using the import keyword
import { sum } from "./example.js";
console.log(sum(1, 2)); // 3
sum = 1; // error
- Importing a single binding
import * as example from "./example.js";
console.log(example.sum(1, example.number)); // 8
console.log(example.multiply(1, 2)); // 2
- Importing multiple bindings
import { sum, multiply, number } from "./example.js";
console.log(sum(1, number)); // 8
console.log(multiply(1, 2)); // 2
- Importing all of the module
Modules
Importing - module gets executed only once
import { sum } from "./example.js";
import { multiply } from "./example.js";
import { number } from "./example.js";
- import/export keywords must be used outside statements
if (flag) {
export flag; // syntax error
}
function tryImport() {
import flag from "./example.js"; // syntax error
}
Modules
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
Modules
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
Modules
Default values
export default function(num1, num2) {
return num1 + num2;
}
function sum(num1, num2) {
return num1 + num2;
}
export { sum as default };
- Exporting
import sum from "./example.js";
console.log(sum(1, 2)); // 3
- Importing
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"
Modules
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);
Modules
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>
ITERATORS
Iterators and generators
The for loop
var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i]);
}
- Tend to get complicated when complex logic and nesting is required
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 }"
Iterators and generators
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
- The * before the function name makes the function a generator.
- The yield keyword specifies values the resulting iterator should return when next() is called
Iterators and generators
Generator Function Expressions
let createIterator = function *(items) {
for (let i = 0; i < items.length; i++) {
yield items[i];
}
};
let iterator = createIterator([1, 2, 3]);
- Creating an arrow function that is also a generator is not possible.
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
Iterators and generators
The for-of loop
let values = [1, 2, 3];
for (let num of values) {
console.log(num);
}
// 1
// 2
// 3
- A for-of loop calls next() on an iterable
- This for-of loop first calls the Symbol.iterator method on the values array to retrieve an iterator.
- The for-of statement will throw an error when used on, a non-iterable object, null, or undefined.
let divs = document.getElementsByTagName("div"); // NodeList Iterator
for (let div of divs) {
console.log(div.id);
}
Iterators and generators
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
Iterators and generators
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
Iterators and generators
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 }"
Iterators and generators
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
Iterators and generators
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 }"
- Stop execution
- Specify last value
Iterators and generators
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());
Iterators and generators
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'));
Iterators and generators
OTHER ES6 FEATURES
Other ES6 Features
- Set and Map
- Symbols
- Promises
- Unicode characters
- Improved Arrays
- New Math methods
- Proxies
- Reflection API
- Tail call optimization
TYPESCRIPT OVERVIEW
TypeScript
- Superset of ECMAScript 2015 (ES6)
- Open Source
- Anders Hejlsberg @ Microsoft, 2012
Static styping
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
Classes ++
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");
Interfaces
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;
}
}
Generics
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
Decorators
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() {
}
- Functions that are invoked with a prefixed @ symbol, and immediately followed by a class, parameter, method or property.
Angular Workshop - Typescript and ES6
By Andrei Antal
Angular Workshop - Typescript and ES6
- 1,389