Friday, February 5, 2021

Asynchronous Programming

A promise is an action that produces a result at some point in the future, unless it dies with an exception.
To combine two promises, you need to use method calls. Instead, the async/await constructs give you a simpler syntax that has the ordinary sequential flow.

1 JavaScript Concurrent Programming

What is a concurrent program?

  • a program is concurrent if it controls multiple activities which execute at same time, with overlapping timelines

What is I/O operations?

  • I/O (input/output) operations are also known as time-consuming operation.
  • I/O operaions occurr when the program interacts with the system’s disk and network.
  • I/O operations include reading/writing data from/to a disk, making HTTP requests and querying databases.

Concurrent programs in Java and C++

  • these programming languages are multithreaded
  • a program has a main thread, time-consuming operations are handled in a separate thread of execution
  • if multiple threads modify shared data, access to data must be regulated to prevent data corruption

Concurrent programs in JavaScript

  • JavaScript programs run in a single thread. If a function starts it will have to complete its execution, before another function is able to start
  • if a JavaScript program has to wait for something to happen, it cannot do something else in another thread
  • if a function modifies some shared data, other functions cannot access this data before the function returns.
    This means that in your javascript programs you don’t have to worry about synchronizations and mutex locks.

How JavaScript process time-consuming operations?

  • JavaScript has three features that allow you to run code in concurrently: callbacks, promises and async/await construct
  • time-consuming operations in JavaScript are asynchronous, also known as non-blocking
  • for example a program that is waiting for a HTTP response specifies a callback function; the current function continues execution and the callback is invoked when data is available

Example: a function that loads a JSON object from a given url

const loadJSON = url => {
  const request = new XMLHttpRequest();
  request.addEventListener('load', (e) => {
    if (request.status === 200) { // status code for success
      let json = JSON.parse(request.responseText);
      console.log({json})
    } else { 
      console.error(`Server responded with a ${request.status}`)
    }
  });
  request.addEventListener('error', (e) => console.error("NetworkError"));
  request.addEventListener('timeout', (e) => console.error("Timeout!!"));    
  request.open('GET', url, true);
  request.send();
};  

Note to the example:

  • the function uses the XMLHttpRequest API
  • the response's payload is processed sometime later in the asynchronous callback
  • an invocation to the loadJSON function returns immediately and does not wait for the data to load

Callbacks inside callbacks or Callbacks Hell

  • Doing tasks in order requires callbacks inside callbacks
  • Consider a situation where your program loads a page which contains the URL of an image and then it loads the image as well: this leads to nested calls

Promises VS Callbacks

  • promises allow to combine asynchronous tasks without nesting callbacks
  • promises make it easy to link completion and error actions than callbacks

2 Making Promises

How to build promises

  • you can make promises either by invoking APIs that yield promises or by invoking the Promise constructor
  • note: it is more likely that you use libraries that return promises, than you invoke the Promise constructor for making your own promises

Example: a fetch call yields a promise that resolves with a Response object

// fetch JSON for client IP address
let promise = fetch('https://api.ipify.org?format=jsonp&callback=?');
console.log(promise); // Promise {<pending>}

// fetch image file
promise = fetch('https://upload.wikimedia.org/wikipedia/commons/d/d9/Wikipedia_Monument_2.JPG');
console.log(promise); // Promise {<pending>}

Building promises invoking the Promise constructor, a summary of the process

const myPromise = new Promise((resolve, reject) => {
  const callback = (args) => {
    if (success) resolve(result) 
    else reject(reason)
  }
  asynTask(callback)
})
  • the Promise constructor takes as its argument a function, called executor function
  • the executor function takes two parameters, which are handler functions
  • In the body of the executor function you start the async task:
    • if the task yields a result, the result is passed to the resolve handler
    • if the task fails, the reject handler is invoked passing the reason of the failure

Example: the promiseSum promise factory function

// A factory that yields a promise that resolves with the sum two given numbers
function promiseSum(add1, add2, delay = 1000) {
  return new Promise((resolve, reject) => {
    const callback = () => {
      let sum = add1+add2;
      resolve(sum);
    }
    setTimeout(callback, delay);
  })
}

Note to the example

  • the executor function invokes setTimeout passing a callback and a delay.
    After the delay has passed, the callback is invoked, which passes the result to the resolve handler

Promise Control Flow

  • the life cycle of a js promise: from the promise's creation to the promise's result production
  • The Promise() constructor is called.
    • the JavaScript interpreter invokes the constructor argument: the executor function is called, being passed resolve and reject
    • the executor function set up the asynchronous task and returns
    • the constructor returns.
    • at this point, the promise is in the pending state.
  • some time passes ...
  • the async task completes and invokes a callback that you provided in the executor function
    • if in that callback you have a call to reject handler, the promise is rejected and settled
    • if in the callback you have a call to resolve handler with an object other than a promise, then the promise is fulfilled and settled
    • if in the callback you have a call to resolve with another promise, then the current promise is resolved but not fulfilled

3 Handling promises using methods

The Promise.prototype.then(onFulfilled) method allows to define a fulfillment handler

promise.then(value => {…})
  • the fulfillment handler processes the result of the promise
  • the fulfillment handler is to be executed when the promise is resolved

Example: get the result of the delayed sum of two numbers

function promiseSum(add1, add2, delay = 1000) {
  return new Promise((resolve, reject) => {
    const callback = () => {
      let sum = add1+add2;
      resolve(sum); 
    }
    setTimeout(callback, delay);
  })
}
let promise = promiseSum(25, 25);
console.log(promise);  // Promise {<pending>}
// in then method, get the sum, result of the promise
promise.then((value) => {
  console.log(promise); // Promise { 50 }
  console.log("Sum is: ", value); // Sum is: 50
});

Example: get the Response, result of fetch invocation

// fetch makes a promise that resolves with a Response object
let promise = fetch('https://api.ipify.org?format=json');
console.log(promise); // Promise {<pending>}
// in then method, get Request object, result of fetch promise
promise.then(response => {
  console.log(promise); // Promise {<fulfilled>: Response}
  console.log(response) ; // Response {type: "cors", url: "https://api.ipify.org/?format=json", redirected: false, status: 200, ok: true, …}
});

