JavaScript
Part 2


Let me introduce myself


Roman
Senior Front End Developer at TechMagic
@graniron
roman.yavoriv@techmagic.co
graniron


Agenda


- Closure
- Error handling
- Prototype and Inheritance
- Classes
- Async
- Modules
Closure



Closure


A closure is a function that remembers its outer variables and can access them
Closure


function outer() {
let count = 0;
return function inner() {
return ++count;
};
}
let counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
Lexical Environment


function outer() {
let count = 0;
return function inner() {
return ++count;
};
}
let counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
// inner Lexical Environment
{}
// outer Lexical Environment
{ count: 0, inner: fn }
// global Lexical Environment
{ outer: fn, counter: fn, ... }
Lexical Environment


// inner Lexical Environment
{}
// outer Lexical Environment
{ count: 0, inner: fn }
// global Lexical Environment
{ outer: fn, counter: fn, ... }
When the code wants to access a variable:
1) the inner Lexical Environment is searched;
2) then the outer Lexical Environment is searched;
...
n) the global Lexical Environment is searched;
n + 1) JS engine gives up
function outer() {
let count = 0;
return function inner() {
return ++count;
};
}
let counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
Error Handling



Error Handling


Each error should be handled
Unhandled error kills script
Error Handling Cases


Network request
Internal script execution which can cause an error
Error Handling Methods


try {
// try running the code...
} catch (err) {
// handle possible error
} finally {
// execute always ...
}
new Promise((resolve, reject) => {
// resolve or reject
}).catch((err) => {
// handle posible error
});
Error object


message – the human-readable error message.
name – the string with error name (error constructor name).
stack (non-standard, but well-supported) – the stack at the moment of error creation.
Prototypal Inheritance



Prototype


In JavaScript, all objects have a hidden [[Prototype]] property
It points to the object from which inheritance is made
const user = { name: "Jonh", age: 22 };
console.log(user.__proto__);
console.log(Object.getPrototypeOf(user));
Prototypal Inheritance


If we want to read a property of obj or call a method, and it doesn’t exist, then JavaScript tries to find it in the prototype

function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`)
}
const person1 = new Person('John', 22);
person1.greet();
Prototype and Function


Every function has the "prototype" property
The Person.prototype property sets __proto__ of new objects when new Person() is called.
The "prototype" property only has such a special effect when set on a constructor function, and invoked with new.
function Person() { ... }
Person.prototype // {constructor: ƒ}
const person1 = new Person('John', 22);
person1.__proto__ === Person.prototype // true
Set prototype


// extend base class
class User extends Person { ... }
// with function contructor
function Person() { ... }
new Person();
// with setPrototypeOf
Object.setPrototypeOf(obj, prototype);
// with Object constructor
new Object(prototype);
Native inheritance


const now = new Date(); // Date.prototype
const arr = [1, 2, 3]; // Array.prototype
const emailRegex = new RegExp(...); // RegExp.prototype
const name = 'John'; // String.prototype
const age = 22; // Number.prototype
Classes



Classes


Object creation
- Object literal
- Constructor function
- Class constructor
Classes


class MyClass {
name = 'John'; // property
constructor(...) { // constructor
// ...
}
method(...) {} // method
get something(...) {} // getter method
set something(...) {} // setter method
}
class User {
canFly = false;
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello my name is ${this.name}`)
}
}
const user1 = new User('John');
user1.greet();
Extending Class


class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
stop() {
this.speed = 0;
alert(`${this.name} stands still.`);
}
}
class Rabbit extends Animal {
hide() {
alert(`${this.name} hides!`);
}
}
Rabbit.prototype.__proto__ will be Animal.prototype, so methods are inherited.
Super


- super.method(...) - call a parent method.
- super(...) - call a parent constructor (inside our constructor only).
- constructors in inheriting classes must call super(...), and (!) do it before using this.
Super


class Rabbit extends Animal {
// ...
}
class Rabbit extends Animal {
constructor(name, earLength) {
super(name);
this.earLength = earLength;
}
// ...
}
class Animal {
run() {
console.log('run')
}
}
class Rabbit extends Animal {
onDanger() {
super.run();
}
}
Home reading


Async JS



Handle async operations


- Callbacks
- Promises
- Async/await
JS Thread


JS has only one thread
console.log(1);
setTimeout(() => {
console.log(2);
}, 1000);
console.log(3);
1
3
2
Callbacks


function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
setTimout(cb, 1000);
setInterval(cb, 1000);
Callbacks and Erros


- The first argument of the callback is reserved for an error if it occurs. Then callback(err) is called.
- The second argument (and the next ones if needed) are for the successful result. Then callback(null, result1, result2…) is called.
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
loadScript('/my/script.js', function(error, script) {
if (error) {
// handle error
} else {
// script loaded successfully
}
});
Promises


new Promise(function(resolve, reject) {
// do staff, resolve(value) or reject(error)
});



promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
promise
.then(function(result) {...})
.catch(function(err) { ... ))
.finaly(function() { ... })
promise.catch(function(err) { /* handle an error */ });
Chaining


const promise = new Promise((resolve, reject) => {
resolve(1);
})
promise
.then((res) => {
console.log(res); //1
return res * 2;
})
.then((val) => {
console.log(val); // 2
return val * 2;
})
.then((val) => {
console.log(val); // 4
})
.finaly(() => console.log('done'))
Promise API


// wait for all to resolve
Promise.all(arrayOfPromises);
// wait for all to setl
Promise.allSettled(arrayOfPromises);
// wait for the first(fastest) to resolve or reject
Promise.race(arrayOfPromises);
// makes a resolved promise with the given value
Promise.resolve(value);
// makes a rejected promise with the given error
Promise.reject(error);
Async/await


async function f() {
return 1;
}
f().then(alert); // 1
The word “async” before a function means one simple thing: a function always returns a promise
Async/await


// works only inside async functions
let value = await promise;
async function getUsers() {
let response1 = await fetch('/api/user1');
let user1 = await response.json();
let response2 = await fetch('/api/user2');
let user2 = await response.json();
return [user1, user2];
}
getUsers()
.then((users) =>
{ ... }
);
Async/await and Errors


async function getUsers() {
...
}
getUsers()
.then((users) => { ... })
.catch((err) => { ... })
async function getUsers() {
try {
let response1 = await fetch('/api/user1');
let user1 = await response.json();
let response2 = await fetch('/api/user2');
let user2 = await response.json();
return [user1, user2];
} catch (err) { ... }
}
Home reading


Modules



Modules


A module is just a file.
One script is one module
// sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
export - expose what you would like to share
import - get file by the path
// main.js
import {sayHi} from './sayHi.js';
alert(sayHi); // function...
sayHi('John'); // Hello, John!
Modules work only via HTTP(s), not in local files
Modules


Top-level variables and functions from a module are not seen in other scripts.
The module is executed only once. Exports are generated, and then they are shared between importers
Questions



Resources


Homework


js-advanced
By Roman Yavoriv
js-advanced
- 533