JavaScript

Part 2

Let me introduce myself

Roman

Senior Front End Developer at TechMagic

 

 

@graniron         

roman.yavoriv@techmagic.co

graniron

 

Agenda

  1. Closure
  2. Error handling
  3. Prototype and Inheritance
  4. Classes
  5. Async
  6. 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).
- c
onstructors 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