The Promise.prototype.then method allows to chain promises

  • As already said, ECMAScript 6 introduced promises to make it easier to do one asynchronous task and then another:
    you can use the then method to pass the result of a promise to another promise
  • after a Promise is fulfilled, the then method is called; suppose that the then's handler produces another promise:
    to process the result of the second promise, invoke the then method once again

Example: get the result of response.json() by invoking then method

// fetch() returns a promise and response.json() returns a promise as well 
fetch('https://api.ipify.org?format=json')
  .then( response => {  // first then invocation
    console.log(response) ; // Response { … }
    return response.json();
  })
  .then( json => {  // append a second then invocation
    console.log(json); // {ip: "83.47.216.44"}
    document.getElementById('text').textContent = `IPv4: ${json.ip}`;
  })

The Promise.prototype.catch(onRejected) method allows to define a rejection handler

promise.catch(reason => {…})
  • the rejection handler is called if a rejection has happened.
  • the catch method yields a new promise, the same way the then method yields a new promise.
  • the result of the new promise depends on the return value of the rejection handler:
    • if it is a value, the promise is now fulfilled with that value;
      if it throws another exception, the promise is still rejected with a given reason;
      if it returns another promise, that promise is chained
    • commonly, the rejection handler deals with the problem and then returns a value to resolve the promise

The Promise.prototype.finally(onFinally) method invokes the onFinally handler after the promise is settled

promise.finally(() => {…})
  • the finally handler is always invoked, no matter the promise is resolved or rejected
  • the finally handler has the purpose of doing some clean up that has to occur no matter what happend before
  • the handler has no arguments

Example: use catch and finally methods with fetch API

const update = () => {
  // get JSON for client IP
  fetch('https://api.ipify.org?format=json')  
    .then(response => response.json())
    .then(json => {
      // update DOM to show IP
      document.querySelector('section#result').innerHTML=
       `<h2>My Public IP</h2>              
        <p class="ip"> ${json.ip} </p>
        <p> Last update: ${(new Date()).toLocaleTimeString()}</p>`;
    })
    .finally(() => {
      // always update DOM to add the reload button
      document.querySelector('section#reload').innerHTML = 
        `<a id="refresh-icon" href="#" title="refresh"><i class="fas fa-sync-alt"></i></a>`
      document.getElementById('refresh-icon').addEventListener('click', e => {
        e.preventDefault();
        update();
      });
    })
    .catch(err => {
      // update DOM with error message
      console.error(err);
      document.querySelector('section#result').textContent='Sorry, we cannot load data'; 
    })
}

document.addEventListener('DOMContentLoaded', update);

See the Pen fetch then finally catch by Massimiliano De Simone (@maxdesimone) on CodePen.

4 Handling promises with async/await construct

Which programming style to use with promises?

  • the asynchronous programming style
    • a chain of then/finally/catch method invocations
    • code hard to read and reason about
  • the synchronous programming style
    • await operator within async functions
    • code with sequence of statements and using the traditional control flow

The await operator

let value = await promise
  • the value of the await operator is the fulfillment value of the promise object
  • it makes your program appear as it were waiting for the promise to settle
  • it can only occur within a function tagged by async keyword

Example: async function with await fetch

const loadJSON = async (url) => {
  // await gets fetch promise result
  const response = await fetch(url);
  // await gets response.json promise result
  const json = await response.json();
}

Async functions

  • the JavaScript interpreter rewrites async functions: code after await is put into a then handler and executed after the promise resolves
  • You can apply the async keyword to different kinds of functions: function expressions and function definitions, arrow functions, class methods and object literal methods
  • the result of the async keyword is a function object which is an instance of the AsyncFunction class

Example: async functions sequential programming style

const update = async () => {
  try {
    // remove refresh and add spinner icon
    document.querySelector('section#spinner').innerHTML =  
      `<a id="refresh-icon" href="#" title="refresh">
        <i class="fas fa-sync-alt fa-spin"></i></a>`;  
    // await fetch() evaluates to http response
    const response = await fetch('https://api.ipify.org?format=json');
    // await response.json() evaluates to json  
    const json = await response.json();
    // show result to user
    document.querySelector('section#result').innerHTML=
       `<h2> My Public IP </h2>              
        <p class="ip"> ${json.ip} </p>
        <p> Last update: ${(new Date()).toLocaleTimeString()} </p>`;    
  } catch (err) {
    // show error message to user
    document.querySelector('section#result').textContent='Sorry, we cannot load data'; 
    console.error(err);
  } finally {
    // always remove spinner & add refresh icon
    document.querySelector('section#spinner').innerHTML = 
      `<a id="refresh-icon" href="#" title="refresh">
        <i class="fas fa-sync-alt"></i></a>`;
    document.getElementById('refresh-icon').addEventListener('click', e => {
      e.preventDefault();
      update();
    });
  } // END finally
} // END try

document.addEventListener('DOMContentLoaded', update);

See the Pen async function by Massimiliano De Simone (@maxdesimone) on CodePen.

Async functions return values

  • async functions always return a promise
  • if the function returns a value, the value becomes a promise
  • if the function returns null, it actually returns Promise.resolve(null)

Example: an async function returning a value

// the function returns a string value, but the value becomes a promise
const getClientIP = async () => {
  const response = await fetch('https://api.ipify.org?format=json');
  const json = await response.json();
  return json.ip; // returns a promise
}
// To get a value returned by async functions invocations:
// - either invoke the async function and chain a then method
getClientIP().then( value => console.log(value) )
// - or invoke the async function from within another async function
const clientFunction = async () => {
  const value = await getClientIP();
}
clientFunction();

Example: async function return null

const loadJSON = async (url) => {
  // return null becomes Promise.resolve(null)
  if (url === undefined) return null; 
  // await gets fetch promise result
  const response = await fetch(url);
  // await gets response.json promise result
  const json = await response.json();
}

What if an async function throws an exception?

  • if a statement of an async function throws an exception, the async function yields a rejected promise

What if an await get a rejected promise?

  • if await operator gets a rejected promise, it throws an exception

Handling errors by async function invocations

  • when using await, you should set up a strategy for error handling
  • for example, the top level async function encloses all async function invocations within a try/catch statement

Example: async function that does exception handling

See the Pen async functions exception handling by Massimiliano De Simone (@maxdesimone) on CodePen.

Tuesday, December 22, 2020

Timers

JavaScript defines setTimeout() and setInterval() functions; these functions are not standard, but are implemented by both browsers and server-side JavaScript engines

The setTimeout function

setTimeout() invokes a function after a specified number of milliseconds has elapsed

timeoutID = setTimeout(function[, delay]);
  • the function parameter specifies the function to be executed after the timer expires
  • the delay parameter specifies the interval, in milliseconds, after the timer expires
  • the arg1, arg2, ... optional parameters specify additional arguments passed to function when timer expires
  • the timeoutID returned value identifies the timer; pass this value to the clearTimeout() function to cancel the timeout
  • note: if delay parameter is omitted, the function will be called as soon as possible, when the JavaScript engine is idle
  • note: the setTimeout function returns immediately and does not wait for the interval to elapse before returning

Example: setTimeout invocations

setTimeout(() => { console.log("Three"); }, 1000);
setTimeout(() => { console.log("two"); }, 2000);
setTimeout(() => { console.log("one"); }, 3000); 
setTimeout(() => { console.log("go!"); }, 4000);

Example: using setTimeout() and clearTimeout() with Console API

HTML

<button id="setTmr"> SET Timer </button> 
<button id="cancelTmr"> CANCEL Timer </button>    

JavaScript

let timeoutID;
const create = (e) => {
  timeoutID = setTimeout(console.log, 2000, 'Hurry up!');   
};
const dismiss = (e) => {
  clearTimeout(timeoutID);
};
document.getElementById('setTmr').addEventListener('click', create);
document.getElementById('cancelTmr').addEventListener('click', dismiss);

The setInterval function

The setInterval() invokes a function repeatedly every time a specified number of milliseconds has elapsed

intervalID = setInterval(function[, delay [, arg1, arg2, ...]]);
  • the function parameter is the function to be executed every interval of time
  • the delay parameter is the interval, in milliseconds, the timer should wait for between executions
  • the arg1, arg2, ... optional parameters specify additional arguments passed to function when timer expires
  • the returned intervalID is a value which identifies the timer; pass this value to clearInterval() to cancel the interval

Example: using setInterval(), and clearInterval() with Console API

let counter = 0;
let string = "Hello-World";

// every second clear console and log the string
let intervalID = setInterval(() => {
  console.clear();
  console.log(string.substring(0, ++counter));
  if(counter === string.length)
    clearInterval(intervalID);    
}, 1000);

Thursday, November 5, 2020

Function Arguments and Parameters

Function parameters are defined in the function signature; function arguments are values provided every time the function is invoked

  • NOTE: function invocations work with fewer or with more arguments than declared parameters

1 Optional Parameters and Defaults

Invoking a function with fewer arguments

  • a function can be invoked with fewer arguments than declared parameters
  • the missing parameters are set to the undefined value
  • you can write a function where some arguments are optional

Writing functions with optional parameters

  • in traditional JavaScript, you test whether a parameter is undefined
  • as alternate solution, you can use ES6 parameter defaults syntax
  • note: in functions with optional parameters, the optional ones should always be the last of the argument list

Writing functions with optional arguments, using traditional JavaScript syntax

Example: the sum function with not undefined testing

const sum = (x, /*optional*/ y) => {
    // test for undefined argument
    y = y === undefined ? x : y;
    return x + y;
}
console.log(sum(2, 2)); // 4 
console.log(sum(2)); // 4

Writing functions with optional arguments using ES6 parameter defaults syntax

  • ES6 allows to define default values for each function parameter directly in the function signature
  • just follow the parameter name with an equal sign and the default value
  • note: parameter default expressions are evaluated when the function is invoked, not when the function is defined
  • note: parameter defaults can be a constants, variables or function invocations

Example: the sum functions with default arguments

// 1. the sum function has parameter y with default argument
const sum = (x, y = x) => {
  return x + y;
}
console.log(sum(2, 2)); // 4 
console.log(sum(2)); // 4

// 2. the sum2 function has multiple default arguments
const sum2 = (x = 0, y = x) => {
  return x + y;
}
console.log(sum2(2, 2)); // 4 
console.log(sum2(2)); // 4
console.log(sum2());  // 0 

2 Rest Parameters

Invoking functions with more arguments

  • if you call a function with more arguments than declared parameters, the extra is ignored
console.log(sum(2, 2, 4)); // 4 

Writing variadic function with ECMAScript 6 rest parameters

  • ES6 rest parameters syntax allows to write function that can be invoked with more arguments than declared parameters
  • functions that accept any number of arguments are named variadic functions, variable arity functions or varargs functions

Example: the sum function with rest parameters

const sum = (first = 0, ...rest) => {
  let sum = first 
  for (const value of rest) { 
    sum += value 
  }
  return sum;
}
console.log(sum(1, 2, 3, 4, 5)) // 15
console.log(sum(6, 7, 8, 9, 10)) // 40

Function's rest parameters syntax

  • a rest parameter is preceded by three periods
  • a rest parameter must be the last parameter in the function signature

How do rest parameters work?

  • the arguments passed in the function invocation are assigned to non-rest parameters
  • the remaining arguments (the rest of the aguments) are stored in a array which is the value of the rest parameter
  • in the function body, the rest parameter has array type
  • in the function body, the value of rest parameter can be an empty array, but cannot be undefined

3 The Arguments Object

In traditional JavaScript, you could write variadic functions using the arguments object

The arguments object:

  • it is defined in the function body
  • it contains all the arguments values passed to the function for that invocation
  • it is an array-like object, with length property; which means that argument values are looped over in a for loop

Note: in modern javaScript: you should not be using the Arguments object anymore

  • in strict mode arguments is treated as a reserved keyword
  • try to refactor any function using the arguments object and replace it with ...args rest parameters

4 The Spread Operator for Function Calls

What is the use of the spread syntax?

  • the spread syntax (...) is used to extract the values contained in an iterable object

When using the spread syntax?

  • use the spread syntax in situations where multiple simple values are expected
  • for example, when creating array and object literals or when invoking functions

Example: Invoking Math.max with an array argument

// Math.max function can take any number of arguments
console.log(Math.max(11, 17, 13)) // 17
let numbers = [1, 7, 5, 9]
// passing an array will not work
console.log(Math.max(numbers)) // NaN
// you need to spread out the contents into individual arguments
console.log(Math.max(...numbers)) // 9

Comparing the spread syntax and the rest syntax

  • in a function invocation, the three dots are called the spread syntax as they separates the content of an iterable object into multiple single values
  • in a function definition, the three dots are called the rest parameter as they gather multiple arguments into an array

5 Destructuring Function Arguments into Parameters

When a function is invoked, argument values are assigned to the parameters declared in the function signature

  • parameter assignment is like variable assignment and therefore destructuring can be used in function parameter assignment

How to destructure function arguments into parameters?

  • in function signature definitions:
    • put parameter names in square brackets to destructure array arguments
    • put parameter names in curly braces to destructure object arguments
  • in function invocation expression
    • pass an array value for each pair of brackets in the function signature
    • pass an object value for each pair of curly braces in the function signature
  • when function invocation expression is evaluated
    • arguments value are extracted and assigned to the array elements or the object properties

Example: the vectorSum([x1,y1], [x2,y2]) function adds 2D vectors

  • this function destructures the components of 2D vectors in two parameters [x,y]
function vectorSum([x1,y1], [x2,y2]) { 
    return [x1+x2, y1+y2];
}
vectorSum([1,2], [3,4]) // [4,6]    

When is it convenient to destructure object arguments into function parameters?

  • use argument destructuring, if function parameter lists are long or contains many optional parameters

Example: a function with optional parameter written in traditional JavaScript

  • check whether each argument is undefined and provide a default value
// 1. in traditional JavaScript, check whether argument is not undefined
function objectToString(object, config) {
  if (config === undefined) config = {}; 
  if (config.nameValueSeparator === undefined) config.nameValueSeparator = ' = ';
  if (config.propSeparator === undefined) config.propSeparator = ', ';
  ...
}

Example: a function with optional parameter using ES6 parameter destructuring

  • this function turns an object into a string
  • this function is customizable, because the caller specifies some configuration parameters: the name-value separator, the property separator, etc.
  • instead of using parameters, this function uses an object literal
  • the function takes as second parameter an object, which declares the destructured parameter variables
  • the destructured parameters are: leftDelimiter, nameValueSeparator, propSeparator, rightDelimiter
  • each destructured parameter has a default value
  • in case the caller does not provide any argument for the config object, there is also a default blank object {}
// 2. in ECMAScript 6, use destructuring and assign default value to destructured parameters
function objectToString(object, {leftDelimiter = '{', 
                                 nameValueSeparator = '=', 
                                 propSeparator = ', ', 
                                 rightDelimiter = '}'} = {} ) {
    let props = Object.entries(object)
    .map( ([k,v]) => { return [k, typeof(v)==="string"?`"${v}"`:typeof(v)==="function"?"Function":v] })
    .map( item => { return `${item.join(nameValueSeparator)}` })
    .join(propSeparator)
    return `${object.constructor.name}: ${leftDelimiter}${props}${rightDelimiter}`;
}

// actress object
const actress = { 
    id: 100, 
    firstName: 'Uma', 
    middleName: 'Karuna', 
    lastName: 'Thurman', 
    age(){ return } 
};

// invoke without config argument
console.log(objectToString(actress)); 
// Object: {id=100, firstName="Uma", middleName="Karuna", lastName="Thurman", age=Function}

// invoke with config argument
const options = { 
    nameValueSeparator : '\u279C',
    propSeparator : ' \u2E3A ',
    rightDelimiter : ')', 
    leftDelimiter : '('
};
console.log(objectToString(actress, options)); 
// Object: (id➜100 ⸺ firstName➜"Uma" ⸺ middleName➜"Karuna" ⸺ lastName➜"Thurman" ⸺ age➜Function)

Wednesday, October 7, 2020

ECMAScript 6 Modules

Table of Contents

    1 Modules in ECMAScript 6
    2 Exports
    3 Imports
    4 JavaScript Modules in Web browsers

Modular programming is encapsulating functionalities and hiding implementation details

  • modular programming allows to keep the global context clean
  • modules may not accidentally modify anymore shared bindings defined by other modules

Before ES6 modules, many different solutions were adopted to the problem of name conflicts and encapsulation

  • closures as namespace
  • CommonJS module specification for server JavaScript runtime environments
    • CommonJS defines the require() function and exports variable
    • NodeJS module system implements the CommonJS specification and uses the require() function and module.exports object
  • AMD (Asynchronous Module Definition) specification
    • many asynchronous module systems run on browsers
    • RequireJS is the most important AMD API implementation and uses the define() function

1 Modules in ECMAScript 6

What is a Module?

The ECMAScript 2015/ES6 standard introduces the import and export keywords into JavaScript to support modularity

Module scope

  • each module has its separate scope
  • module scope is not the global scope of browser or NodeJS
  • in normal scripts, bindings declared at top level code go into the global scope, whereas in modules bindings exist only in the scope of the module

Private and public values

  • each JS file is its own module
  • JavaScript constants and variables, functions and classes defined within a module are private to that module
  • if a module explicitly export a value, that value will be available to other modules that explicitly import it

Naming conficts

  • modules provides a way to manage naming conflicts
  • names of public identifiers do not clash because they are unique
  • names of private identifiers are not important; even if they happen to have the same name, they do not clash
ECMAScript Modules

Modules code is always executed in strict mode

  • in normal script, code can use loose syntax such as arguments object, with statement, undeclared variables, whereas in modules code is automatically in strict mode
  • in ES5 strict mode, top level code this points to the global context, whereas in ES6 modules top level code this value is undefined

Modules are different from classes

  • classes look like modules, as they have public and private fields and methods, but you cannot use classes in place of modules
  • classes are meant to be instantiated and there may exist many instances of a class
  • a module is a collection of JavaScript statements that are executed only once, when the module is loaded

Modules have dependencies

  • a module can depend on other modules
  • a module should state which other modules they depend on
  • module dependencies are statically resolved:
    the module system determine dependencies by reading the file and without executing them
  • ES6 module system supports cyclic dependencies

ES6 module system loads modules asynchronously

  • loading modules asynchronously is a main feature that affects browser usability
  • otherwise, the browser have to wait until modules are finished loading

2 Exports

A module exports one or multiple values

  • a module exports values that your module will import
  • a module contains any kind of declaration (constants, variables, function and classes) and expressions
  • usually, the exported value is a function or class, but may also be an object, an array or a constant

A module should select one value as default export

  • actually, modules are well designed if they export just one value
2.1 Default Exports

to export a default declaration/expression, precede it with export default keywords

  • choose at most one declaration/expression as default
  • it is common having modules that exports a single value
  • export default can export: function expressions, class expressions or object literals
// exporting a class declaration
export default class MyClass { ... }
// exporting a class expresssion
export default class { ... }
2.2 Named Exports

How to export declarations?

  • to export declarations, precede any declaration with export
  • alternatively, write a single export statement at the end of the module

// declare and export on one line
export const myConstant = 1111
export function myFunction(param) { ... }
export class MyClass { ... }

// declare and then export
const myConstant = 1111
function myFunction(param) { ... }
class MyClass { ... }
export { myConstant, myFunction, MyClass }

Default exports vs named exports

  • It is uncommon that a module have both regular exports and one default export
  • Default exports are easier to import than non-default exports
  • named exports can only be used on declarations that use an identifier
    whereas default exports can be used on anonymous expressions

3 Imports

Import a value, already exported by a module, using the import and from keywords

3.1 Default Imports

Importing a value from a module that exports a single default value

import defaultExport from 'module-name'
  • defaultExport is an identifier that names the default export in the client module
  • module-name is a string literal that specifies the module from where you are importing its default export
    the string should be: an absolute path starting with "/", a relative path starting with "." or an URL with protocol and hostname; variables and string template string are not allowed.
    In web browser the string in interpreted as URL relative to location of the current module, in NodeJS the string is interpreted as a filename relative to current module.

The import statement assign the imported value to a constant, it is equivalent to:
const <identifier> = <imported_value>

Like exports, imports must appear at top level code:

  • imports must not be within, classes, function or nested in conditionals
  • usually they are place at start of the module

Example: import a default export

// give a name of your choosing to the default import
import MagicClass from './modules/myclass.js'
3.2 Named Imports

A module can export default values or/and named values

Importing values from a module that export values by names

// import a single named value
import { namedExport } from 'module-name'
// import two named values
import { namedExport1, namedExport2 } from 'module-name'
// you can rename the imported value using the as syntax
import { namedExport1 as myName1, namedExport2 as myName2 } from 'module-name' 
  • where namedExportN is the name given by author of the module

Example: import two functions exported by names trick1 and trick2

import { trick1, trick2 } from 'modules/magicModule.js'

Importing default exports VS importing named exports

  • default exports are not required to have a name in the module that export them, but the you give them a local name in the module that import them
  • named exports have a name in the module that export them, and you must refer to those names, if you want to import their values
  • Note: the exporting module can export any number of named values and the import statement that refernces that module can import any subset of those values
  • Note: the identifiers within import curly braces are all hoisted to the top of the importing module and are assigned to constants

Importing both default and named exports

  • usually, modules define either one default export or multiple named exports
  • it is uncommon for module to define both default and named exports
  • nevertheless you can import default and named exports

Example: importing both default and named exports

import defaultExport, { namedExport1, namedExport2 } from 'module-name'

There a third form of the import statement: you can use the import statement just to execute the module

import 'init.js'
  • employ the code import 'init.js' not to import a module value, but to execute the body of the module once

4 JavaScript Modules in Web browsers

Packaging modules for web browsers

  • at the time of this writing, production code using ES6 modules is packaged with module bundling tools, such as Webpack, Rollup and Parcel. Webpack combines all your imported ES6 module files and packages them into one or more output files known as bundles
  • ES6 modules are natively supported by modern browsers and therefore a bundler is not required in development environment, but it is still worth having a bundler in production environment, because browsers have to download fewer files

Applying modules to HTML documents

  • declare a script is module by using the module value of type attribute
<script type="module" src="./main.js"></script> // as external script
// or
<script type="module">import "./main.js";</script> // as inline script

A <script type="module"> element indicates the starting point of a modular program

  • the starting module loads on demand the other modules the program consists of
    • the other modules are expected to be defined in its own javascript file
    • none of the other modules are expected to be defined as inline modules
  • code between two <script type="module"></script> tags is an ES6 module
    • inline modules should no be used as there is no a way to import values exported from inline modules

Execution of modules is deferred

  • script elements with the type="module" attribute are loaded and executed like script elements with defer attribute
  • browsers load the script as soon as it encounter them, but script is executed after HTML parsing is complete

The async attribute

  • adding the async attribute to a module script element, modifies when the module is executed
  • the async module will be executed as soon as the script is loaded and before HTML parsing is complete

The nomodule attribute

<script type="module" src="main.js"></script> // type="module"
<script nomodule src="fallback.js"></script>  // nomodule attribute
  • module-agnostic browsers ignore type="module" and nomodule attribute, so they execute fallbak.js
  • module-aware browsers recognise type="module" and nomodule attribute, so they execute main.js

Converting newer ECMAScript versions into older versions

  • a code translator is a tool that converts code from a programming language to another programming language
    a compiler is a translator used to convert high-level programming language to low-level programming language
  • Babel is a JavaScript (trans)compiler that converts ECMAScript 2015+/ES6+ code into a version of JavaScript, that can be run by older JavaScript engines.
  • using Babel and Webpack tools, you can convert modular JavaScript code to non-modular ES5 code
    and then load the ES5 code through the <script nomodule></script> element

Module filename extensions .mjs or .js

  • NodeJS adopts the .mjs filename extension for modular code
  • web browser script element can load modules with both .mjs or .js filename extension,
    if your web servers uses the .mjs extension, it must be configured to use the same MIME type as of .js files

Sunday, September 13, 2020

JavaScript Classes

0 Introduction

Object literals

  • when you create object literals, you treat each object as a unique set of properties
// object with literal notation
let uma = { 
    firstname: 'Uma', 
    lastname: 'Thurman', 
    birthdate: 'April 29, 1970',
    age(){
        let millisecs = (new Date() - new Date(this.birthdate));
        return Math.floor( millisecs / (1000*60*60*24*365) );
    }
} 

In the object-oriented paradigm, objects have: state, behaviour and identity

  • the state of the object uma is the value of the properties: firstname, lastname and birthdate
  • the object uma has identity, because another object with same state is a different object
  • the object uma has behaviour: the age function

Classes of objects

  • it is convenient to define a class of objects that shares certain properties
  • instances of the class have their own properties and their class methods
    • these own properties hold the state of the object
    • these methods are defined by the class and shared by all the instances of the class
  • for example, imagine a class named Employee that represents the employees that work for a company

JavaScript classes

  • JavaScript classes use prototype-based inheritance
  • objects belong to the same class if they inherit from the same prototype

How to define classes in JavaScript

  • In ES5, objects are instances of the same class if they were created by the same constructor function
  • In ES6, you can define classes by using a simplified syntax with class keyword

1 Classes and Factory Functions

How to define a class in JavaScript using a factory function:

  • write an object to use as prototype and a factory function that create new objects that inherit from this prototype
  • the factory function makes new objects and initializes them
  • note: this is not the idiomatic way to define a class in JavaScript

Example: defining the createPerson factory function

// place common properties into prototype object
const personPrototype = {
    age(){
        let millisecs = (new Date() - new Date(this.birthdate));
        return Math.floor( millisecs / (1000*60*60*24*365) );
    }
}
// then set the [[Prototype]] slot in the factory function
function createPerson(firstname, lastname, birthdate) {
    const result = { firstname, lastname, birthdate };
    Object.setPrototypeOf(result, personPrototype);
    return result;
}

Example: using the createPerson factory function

// create two instances of the Person class
const uma  = createPerson('Uma', 'Thurman', 'April 29, 1970');
const john = createPerson('John', 'Malkovich', 'December 9, 1953');
// a person has only three properties
console.log(uma);  // { firstname: 'Uma', lastname: 'Thurman', birthdate: 'April 29, 1970' }
console.log(john); // { firstname: 'John', lastname: 'Malkovich', birthdate: 'December 9, 1953' }
// the age method in the prototype object 
console.log(Object.getPrototypeOf(uma)); // { age: [Function: age] }
// invoke the age method in the shared prototype object
console.log(`${john.firstname} ${john.lastname} is ${john.age()}`); // John Malkovich is 66

All instances of the Person class share the same prototype object

  • the [[Prototype]] properties of uma and john objects points to the same personPrototype object

2 Classes and Constructors

What is a constructor function?

  • a constructor is a function designed for the initialization of newly created objects
  • by convention, a constructor function has its name written in uppercase
  • invoke a constructor using the new keyword:
    • for example: const john = new Person()
    • the new keyword automatically creates a new object
    • the constructor only has to initialize the state of the new object

Setting prototype for new objects

  • a constructor invocation automaticaly sets a prototype
    • the Person.prototype property is used to set the [[Prototype]] property of the new object
    • objects created by the same constructor inherits from the same prototype and therefore are instances of the same class
  • The prototype object of the constructor is stored as a property of the function object
    • the constructor function is an object and therefore it can have properties
    • the Person.prototype is a property of the Person function and you can add more functions to it
      • for example: you can add the age() method to Person.prototype

What happens when you call the Person constructor:

    const uma = new Person('Uma', 'Thurman', 'April 29, 1970');
  1. the new keyword creates a new blank object
  2. the Person function is invoked as method of new object
    • the this value is set to the new object
  3. the body of the Person function is executed to initialize the new object
    • the firstname, lastname, birthdate properties of the object are set to the constructor's arguments
  4. the [[Prototype]] property of the new object is set to the prototype object of the Person constructor function
  5. the newly constructed object is returned

Example: declaring the Person constructor function

// the Person constructor function
function Person(firstname, lastname, birthdate) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.birthdate = birthdate;
}

// Add methods to the prototype of the Person constructor function
Person.prototype.age = function() {
    let millisecs = (new Date() - new Date(this.birthdate));
    return Math.floor( millisecs / (1000*60*60*24*365) );
}

Example: using the Person constructor function

// construct two instances
const uma  = new Person('Uma', 'Thurman', 'April 29, 1970');
const john = new Person('John', 'Malkovich', 'December 9, 1953');
// log the two instances of Person class
console.log(uma);  // Person { firstname: 'Uma', lastname: 'Thurman', birthdate: 'April 29, 1970' }
console.log(john); // Person { firstname: 'John', lastname: 'Malkovich', birthdate: 'December 9, 1953' }
// invoke age method from Person.prototype
console.log(`${john.firstname} ${john.lastname} is ${john.age()}`); // John Malkovich is 66
// invoke toString method from Object.Prototype
console.log(john.toString()); // [object Object]
// the prototype is an object with a single property, the age function
console.log(Person.prototype); // Person { age: [Function] }

In the image below, two objects were created by using the Person constructor function.

  • uma and john instances share the same prototype
  • Person.prototype has a [[Prototype]] property that points to Object.prototype
two instance of the Person class

Compare and contrast createPerson factory function and Person constructor function

  • constructor functions use capital letters whereas regular functions and methods begin with lowercase letters
  • the constructor is invoked with the new keyword, whereas the factory function without the new keyword
  • the constructors does not have to return the new object, whereas the factory function does
  • in the factory function, the prototype was arbitrary named personPrototype, whereas, in the constructor function, the prototype is named Person.prototype and this name is mandatory

Classes and arrow functions

  • both factory and constructor examples use ordinary functions, not arrow functions to define constructor; arrow functions do not have a prototype property and cannot be used as constructors
  • both the two examples use ordinary functions, not arrow functions, to define methods. Methods use this to refer to the object on which they are invoked. Ordinary functions set the this value based on the objects which they are invoked on, whereas arrow functions inherit this from the context where they are defined.

Prototypes as Class identity

  • the prototype object, not the constructor, is fundamental to the identity of a class
  • if two constructors have prototype properties that point to the same prototype object, then both the two constructors can be used to create instances of the same class.

Constructors as Class face

  • the constructor function represents the face of a class
    • the name of the constructor is adopted as the name of the class
    • we say that the Employee constructor creates Employee objects

How to test objects for membership in a class

  • use the constructor with the instanceof operator for testing objects for membership in a class
  • the obj instanceof Constructor expression evaluates to true if obj inherits from Constructor.prototype
  • let john = new Person();
    john instanceof Person 
    // -> true: john inherits from Person.prototype 
    
  • note: the inheritance need not to be direct: if obj inherits from obj2 and obj2 inherits from Constructor.prototype, then the expression still evaluates to true
  • note: the obj instanceof Constructor expression does not check whether obj is initialized by the Constructor function. If you create a People constructor function that sets the prototype to the same Person.prototype, then the instanceof operator will find objects created by People as instances to the same Person class

Testing for the prototype without using constructor function

  • the Object.prototype.isPrototypeOf() method allows to test the prototype chain without using the constructor
  • this method is useful if you defined a class using a factory function not a constructor function
  • proto.isPrototypeOf(obj) returns true if proto is in the prototype chain of obj
  • example: personPrototype.isPrototypeOf(person);

The constructor property

  • Any regular JavaScript function can be used as constructor
  • every regular JavaScript function has a prototype property, which points to the prototype object
  • the prototype object has a single property: the constructor property, which references to the function object:

The constructor property for class instances

  • All the functions has their predefined prototype objects that has its constructor property
  • every class instance inherits the constructor property that refers to its constructor function
  • the constructor serves as face of a class, the constructor property gives the class of the object
  • const john = new Person();
    john.constructor === Person; // true
    

3 Classes with the class Keyword

Wednesday, May 6, 2020

Exceptions

If your program encounters an unusual situation that does not allow to continue the normal flow of the code, it can throw an exception.

Example: the processArray function

let processArray = function (array) {
    // do type checking and throw an error object
    if (!Array.isArray(array)) {
        throw new Error("processArray: argument must be an array");
    }
    array.sort();
    array.reverse();
}
      
let array = [666, 1, 2, 222, 333, 555, 777];
    
try {
    processArray(array);
    console.log(array);
    processArray({}); // Error: processArray: argument must be an array
} catch (e) {
    console.error(e);
}  

the throw statement

In JavaScript you can throw any value, but it is customary to throw an object created by the Error function.
The throw statement interrupts execution of the function and transfers control to the enclosing catch or finally.

throw new Error('message');

The Error object properties

  • the Error object contains a name property that identifies the type of the exception and a descriptive message property.
  • the JavaScript implementation can produce these exception names: 'Error', 'EvalError', 'RangeError', 'SyntaxError', 'TypeError', 'URIError'.

What is the point of exception?

  • what's the difference between throwing an exception as opposed to returning undefined?
  • exceptions are intended to signal problems that the caller might not be able to handle.
  • for example, suppose that a function makes a HTTP request, gets a string form a server and then calls the JSON.parse function.
    If the server makes a serious mistake, JSON.parse will not be able to parse the string and will throw an exception.
    Then, the caller of JSON.parse can't fix the problem, it has to give up and let propagate the exception up the call stack. Until, some further up will deal with the problem.

The try/catch statement

The exception object is delivered to the catch clause of a try statement:

const troubleFunction = () => { 
    throw new Error('troubleFunction'); 
}

try {
    troubleFunction();
    console.log("this is skipped");
} catch (error) {
    // handle and/or log the exception
    console.log(error);
    console.log("this is executed");
}
// resume the normal flow of execution
console.log("this is executed also");

Handling exceptions

  • in the catch clause, you want to log the error
  • usually you don't want to analyze the type of the errors, you don't want the type of the exception to drive the control flow.

The try/finally statement

try {
    // open file
} finally {
    // close file
}

The finally clause

  • if is always executed, whether the try block was successful or whether an exception happened.
  • it is intended to release resources safely
  • Note: never use the return statement in this clause, as they do no interact well

Saturday, January 11, 2020

Variable Declaration and Assignment



Programmers use name or identifier to represent values:

  • you declare a variable and assign the variable a value; later, you refer to that value using the variable's name.
  • this is said binding a name to a value.

Variable declaration

  • until ECMAScript 5, variables and constants are declared using the keyword var
  • in ECMAScript 6, variables are declared by the let keyword and constants by the const keyword
    • let and const work the same way, but const must always be initialized.

1 Declarations with let and const

// a declaration statement for each variable
let i; 
let myVariable;

// a single statement for declaring multiple variables
let i, myVariable;

// variables declaration and initialization
let x = 1, y = 1, z = 1;
let text = 'Hello User';

// the speed of light constant
const C = 299792458;

You can use the let keyword in the three kinds of for loop to declare the loop variable

for(let i=0, l=data.length; i<l; i++) { // the for loop 
    console.log(data[i]);              // data is array-like object
}
    
for(let item of data) { // the for/of loop 
    console.log(item); // data is iterable object
} 
    
for(let propName in object) { // the for/in loop
    console.log(propName);  // data is plain object
} 
Variable and Constant Scope

The scope of a variable is the area of the JS program where the variable is defined.

  • variables and constants declared by let or const statements are block scoped: they are defined in the block of code where the let or const appears.
    • JS blocks are: function and classes definitions, the bodies of if statements, for and while loops; any set of curly braces constitutes a block of code.
  • note: variables declared as part of a for loop have their scope in the body of the loop.

A global variable is a variable that is declared at the top level, out of any block

  • a global variable has global scope
  • in a Node and client-side JS modules, modules have their scope. The scope a global variable is the file the variable is defined in.
  • in client-side JS that do not use of modules, a web page can load any number JavaScript files using multiple <script> elements. If a <script> element defines a global, that variable is defined in all the <script> elements that execute after the let statement execution.
Repeated Declarations

It is an error to have two let or const declarations in the same scope

  • You are allowed to re-declare a variable in a nested scope, but it is not a best practice.

Example: declare two variables with same name using let

let myVar = 10;

if(smth == true) {
    let myVar = 100;  // OK
}

let myVar = 100;  // Syntax error
Variables and Types

JavaScript variables are untyped:

  • no type is associated in a JS variable declaration
  • it is legal to assign a number and then assign a string to the same variable

2 Variable Declarations with var

Until ECMAScript 5, you could declare variables only using the var keyword:

  • the syntax of var is the same of let:
var myVariable = 'value';

for(var i = 0, len = data.length; i < len; i++) { // for loop
    console.log(data[i]);
} 

3 Declarations with var VS declarations with let

There are differences between var and let declared variables

  • variables declared by var are scoped to the immediate function body, while variables declared by let are scoped to the immediate enclosing block
  • unlike let statement, it is legal to declare multiple times the same variable using the var statement
  • variables declared by var are hoisted: the declaration of the variable is moved up to the top of the function body, while the initialization remains in the place where the var statement appears.
  • variables declared by var, out of any function, are global variables: these global variables are implemented as properties of the global object and cannot be deleted using the delete operator.

Note: what happens if you try to use a variable name, omitting any of the var/let/const declaration statement?

  • in strict mode you get a reference error; in non-strict mode, you create a global variable, even if your variable name is nested in a function or block. This is bug prone.

4 Execution Context

The execution context is the environment where the current code is being executed.

  • the execution context of a variable or function defines what other data it has access to
  • each execution context has an associated variable object that stores all variable and functions it defines. The variable object cannot be accessed by your programs, but is used behind the scenes to handle data

The global execution context

  • the global execution context is the first context that is defined, when the JavaScript interpreter starts.
  • in web browsers the global context is the window object
  • when it has finished execution of all its code, the execution context is destroyed, along with all the variables and functions defined within it. The global context is destroyed when the application exits, for instance when a browser is closed

The function execution context

  • each function invocation has its own execution context
  • once a function starts executing, the function's context is pushed onto a context stack
  • after the function has finished executing, the function's context is popped off, returning control to the previous executing context
  • the current execution context of the context stack is the one that sits on the top of the stack

5 Destructuring Assignment

ECMAScript 6 destructuring:

  • it is the process of breaking down a data structure into smaller parts
  • it allows to extract the values contained in an array or an object and assign them to a variable

The destructuring assignment syntax

 [ variable1, variable2, ... , variableN ] = array; //  array destructuring assignment
 { variable1, variable2, ... , variableN } = object; // object destructuring assignment
  • the left side specifies one or more variables, using a syntax that looks like array or object literals
  • the right side assigns an array or an object value

The destructuring assignment can be used:

  • in a variable declaration statement, to initialize variables
  • in an assignment expression, to assign values to variable already declared
  • in the definition of the parameters to a function.

Example: array destructuring

/**
 *   ARRAY DESTRUCTURING  
 */

// the array to be destructured
const array = [5, 10];

// variable declaration and destructuring assignment
let [x, y] = array;  // Same as let x=array[0], y=array[1]
console.log(x, y);   // 5, 10

/* The number of variables on the left of a destructuring assignment does
   not have to match the number of array elements on the right. */

[x,y] = [1];         // x == 1; y == undefined
[x,y] = [1,2,3];     // x == 1; y == 2
[,x,,y] = [1,2,3,4]; // x == 2; y == 4

// swapping variable values
[x,y] = [y,x];      // x == 4; y == 2 

// default values
[x=10, y=20, z=30] = [1,2]; // x == 1; y == 2; z == 30     

// rest syntax to collect remaining values
[x, ...y] = [1,2,3,4];  // y == [2,3,4]

/**
 *   DESTRUCTURING ITERABLES OBJECTS IN FOR/OF LOOPS
 */
let actresses = [ 
    {firstName:'Uma', lastName:'Thurman'}, 
    {firstName:'Scarlett', lastName:'Johansson'}, 
    {firstName:'Jodie', lastName:'Foster'} 
];
// loop variables are assigned elements of the array
for(const {firstName, lastName} of actresses) {
    console.log(firstName, lastName);
}

/**
 *   DESTRUCTURING ITERABLE OBJECTS
 */
// You can destructure not only arrays but any iterable object: for example a String
let [first, ...rest] = "bye bye"; 
console.log(first); // "b"; 
console.log(rest);  // ["y","e"," ","b","y","e"];  

Example: object destructuring

/**
 *   OBJECT DESTRUCTURING  
 */

// the objects to be destructured
let actress = { 
    firstName: 'Uma', 
    middleName: 'Karuna', 
    lastName: 'Thurman' 
};

// 1 - ASSIGNMENT with DECLARATION

// variable have the same name of the properties of the destructured object
var {firstName, middleName, lastName} = actress;  // same as: var firstName='Uma', middleName='Karuna', lastName='Thurman';
console.log(firstName, middleName, lastName); // "Uma Karuna Thurman"

// 2 - ASSIGNMENT without DECLARATION

// there must be parentheses around the assignment without declaration
({firstName, lastName} = actress);  
console.log(firstName, lastName); // "Uma Thurman"    

// variable names do not have to match property names of the destructured object
const {firstName: umaFirstName, lastName: umaLastName} = actress;
console.log(umaFirstName, umaLastName); // "Uma Thurman"   

// another actress object, with no middleName property
actress = { firstName: 'Helena', lastName: 'Bonham Carter' };

// the destructured value of middleName is undefined
({firstName, middleName, lastName} = actress);
console.log(firstName, middleName, lastName); // "Helena undefined Bonham Carter"  

// destructured assignment with default values
({firstName = 'Name', middleName = '', lastName = 'Surname'} = actress);
console.log(firstName, middleName, lastName); // "Helena  Bonham Carter"

// nested object destructuring
let customer = {
    displayName: 'john77',
    firstName:'John', 
    lastName: 'Smith',
    address: {
        street: '17, Thomas Street West', 
        locality: 'Dublin', 
        county: 'County Dublin', 
        eircode: 'D08 XV0T',
        country: 'Ireland'
    },       
    phone: '+353 14370969',
    email: 'johnsm@iemail.com'
};
// assignment with declaration
var {
    displayName,
    firstName, 
    lastName,
    address: { street, locality, county, eircode, country },
    phone,
} = customer;
console.log(`${displayName} is ${firstName} and lives in ${country}`); // john77 is John and lives in Ireland
  
/**
 *   DESTRUCTURING ARGUMENT OBJECTS INTO FUNCTION PARAMETERS
 */
function whois({ displayName, firstName, address: {country} }) {
    return `${displayName} is ${firstName} and lives in ${country}`;
}
console.log(whois(customer)); // john77 is John and lives in Ireland

/**
 *   DESTRUCTURING OBJECTS IN FOR/OF LOOPS
 */
actress = { firstName: 'Uma', middleName: 'Karuna', lastName: 'Thurman' };
// loop variables are assigned elements of the array of entries
for(const [pName, pValue] of Object.entries(actress)) {
    console.log(pName, pValue);
}