Salesforce Javascript Developer I (Beta) Study Notes

Sources:

Hints for the exam

After taking the exam I can give you some hints. It is a very deep technical exam, meaning most of the question give you a piece of code, sometimes quite a lof of lines of code for a single question, and you have to provide the output of that piece of code. For example, expect to see an array that has a lot of manipulations with map(), reduce() and so on. So you really need hands on this. I recommend you to use codepen.io or another tool to run as many examples as you can to understand all the different concepts (scope, coercion, promises and so on), and do it lots of times until you have everything very clear. I can tell you this exam is not an easy one if you don’t have hands-on experience and you only have theorical knowledge.

  • Variables / Types: Format Dates (toLocaleDateString())
  • Arrays: Lots of questions about .map() and .reduce() functions and also slice() and splice().
  • Coercion: Questions about giving the output. For example: what’s the result of this sentence?: null + 10 + ‘4’ + NaN
  • Browser APIs: Questions on Window.localStorage and Window.history
  • Objects / Classes: tons of questions about scope and “this”
  • Events: Notice the different between button.addEventListener(“click“, handleClick); (click vs onclick)
  • Modules: Lots of questions about exporting and importing (named exports, vs export all)
  • Error handling: Give the output of the try / catch / finally flows
  • Debugging / Testing: few questions about how to debug. Also questions about false positives and false negatives tests (watch out the theory here)
  • Promises: can you put a “.then()” after a “.catch()” ?
  • Node.js: Quite a few questions around “npm” and its package.json. How do you add dependencies? dependencies only for dev, etc. On the CLI, study the “–inspect” argument.
  • Scope: Not many but still relevant, var vs let
  • Others: know the difference between setTimeout() and setInterval()

Study Guide

https://trailhead.salesforce.com/help?article=Salesforce-Certified-JavaScript-Developer-I-BETA-Exam-Guide

Variables, Types, and Collections20%
Given a scenario, write code to create variables and initialize them correctly
Given a business requirement, utilize strings, numbers, and dates effectively
Given a scenario or example, demonstrate awareness of type coercion and its effects
Given a specific scenario, distinguish truthy or falsey evaluations
Given a list of data, demonstrate data manipulation with arrays
Given a JSON response, demonstrate how to operate the JSON object
Objects, Functions, and Classes25%
Given a business requirement, locate the best function implementation
Given a business requirement, apply fundamentals of object implementation to solve the business requirement
Given a business requirement, apply fundamentals of class implementation to solve the business requirement
Given a JavaScript module, give examples of how to use the module
Given a JavaScript decorator, give examples of how to use the decorator
Given a block of code, analyze the variable scope and the execution flow
Browser and Events14%
Given a business requirement, utilize Events, event handlers and propagation
Given a business requirement, evaluate and manipulate the DOM
Given a scenario, utilize the Browser Dev Tools to investigate code behavior
Given a scenario and requirements, utilize browser specific APIs
Debugging and Error Handling8%
Given a scenario, handle errors properly
Given code to be debugged, use the console and breakpoints
Asynchronous Programming15%
Given a scenario, apply asynchronous programming concepts
Given a scenario, use event loop and event monitor or determine loop outcomes
Server Side JavaScript11%
Given a scenario and requirements, infer which Node.js implementation is a good solution
Given a scenario and requirements, infer which Node.js CLI command is a good solution
Know the core Node.js modules and given requirements, infer which Node.js library/framework is a good solution
Given a scenario and requirements, distinguish which Node.Js Package Management solution is the most fitting
Testing 7%
With a block of code and the associated Unit Test, determine where the test is ineffective and modify it to make it more effective

Variables, Types, and Collections

Variables

Keyword
Scope
Mutable Assignment
var
function
yes
let
block
yes
const
block
no

New variables in JavaScript are declared using one of three keywords: let, const, or var.

JavaScript is case sensitive

var is the most common declarative keyword. It does not have the restrictions that the other two keywords have. This is because it was traditionally the only way to declare a variable in JavaScript. A variable declared with the var keyword is available from the function it is declared in.

An example of scope with a variable declared with var:

// myVarVariable *is* visible out here

for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) { 
  // myVarVariable is visible to the whole function 
} 

// myVarVariable *is* visible out here 

let allows you to declare block-level variables. The declared variable is available from the block it is enclosed in.

// myLetVariable is *not* visible out here

for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
  // myLetVariable is only visible in here
}

// myLetVariable is *not* visible out here

Constants

All variables are pointers. Assignment is the act of pointing that variable to something in memory. Mutability is whether or not a variable can be reassigned once it has initially been assigned something. Using var or let creates mutable pointers, whereas const is immutable.

var myBike = "Mountain Bike";
let currentGear = 5;
const numberOfGears = 12;
//reassignment
myBike = "Penny Farthing"; // this works
currentGear = 1; // so does this
numberOfGears = 1; // error

When working with objects (rather than primitives), remember that const only prevents reassigning your variable to a different object. The object itself (its properties, functions, and so on) can still be changed

// call constructor, new object, assign it to bike
const bike = new Bike();
//Change internal state by calling a function
bike.changeGear("front", "Up");
// add a new member to bike that did not exist before
bike.type = "Penny Farthing";
// check for success
console.log(bike.calculateGearRatio()); // 4.0909...
console.log(bike.type); // "Penny Farthing"
// attempt to point bike to new instance of Bike
bike = new Bike(1,2); // error

const allows you to declare variables whose values are never intended to change. The variable is available from the block it is declared in.

const Pi = 3.14; // variable Pi is set 
Pi = 1; // will throw an error because you cannot change a constant variable.

If you declare a variable without assigning any value to it, its type is undefined.

An important difference between JavaScript and other languages like Java is that in JavaScript, blocks do not have scope; only functions have a scope. 

Types

Numbers

Numbers in JavaScript are “double-precision 64-bit format IEEE 754 values”, according to the spec. This has some interesting consequences. 

0.1 + 0.2 == 0.30000000000000004;

The standard arithmetic operators are supported, including addition, subtraction, modulus (or remainder) arithmetic, and so forth. There’s also a built-in object called Math.

Math.sin(3.5);
var circumference = 2 * Math.PI * r;

You can convert a string to an integer using the built-in parseInt() function. This takes the base for the conversion as an optional second argument, which you should always provide

parseInt('123', 10); // 123
parseInt('010', 10); // 10

If you want to convert a binary number to an integer, just change the base:

parseInt('11', 2); // 3

Similarly, you can parse floating point numbers using the built-in parseFloat() function. Unlike its parseInt()parseFloat() always uses base 10.

You can also use the unary + operator to convert values to numbers:

+ '42';   // 42
+ '010';  // 10
+ '0x10'; // 16

A special value called NaN (short for “Not a Number”) is returned if the string is non-numeric:

parseInt('hello', 10); // NaN

NaN is toxic: if you provide it as an operand to any mathematical operation, the result will also be NaN:

NaN + 5; // NaN

You can test for NaN using the built-in isNaN() function:

isNaN(NaN); // true

JavaScript also has the special values Infinity and -Infinity:

 1 / 0; //  Infinity
-1 / 0; // -Infinity

You can test for Infinity-Infinity and NaN values using the built-in isFinite() function:

isFinite(1 / 0); // false
isFinite(-Infinity); // false
isFinite(NaN); // false

The parseInt() and parseFloat() functions parse a string until they reach a character that isn’t valid for the specified number format, then return the number parsed up to that point. However the “+” operator simply converts the string to NaN if there is an invalid character contained within it.

Strings

Strings in JavaScript are sequences of Unicode characters (UTF-16). To find the length of a String (in code units), access its length property:

'hello'.length; // 5

You can use strings like objects. They have methods as well that allow you to manipulate the string and access information about the string

'hello'.charAt(0); // "h"
'hello, world'.replace('world', 'mars'); // "hello, mars"
'hello'.toUpperCase(); // "HELLO"

Dates

JavaScript comes with the built in Date object and related methods

The Date object is a built-in object in JavaScript that stores the date and time. It provides a number of built-in methods for formatting and managing that data.

By default, a new Date instance without arguments provided creates an object corresponding to the current date and time. This will be created according to the current computer’s system settings. JavaScript, however, understands the date based on a timestamp called epoch time and we can get it with getTime()

// Set variable to current date and time
const now = new Date();
// View the output
console.log("" + now); // "Mon Mar 02 2020 09:57:22 GMT+0100 (hora estándar de Europa central)"

console.log(now.getTime()); //1583139516795

Different ways to create Dates

Date CreationOutput
new Date()Current date and time
new Date(timestamp)Creates date based on milliseconds since Epoch time
new Date(date string)Creates date based on date string
new Date(year, month, day, hours, minutes, seconds, milliseconds)Creates date based on specified date and time

We can modify Date properties after the creation with set methods

Date/TimeMethodRangeExample
YearsetFullYear()YYYY1970
MonthsetMonth()0-110 = January
Day (of the month)setDate()1-311 = 1st of the month
Day (of the week)setDay()0-60 = Sunday
HoursetHours()0-230 = midnight
MinutesetMinutes()0-59
SecondsetSeconds()0-59
MillisecondsetMilliseconds()0-999
Timestamp setTime() Milliseconds since Epoch time

And we have several methods to get the Date information

Date/TimeMethodRangeExample
YeargetFullYear()YYYY1970
MonthgetMonth()0-110 = January
Day (of the month)getDate()1-311 = 1st of the month
Day (of the week)getDay()0-60 = Sunday
HourgetHours()0-230 = midnight
MinutegetMinutes()0-59
SecondgetSeconds()0-59
MillisecondgetMilliseconds()0-999
Timestamp getTime() Milliseconds since Epoch time

However, these get methods retrieve the date components based on the user’s local timezone settings. For increased control we can use the getUTC methods that calculate the time based on the UTC standard

Date/TimeMethodRangeExample
YeargetUTCFullYear()YYYY1970
MonthgetUTCMonth()0-110 = January
Day (of the month)getUTCDate()1-311 = 1st of the month
Day (of the week)getUTCDay()0-60 = Sunday
HourgetUTCHours()0-230 = midnight
MinutegetUTCMinutes()0-59
SecondgetUTCSeconds()0-59
Millisecond getUTCMilliseconds() 0-999

Other types

JavaScript distinguishes between null, which is a value that indicates a deliberate non-value (and is only accessible through the null keyword), and undefined, which is a value of type undefined that indicates an uninitialized variable — that is, a value hasn’t even been assigned yet.

JavaScript has a boolean type, with possible values true and false (both of which are keywords.) 

Truthy and Falsy

When an expression expects a Boolean value, the following values are always treated as false. 

  • false (of course)
  • 0 (the number zero)
  • "" or '' (an empty string)
  • null
  • undefined
  • NaN (the result of failed mathematical operations)

false is false (of course!). The rest of these values are coerced to false. As a group they are referred to as falsy

There’s no question that true is just that—true. But any other value of any type is coerced to true. We call these values truthy

This is widely accepted

const myRecord = response.getReturnValue();
if (myRecord) {
  //now process the record
}

Boolean operations such as && (logical and), || (logical or), and ! (logical not) are supported;

Coercion

When most JavaScript operators encounter an invalid type, they attempt to convert the value to a valid type. This process of implicitly converting a type is called implicit type coercion.

let num1 = 9 * "3";
console.log(num1); // 27 (a number)
let num2 = 9 + "3";
console.log(num2); // "93" (a string)

Don’t Use Implicit Type Coercion

false == ""; // true
false == "0"; // true
"" == "0"; // false
[0] == 0; // true

For Boolean comparison the best practice is to use === and !==. With these operators, primitive types are only equivalent when both type and value match, and object comparisons are only true when their respective pointers are pointing to the same memory address. Trying the same comparisons as above: 

false === ""; // false
false === "0"; // false
"" === "0"; // false
[0] === 0; // false

Arrays

Arrays are list-like objects whose prototype has methods to perform traversal and mutation operations. Neither the length of a JavaScript array nor the types of its elements are fixed. Since an array’s length can change at any time, and data can be stored at non-contiguous locations in the array, JavaScript arrays are not guaranteed to be dense;

Arrays cannot use strings as element indexes (as in an associative array) but must use integers. 

Setting or accessing via non-integers using bracket notation (or dot notation) will not set or retrieve an element from the array list itself, but will set or access a variable associated with that array’s object property collection.

Create Array

let fruits = ['Apple', 'Banana']
console.log(fruits.length); // 2

Access (index into) an Array item

let first = fruits[0]; // Apple
let last = fruits[fruits.length - 1]; // Banana

Iterate

fruits.forEach(function(item, index, array) {
  console.log(item, index);
})
// Apple 0
// Banana 1

Add to the end of an Array

let newLength = fruits.push('Orange');
// ["Apple", "Banana", "Orange"]

Remove from the end of an Array

let last = fruits.pop(); // remove Orange (from the end)
// ["Apple", "Banana"]

Remove from the front of an Array

let first = fruits.shift(); // remove Apple from the front
// ["Banana"]

Add to the front of an Array

let newLength = fruits.unshift('Strawberry'); // add to the front
// ["Strawberry", "Banana"]

Find the index of an item in the Array

fruits.push('Mango'); // ["Strawberry", "Banana", "Mango"]
let pos = fruits.indexOf('Banana'); // 1

Remove an item by index position

let removedItem = fruits.splice(pos, 1); // this is how to remove an item
// ["Strawberry", "Mango"]

Copy an Array

let vegetables = ['Cabbage', 'Turnip', 'Radish', 'Carrot'];
console.log(vegetables); // ["Cabbage", "Turnip", "Radish", "Carrot"]
let shallowCopy = vegetables.slice();
console.log(shallowCopy); // ["Cabbage", "Turnip", "Radish", "Carrot"]

Accessing array elements

JavaScript arrays are zero-indexed: the first element of an array is at index 0, and the last element is at the index equal to the value of the array’s length property minus 1.

There is nothing special about JavaScript arrays and the properties that cause this. JavaScript properties that begin with a digit cannot be referenced with dot notation, and must be accessed using bracket notation.

let years = [1950, 1960, 1970, 1980, 1990, 2000, 2010];
console.log(years.0);  // a syntax error
console.log(years[0]);  // works properly
renderer.3d.setTexture(model, 'character.png');  // a syntax error
renderer['3d'].setTexture(model, 'character.png');  // works properly

When setting a property on a JavaScript array when the property is a valid array index and that index is outside the current bounds of the array, the engine will update the array’s length property accordingly:

fruits[5] = 'mango'
console.log(fruits[5])            // 'mango'
console.log(Object.keys(fruits))  // ['0', '1', '2', '5']
console.log(fruits.length)        // 6

JSON

JSON is a text-based data format following JavaScript object syntax. Even though it closely resembles JavaScript object literal syntax, it can be used independently from JavaScript.

JSON exists as a string — useful when you want to transmit data across a network. It needs to be converted to a native JavaScript object when you want to access the data. This is not a big issue —  JavaScript provides a global JSON object that has methods available for converting between the two.

Accesing data

let myJSON = { "name": "Chris", "age": "38" };
console.log(myJSON['age']); // 38
console.log(myJSON['name']); // Chris

If we have to deal with a complex JSON object we can traverse the properties like this

staffJSON['members'][1]['roles'][2];

Convert String to JSON

request.open('GET', requestURL);
request.responseType = 'text'; // now we're getting a string!
request.send();

request.onload = function() {
  const superHeroesText = request.response; // get the string from the response
  const superHeroes = JSON.parse(superHeroesText); // convert it to an object
  populateHeader(superHeroes);
  showHeroes(superHeroes);
}

Convert JSON to String

let myJSON = { "name": "Chris", "age": "38" };
console.log(typeof myJSON); // object
let myString = JSON.stringify(myJSON);
console.log(typeof myString); // string

Objects, Functions, and Classes

Objects

Syntactically speaking, there are several ways to create an object in JavaScript. But no matter how you create an object, it’s actually abstracting an underlying API called Object.create().  

Object Literal Notation

const bike = {
  gears: 10,
  currentGear: 3,
  changeGear: function(direction, changeBy) {
    if (direction === 'up') {
      this.currentGear += changeBy;
    } else {
      this.currentGear -= changeBy;
    }
  }
}
console.log(bike.gears); // 10
console.log(bike.currentGear); //3
bike.changeGear('up', 1);
console.log(bike.currentGear); //4

Literal objects are great for one-off objects. But if you want to create two or more objects of the same type, they are not practical

Constructors

Another way to create objects is to use a constructor. A constructor is a function that contains instructions for establishing the properties of an object when that object is created and assigned. This has an advantage over object literal as you can create many instances of objects that have the same properties. In JavaScript, what this points to is not determined by where a function is defined, but rather where that function is called

function Bike(gears, startGear) {
  this.gears = gears;
  this.currentGear = startGear;
}
Bike.prototype.changeGear = function(direction,changeBy){
  if(direction === 'up') {
    this.currentGear += changeBy;
  } else {
    this.currentGear -= changeBy;
  }
}
const bike = new Bike(10, 3);
console.log(bike.gears); // 10
console.log(bike.currentGear); //3
bike.changeGear('up', 1);
console.log(bike.currentGear); //4

In this example Bike is a normal JavaScript function that defines the object. We follow JavaScript convention and capitalize the first word to signal that this function is a constructor.

The new keyword is critical. Without new, the this pointer will not point to the object you expect and causes unintended behavior. 

Notice the assignment of the changeGear function is done using something called prototype. This ensures the function is defined once and shared by all instances created from this constructor.

Assigning Properties and Functions to Objects

Properties come in three basic shapes:

  • Primitives
  • Objects
  • Arrays

There are six primitive types in JavaScript: string, number, Boolean, nullundefined, and symbol. When a variable is a primitive type, it’s passed by value when assigned. That is to say, each time a primitive is assigned, a copy of the value is made and assigned to the new variable. 

Pretty much anything that isn’t a primitive in JavaScript is an object. In object literal notation, object properties are denoted by curly brackets. 

Arrays themselves are also implemented as objects in JavaScript. Arrays can be created with the Array() constructor function or with literal notation denoted by square brackets. 

Referencing Properties by Bracket Syntax

Referencing an object member (properties and functions) is most commonly done with dot notation

bike.frontGearIndex
bike.transmission.frontGearTeeth
bike.calculateGearRatio()

In dot notation, there are strict rules for the names of properties. However, JavaScript also allows for another syntax called bracket notation.

bike["frontGearIndex"]
bike["transmission"]["frontGearTeeth"]
bike["calculateGearRatio"]()

Object Mutability

part from the different syntaxes for defining objects, there’s another key principle of JavaScript objects: mutability. 

Objects in JavaScript are mutable, which means that if you want to modify the shape of an object, you can. 

Let’s take the bike object we created. We could, for instance, add a new property or function. 

bike.isTandem = true;
bike.popAWheelie = function() {
…
};

Even though you may not have access to the code where the object is initially defined, you can modify the shape of your object once it’s in memory. The important point, though, is that only one instance of the object changes. Let’s look back at our Bike constructor:

const bike1 = new Bike();
const bike2 = new Bike();
bike1.isTandem = true;
console.log(bike1.isTandem); // true
console.log(bike2.isTandem); // undefined

If you want several objects to share the same properties or methods, then there’s an inheritance model.

Objects and Inheritance

Despite not having classes as defined by classical languages, JavaScript still has an inheritance model, called prototype inheritance

A prototype is, in fact, another object. It sits in memory, and defines properties or functions that other objects inherit if they share the same prototype. 

Traditionally in JavaScript objects share the same prototype by sharing the same constructor function. Remember the Bike constructor. We assign the changeGear function to something called prototype

function Bike(gears, startGear) {
  this.gears = gears;
  this.currentGear = startGear;
}
Bike.prototype.changeGear = function(direction, changeBy) {
  if (direction === 'up') {
    this.currentGear += changeBy;
  } else {
    this.currentGear -= changeBy;
  }
}

This way every object created from Bike inherits the changeGear function. 

You can also implement multilevel inheritance with prototypes. It is referred to as a prototype chain.

Classes and JavaScript

Under the covers, the engine is still using Object.create and there is still no class (in the object-oriented sense), just that in-memory prototype object that is the actual source of inheritance. 

class Bike {
    constructor(gears, startGear){
        this.gears = gears;
        this.currentGear = startGear;
    }
    changeGear(direction, changeBy) {
        if (direction === 'up') {
            this.currentGear += changeBy;
        } else {
            this.currentGear -= changeBy;
        }
    }
}
const bike = new Bike(10, 5);
console.log(bike.currentGear); // 5
bike.changeGear('up', 2);
console.log(bike.currentGear); // 7

The constructor function is always named constructor. An important feature is that functions and attributes automatically belong to the prototype chain without having to directly reference Object.prototype. This also simplifies creating multilevel prototype inheritance.

What is important to know is that even though the class keyword is used, the underlying object created is still a function. Executing the following code would show “function” and not “class” in the console as the type.

console.log(typeof Bike);  // Display "function"

Another difference is that function declarations can be hoisted. In other words, you can call a function that has yet to be declared. Classes do not allow this sort of thing. A class can only be accessed after its definition is evaluated. 

Classes can contain the following kinds of members.

Member
Description
Example
Constructor
The constructor is executed automatically when creating a new instance of the class. It guarantees that an initialization function is always called. This helps maintain a valid state for the class. But you don’t have to create a constructor. If one is not included, then the JavaScript engine creates an empty one for you.
constructor(name) {
  this.name = name;
}

Static Methods
Static methods are not part of any instance of the class, meaning that you can refer to these methods without referring to an instance. The concept of static class members is not new to ES6, but the static keyword is. Prior to ES6, you had to put any methods you wanted static in the constructor. Now you can put them wherever you want in the class and just use the static keyword.
static methodName() {
  return ‘something’;
}

Prototype Methods
These methods do not include the static keyword and must be referenced with an instance.
printName() {
 console.log(this.name);
}

Getters and Setters
These accessor functions work just like object literals and work the same as they did in ES5. Essentially you just put the get and set keywords in front of the property name. If you define a getter without a setter, then the property becomes read-only.
get area() {
  return this.height * this.width;
 }

set area(value) {
  this.area = value;
}

Class inheritance

Classes come in two flavors: base classes and derived classes. The difference is the extends keyword. Derived classes (also known as subclasses) have them, and base classes don’t. 

class Parent {
  constructor(name) {
    this.name = name;
  }
      
  getName() {
    return this.name;
  }
}  
class Child extends Parent {
  constructor(name) {
    super(name);
  }
        
  getMessage() {
    return 'Hello ' + super.getName();
  }
}
      
let someone = new Child('person');
console.log(someone.getMessage());     // Displays "Hello person"  

notice the use of the super keyword, which allows you to reference the parent constructor and the method definitions from the Base class.

Although commas are used to separate method definitions in objects, they are not allowed in classes. Classes can also be defined using expressions:

const myAnimal = class Animal {
  constructor(name) {
    this.name = name;
  }
          
  printName() {
    console.log(this.name);
  }
}  
  
let duck = new myAnimal('duck');
duck.printName();  // prints "duck"  

Functions

In JavaScript functions are essentially special objects. As objects, they are first-class members of JavaScript. They can be assigned as the values of variables, passed into other functions as parameters, and returned from functions. 

There are two essential phases in the life of a function: definition and invocation.  

When function is declared, its definition is loaded into memory. A pointer is then assigned to it in the form of a variable name, parameter name, or an object property.

Function Declaration

A  declaration is a statement that uses the function keyword to create a function.

// declare function
function calculateGearRatio(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
// call function
let gearRatio = calculateGearRatio(42, 30);
console.log(gearRatio); // 1.4

In this code sample, function is followed by the name of the function, with parameters enclosed in the parentheses. 

This works fine, but there’s some implicit stuff happening. First of all, the function name becomes the variable name. It also implicitly assigns the variable to the enclosing context. Finally, you can call this function before it is declared, such as below where calculateGearRatio is invoked the line before the declaration. 

// call function
let gearRatio = calculateGearRatio(42, 30);
// function is declared after the line it is called
// this is allowed in function declaration
function calculateGearRatio(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
console.log(gearRatio); // 1.4

Function Expressions

Function expressions accomplish the same thing as declarations more explicitly. 

const calculateGearRatio = function(driverGear, drivenGear){
  return (driverGear / drivenGear);
}
// the rest works the same
let gearRatio = calculateGearRatio(42, 30);
console.log(gearRatio); // 1.4

n this instance we have an explicitly assigned variable. Since we’ve named the pointer, we can drop the function name after the function keyword. The only catch here is that the function must be declared prior to it being invoked. 

Returning a Function

Since functions are first-class objects, another way to declare a function is when a function returns another function. This pattern is often referred to as a factory function. 

// when invoked, this function will assign a function
function gearFactory(){
  return function(driverGear, drivenGear){
    return (driverGear / drivenGear);
  }
}
// calculateGearRatio can now be invoked as a function
const calculateGearRatio = gearFactory();
// and all the rest

Anonymous Functions

There are many APIs in JavaScript that require you to pass a function for them to work. Say, for instance, you have an array, and you want to create a new array derived from the values of that array. In this case you would probably use the Array.map function.

let myArray = [1, 5, 11, 17];
let newArray = myArray.map( function(item){ return item / 2 } );
console.log(newArray); // [0.5, 2.5, 5.5, 8.5]

In this snippet, myArray.map takes in a single parameter: a function that is executed once per item in myArray

This function is never reused. It is declared as an argument (with no name…thus “anonymous”) passed into the function, and is executed in the internals of the implementation of the map function. Anonymous functions (also called lambdas in some languages) are commonplace in JavaScript. 

Function Invocation

Invocation Versus Assignment

When working with functions, one potential source of confusion to those new to JavaScript is whether you are assigning/passing a function or invoking it. It all comes down to whether you use the ()

// invoke function and assign value to ratioResult
let ratioResult = bike.calculateGearRatio();
// assign calculateGearRatio function to a new pointer
const ratioFunction = bike.calculateGearRatio;

In the first instance, calculateGearRatio is invoked with the result returned from the function being assigned (in this instance as a primitive value) to the ratioResult variable. On the other hand ratioFunction has simply been assigned or pointed to the calculateGearRatio function. You could then turn around and invoke it as ratioFunction.

Context, Scope, and Closures

Variable Scope

Variables in JavaScript are declared using the varlet, or const keywords. Where you call the keyword dictates the scope of variable being created. 

When using let or const, an argument’s or a variable’s scope is always the actual block in which it is declared. There is a classic thought exercise to show this: https://jsbin.com/gukeyic/4/edit?js,console,output

function countToThree() {
  // i is in the scope of the countToThree function
  for (var i = 0; i < 3; i++){
    console.log(i); // iteration 1: 0
    // iteration 2: 1
    // iteration 3: 2
  }
  console.log(i); // 3
}

While not necessarily bad, hoisting is often misunderstood and can create variable leakage or cause accidental overwrites if a variable is redeclared in a code block. To address these misunderstandings let and const were added to the language to create variables with block-level scope. Let’s revisit the thought exercise.

for (let j = 0; j < 3; j++){
  console.log(j); // 0
  // 1
  // 2
}
console.log(j); // error

By substituting let for var, we now have a variable that exists only in the context of the for loop. Trying to access it after the loop has closed gives us an error. 

Context and this

Objects are where state is tracked. When a function is invoked, there is always an object container around that function. This object container is its context and the this keyword points to that context. So context is not set when a function is declared, but rather where the function is invoked

Because functions can be passed around between objects, what this points to can change.

var obj = {
  aValue: 0,
  increment: function(incrementBy) {
    this.aValue = this.aValue + incrementBy;
  }
}

If you then access the increment function, it works as expected.

obj.increment(2);
console.log(obj.aValue); // 2

But let’s assign that function to another variable and see how it works. 

//assign function to variable
var newIncrement = obj.increment;
//now invoke through the new pointer
newIncrement(2);
console.log(obj.aValue); // still 2 not 4

By assigning the variable to newIncrement, the function now is executed in a different context. Specifically, in this case, in the global context. 

The Function.apply()Function.call(), and Function.bind() functions provide ways to invoke a function while explicitly binding it to a different object context.

The Global Object

When JavaScript is executed without any containing object that you write as a developer, it runs in a global object. For this reason, functions invoked there are said to be running in the global context, which means that accessing this will point there. 

In a browser, the global context is the window object. You can test this easily by running the following in your browser developer tools. 

this === window; // true

Context with Your Objects

In the increment example, as long as the increment function is invoked using obj with the dot notation, this points to obj. Or, generally speaking, when calling a function as object.function() the thing to the left of the dot is always the context in which that function is invoked. 

Think about the Bike example. The Bike constructor defines several properties with the this reference. It also has functions assigned to its prototype that reference this

const Bike = function(frontIndex, rearIndex){
  this.frontGearIndex = frontIndex || 0;
  this.rearGearIndex = rearIndex || 0;
  ...
}
...
Bike.prototype.calculateGearRatio = function(){
  let front = this.transmission.frontGearTeeth[this.frontGearIndex],
  rear = this.transmission.rearGearTeeth[this.rearGearIndex];
  if (front && rear) {
    return (front / rear) ;
  } else {
    return 0;
  }
};

We then call Bike with the new keyword. 

const bike = new Bike(1,2);
console.log(bike.frontGearIndex); // 1
console.log(bike.rearGearIndex); // 2

This looks like we’re invoking the Bike constructor in the global context. However, the new keyword shifts the context (and the this pointer) to the new object on the left side of the assignment. 

When we invoke any of the functions they are now members of the bike object, so they use that as the containing context. 

let gearRatio = bike.calculateGearRatio();
console.log(gearRatio); // 3

It’s easy to invoke a constructor the wrong way. Here’s where things can fall apart.

const badBike = Bike(1,2);
console.log(badBike.frontGearIndex); // error
console.log(window.frontGearIndex); // 1

When you forget to use new, Bike is called like any other function and that crucial shift of this from window to the newly created object fails. Object mutability steps in and the frontGearIndex property is added to window instead

The class syntax in JavaScript forces you to invoke a constructor with the new keyword, so you can’t misdirect your context.

Closures

When a function is declared, it holds a reference to any variables or arguments declared within it, and any variables it references in the scope that it is contained within. This combination of its variables and arguments along with local variables and arguments from its containing scope is called a closure. 

Consider this function, and the function it returns. 

const greetingMaker = function(greeting){
  return function(whoGreeting){
    return greeting + ", " + whoGreeting + "!";
  }
}
const greetingHello = greetingMaker("Hello");
const greetingBonjour = greetingMaker("Bonjour");
const greetingCiao = greetingMaker("Ciao");
console.log(greetingHello("Gemma")); // Hello, Gemma!
console.log(greetingBonjour("Fabien")); // Bonjour, Fabien!
console.log(greetingCiao("Emanuela")); // Ciao, Emanuela!

When greetingMaker is invoked, we might normally imagine its greeting argument to last only for the life of it being called. 

But the returned function keeps a reference to the greeting argument in greetingMaker’s scope. So that finally when it is invoked through greetingHello/Bonjour/Ciao, it’s still accessible. 

Modules

ES6 introduced a long-overdue native module system. But it was separate from all the other ES6 functionality and for a long time, no major browsers supported it. However, that’s finally changed and now most browsers allow you to load ES6 modules with the type="module" attribute on the HTML5 script tag.

Modules are pretty simple to create and use. An ES6 module is essentially just a file containing some JavaScript. Everything inside the module is scoped to that module only. If you want to make something—like a function, a variable, or a class—available somewhere else, you need to use an export statement. You then use an import statement to specify what you want to use from the exported module.

Using variables and functions from other files:

import { printMsg } from './module1.js';
import { msg2, msg1 } from './module2.js';
printMsg(msg1 + msg2);    

module1.js:

export function printMsg(message) {
  const div = document.createElement('div');
  div.textContent = message;
  document.body.appendChild(div);
}

module2.js

let msg1 = 'Hello World! ';
let msg2 = 'This message was loaded from a module.';
export { msg1, msg2 };

It is feasible to rename imported variables

import { msg2, msg1 as msg3 } from './module2.js';
printMsg(msg3 + msg2);

If you just want to import everything from a module and not worry about naming any of the exports. You can do that too by using an asterisk:

import { printMsg } from './module1.js';
import * as message from './module2.js';
printMsg(message.msg1 + message.msg2);  

When referring to module exports, we call them named exports. Just the name gets exported, and you can see this for yourself. If you export a variable and then try to change the value in the imported module, you get an error. Essentially, it’s read-only.

Decorators

Decorators have become popular thanks to their use in Angular 2+. In Angular, decorators are available thanks to TypeScript, but in JavaScript they’re currently a stage 2 proposal, meaning they should be part of a future update to the language.

In its simplest form, a decorator is simply a way of wrapping one piece of code with another — literally “decorating” it. This is a concept you might well have heard of previously as functional composition, or higher-order functions.

This is already possible in standard JavaScript for many use cases, simply by calling on one function to wrap another:

function doSomething(name) {
  console.log('Hello, ' + name);
}

function loggingDecorator(wrapped) {
  return function() {
    console.log('Starting');
    const result = wrapped.apply(this, arguments);
    console.log('Finished');
    return result;
  }
}

const wrapped = loggingDecorator(doSomething);

This example produces a new function — in the variable wrapped — that can be called exactly the same way as the doSomething function, and will do exactly the same thing. The difference is that it will do some logging before and after the wrapped function is called:

doSomething('Graham');
// Hello, Graham

wrapped('Graham');
// Starting
// Hello, Graham
// Finished

Decorators use a special syntax in JavaScript, whereby they are prefixed with an @ symbol and placed immediately before the code being decorated.

It’s possible to use as many decorators on the same piece of code as you desire, and they’ll be applied in the order that you declare them.

@log()
@immutable()
class Example {
  @time('demo')
  doSomething() {
    //
  }
}

This defines a class and applies three decorators — two to the class itself, and one to a property of the class:

  • @log could log all access to the class
  • @immutable could make the class immutable — maybe it calls Object.freeze on new instances
  • @time will record how long a method takes to execute and log this out with a unique tag.

Decorators also allow for a cleaner syntax for applying these wrappers around your code, resulting in something that detracts less from the actual intention of what you’re writing. Decorator provides a good way of writing some simple helper code that can be applied to a lot of places in a very clean and easy-to-understand manner.

Browser and Events

Events, event handlers and propagation

Events are actions or occurrences that happen in the system you are programming — the system produces (or “fires”) a signal of some kind when an event occurs, and also provides a mechanism by which some kind of action can be automatically taken (that is, some code running) when the event occurs.

Some examples:

  • The user clicking the mouse over a certain element or hovering the cursor over a certain element.
  • The user pressing a key on the keyboard.
  • The user resizing or closing the browser window.
  • A web page finishing loading.

Each available event has an event handler, which is a block of code (usually a JavaScript function that you as a programmer create) that will be run when the event fires. When such a block of code is defined to be run in response to an event firing, we say we are registering an event handler. Note that event handlers are sometimes called event listeners — they are pretty much interchangeable for our purposes, although strictly speaking, they work together. The listener listens out for the event happening, and the handler is the code that is run in response to it happening.

Web events are not part of the core JavaScript language — they are defined as part of the APIs built into the browser.

Example:

<button>Change color</button>
const btn = document.querySelector('button');

function random(number) {
  return Math.floor(Math.random() * (number+1));
}

btn.onclick = function() {
  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
  document.body.style.backgroundColor = rndCol;
}

Functions as Event Handlers

If you want a function to fire as the result of an event, it needs to be wired up to that event. Doing so makes that function an event handler. The function definition needs to include a single argument: a pointer to the event that fired it. 

var handleClick = function(event) {
  console.log(event.type);  // click
  console.log(event.currentTarget); // the thing you clicked
  console.log(event.screenX); // screen X coordinate
  console.log(event.screenY); // screen Y coordinate
}

Assigning Event Handlers via DOM APIs

In simple web pages you may occasionally see explicit assigned event handlers in the HTML. 

<button onclick="handleClick(event)">
  Click to Go
</button>

However modern web applications rarely use event binding in HTML. Instead, the DOM API is preferred, specifically the JavaScript Element.addEventListener() function. 

First you need a reference to the HTML element. Below we’ve added an id attribute to our button and removed the onclick attribute. 

<button id="clicker">

Now we reach into the DOM, get the reference to the button, and assign the event listener, handleClick, by passing it in as a value (note, no parentheses). 

let button = document.getElementById("clicker");
button.addEventListener("click", handleClick);

Using the DOM API gives the developer flexibility to make the UI highly interactive and responsive to what the user is doing. The developer can also remove an event listener if functionality needs to be turned off. 

button.removeEventListener("click", handleClick);

You will also see anonymous functions added as event listeners. 

button.addEventListener("click", function(event){
  //...anonymous function body...
});

Bear in mind, anonymous functions can’t be removed using removeEventListener, as there is no pointer to pass in to identify the function. 

In the increment example, assigning the increment function to the newIncrement variable moves the context where it is invoked: to the global object. This is easy to demonstrate.

console.log(this.aValue); // NaN
console.log(window.aValue); // NaN
console.log(typeof window.aValue); // number

Default Behaviour

Sometimes, you’ll come across a situation where you want to prevent an event from doing what it does by default. The most common example is that of a web form, for example, a custom registration form. When you fill in the details and press the submit button, the natural behavior is for the data to be submitted to a specified page on the server for processing, and the browser to be redirected to a “success message” page of some kind (or the same page, if another is not specified.). For this purpose we use the preventDefault() function.

Example: preventing a form from being submited if either First Name or Last Name are blank:

const form = document.querySelector('form');
const fname = document.getElementById('fname');
const lname = document.getElementById('lname');
const para = document.querySelector('p');

form.onsubmit = function(e) {
  if (fname.value === '' || lname.value === '') {
    e.preventDefault();
    para.textContent = 'You need to fill in both names!';
  }
}

Bubbling and Capturing

When an event is fired on an element that has parent elements (in this case, the <video> has the <div> as a parent), modern browsers run two different phases — the capturing phase and the bubbling phase.

In the capturing phase:

  • The browser checks to see if the element’s outer-most ancestor (<html>) has an onclick event handler registered on it for the capturing phase, and runs it if so.
  • Then it moves on to the next element inside <html> and does the same thing, then the next one, and so on until it reaches the element that was actually clicked on.

In the bubbling phase, the exact opposite occurs:

  • The browser checks to see if the element that was actually clicked on has an onclick event handler registered on it for the bubbling phase, and runs it if so.
  • Then it moves on to the next immediate ancestor element and does the same thing, then the next one, and so on until it reaches the <html> element.

In modern browsers, by default, all event handlers are registered for the bubbling phase. So in our current example, when you click the video, the click event bubbles from the <video> element outwards to the <html> element. Along the way:

  • It finds the video.onclick... handler and runs it, so the video first starts playing.
  • It then finds the videoBox.onclick... handler and runs it, so the video is hidden as well.

This is annoying behavior, but there is a way to fix it! The standard Event object has a function available on it called stopPropagation() which, when invoked on a handler’s event object, makes it so that first handler is run but the event doesn’t bubble any further up the chain, so no more handlers will be run.

video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};

Document Object Model

Think of the DOM as a tree. It starts at the root of the browser’s display functionality: the window. From there, the page is encapsulated in window.document, with the page’s body in window.document.body. Then the tree fans out to every bit of content represented on the page.

As an API, the DOM is vast, and lets you touch every part of this tree. It also has a number of methods to optimize interaction with the DOM.

Example: https://jsbin.com/kejonuq/26/edit?html,js,output

JavaScript libraries (reactjs, jQuery) and frameworks (Angular, vuejs) have become the standard for interactive pages. Such frameworks abstract away and simplify DOM interactions, and often automatically apply polyfills for missing features.

Shadow DOM

The DOM API is rich and flexible. Using relatively simple JavaScript it is easy to make changes to the looks, behaviors, and actions invoked by the UI. 

But there is a pitfall. The DOM model makes it difficult to encapsulate pieces of the UI and protect them from accidental (or purposeful and malicious) changes. 

Shadow DOM creates a boundary around a particular part of UI functionality. The boundary prevents a parent from changing the elements or CSS of a child. It also forces any events propagated across the boundary to rescope their targets, preventing the parent from reaching across the shadow DOM boundary.

Web APIs

JavaScript runtime engine runs in many different places, but it’s most often hosted in a browser

browser APIs can: 

  • Interact with the structure of the current page rendered in the browser (Document Object Model or DOM API)
  • Perform asynchronous requests to the server without leaving the current page (Fetch API)
  • Interact with audio, video, and graphics
  • Interact with device features surfaced to the browser (geolocation, device orientation, client-side data storage)

Browser Dev Tools

Every modern web browser includes a powerful suite of developer tools. These tools do a range of things, from inspecting currently-loaded HTML, CSS and JavaScript to showing which assets the page has requested and how long they took to load.


Exploring the DOM inspector

Exploring the CSS editor

By default, the CSS editor displays the CSS rules applied to the currently selected element:

JavaScript debugger

The JavaScript debugger allows you to watch the value of variables and set breakpoints, places in your code that you want to pause execution and identify the problems that prevent your code from executing properly.

In the image, the first section, Watch expressions, shows that the listItems variable has been added. You can expand the list to view the values in the array.

The next section, Breakpoints, lists the breakpoints set on the page. In example.js, a breakpoint has been set on the statement listItems.push(inputNewItem.value);

The final two sections only appear when the code is running.

The Call stack section shows you what code was executed to get to the current line. You can see that the code is in the function that handles a mouse click, and that the code is currently paused on the breakpoint.

The final section, Scopes, shows what values are visible from various points within your code. For example, in the image below, you can see the objects available to the code in the addItemClick function.

Debugging and Error Handling

Before going into examples, let’s make sure that we know some basics. I highlighted four types of errors in JavaScript:

Syntax errors occur at the interpretation time, they are considered as bugs and can never be handled properly because the code is just wrong by definition:

  var x = 2;
  var y = 3;

  console.log(x + y;

2. Program error, which is also a bug. For example, trying to read a property of undefined — it can be avoided by improving the code in this particular case by adding a conditional statement.

   function power(base, exponent) {
    var result = 1;
    for (var count = 0; count < exponent; count++)
      result *= base;
    return result;
  }

What if someone tries to call power (``"``JavaScript``"``, 10)?

3. Runtime errors are called exceptions which happen during the execution:

  • invalid user input
  • failed to connect to a database
  • system out of memory
  • etc.
  var windowObject;
  var windowFeatures = "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes";

  function openPopup() {
    windowObject = window.openObject("http://www.bbc.com/", "BBC_WindowName", strWindowFeatures);
  }

The function above will cause a runtime error because although the syntax is correct, at runtime it is trying to call a method openObject() that doesn’t exist.

Best Practices for Handling Errors

One common criticism of JavaScript is the way errors are handled through callbacks. Check out the pattern in which the code below is written:

 var myFunc = function(cb) {
    doSomething(function (err, a) {
      if (err) return cb(err)
      doSomethingElse(function (err, b) {
        if (err) return cb(err)
        return cb(null, [a, b])
      })
    })
  }

There are quite a number of ways and methods to deal with errors and exceptions.

The try…catch…finally statement marks a block of statements to run in your code and specifies a response should an exception be thrown. It s possible to catch logical and runtime errors but not syntax errors. Below is the syntax for the try…catch…finally statement:

try {
    // Code to run
    [
      break;
    ]
  } catch (e) {
    // Code to run if an exception occurs
    [
      break;
    ]
  }
  [
    finally {
      // Code that is always executed regardless of 
      // an exception occurring
    }
  ]

try statements are the statements to be executed. If an exception occurs during the execution of the try statement, the exception is placed in e and the catch clause is executed otherwise, the catch clause is skipped. The finally clause executes after the try statement is finished, it executes regardless of whether or not an exception was thrown or caught.

The throw statement is used to generate user-defined exceptions. During runtime, when a throw statement is encountered, execution of the current function will stop and control will be passed to the first catch clause in the call stack. If there is no catch clause, the program will terminate. 

The onerror() method was the first event handler to facilitate and handle errors in JavaScript. It is often used with the syntax window.onerror. This enables the error event to be fired on the window object whenever an error occurs during runtime.

  <img src="coolPhoto.jpg" onerror="alert('An error occurred loading yor photo.')" />

Handling Errors in Asynchronous Code

Usually errors in asynchronous code require a large amount of if… else checks and a careful inspection of parameter values. Promises allow asynchronous code to apply structured error handling. When using promises, you can process errors by passing an error handler to the then method or using a catch clause. Just like exceptions in regular code, an exception or rejection in asynchronous code will jump to the nearest error handler. 

var log = "";

  function doWork() {
    log += "W";
    return Promise.resolve();
  }

  function doError() {
    log += "E";
    throw new Error("oops!");
  }

  function errorHandler(error) {
    log += "H";
  }
  doWork()
    .then(doWork)
    .then(doError)
    .then(doWork) // this will be skipped
    .then(doWork, errorHandler)
    .then(verify);

  function verify() {
    expect(log)
      .toBe("This");
    done();
  }

What’s expected is that the log variable will contain “WWEH” when the code finishes executing, meaning the flow of calls with reach doWork , then doWork, then doError, then errorHandler. There are two things we can observe from this. The first is that when the call to doError throws an exception, execution jumps to the next rejection handler which is errorHandler and skips over any potential success handlers. 

A promise object also provides a catch clause to handle errors. Check out this example which is written using a catch clause:

doWork()
    .then(doWork)
    .then(doError)
    .then(doWork)
    .then(doWork)
    .catch(errorHandler)
    .then(verify);

The catch clause takes only a rejection handler method. There can be a difference in behavior between the following two code snippets:

 .then(doWork, errorHandler)

And

  .then(doWork)
  .catch(errorHandler)

In the first one, if the success handler throws an exception or rejects a promise, execution will not go into the error handler since the promise was already resolved at this level. With catch, you can always see an error that was not handled from the previous success handler. 

Asynchronous Programming

JavaScript engine with the stack, queue, events, and APIs
The event loop takes work from the queue and passes it to the stack

Apart from a few legacy bits that linger like the alert() function above, JavaScript is an asynchronous language. 

When an event fires, all that happens is a new message is added to the queue. No event has the ability to take over the thread. Each event fired must get in the queue and wait its turn to run. 

One way to illustrate this is by using the setTimeout function. In this example, invoking setTimeout, we pass an event handler and a timer in milliseconds. When the timer is up it fires, adding the event handler to the queue. 

setTimeout(function(){
  console.log("This comes first");
}, 0);
console.log("This comes second");
//output in console
// "This comes second"
// "This comes first"

Here we’ve set the timer to zero. But this doesn’t mean “call right away.” It just means “put this in the queue right away.” However the execution of the block of code itself needs to finish, clearing the call stack. Only then is the function from setTimeout given its turn. 

Another common mistake is to think the timer is an exact predictor of when the event handler will fire, but it isn’t necessarily. The event handler still has to wait its turn in the queue. By measuring time, we can see this in action

const timer  = function(){
  let start = Date.now();
  setTimeout(function(){
    let end = Date.now();
    console.log( "Duration: " + (end - start) )
  },1000);
};
timer();
// Console output when invoked several times:
// "Duration: 1007"
// "Duration: 1000"
// "Duration: 1002"
// "Duration: 1004"

The time is set to one second, and it runs pretty close to that. But clearly there is some variance in how quickly the function can be added to the queue and then run each time it’s called. 

Callback Pattern

A callback is simply a function passed into another function that invokes it at some point in the future. 

setTimeout(callback, timer)
Element.addEventListener(event, callback)
Array.map(function(item){...})

Let’s apply this to our bike use case to see how callbacks are implemented.

Bike.prototype.changeGearAsync = function(shiftObject, callback){
  let newIndex = shiftObject.currentIndex + shiftObject.changeBy;
  if (newIndex < 0 || newIndex > shiftObject.maxIndex) {
    callback(new Error("There is a problem"), null);
  } else {
    callback(null, newIndex);
  }
};

The argument callback is actually a function. If there’s an error, we invoke it and set the first argument with whatever error data we want to send back. On success, we null the error argument and pass back the good data.

Let’s invoke it

  // invoke async function with anonymous callback
  this.changeGearAsync(shiftObject, function(err, newIndex){
    if (err) {
      console.log("No Change");
    } else {
      that[shiftIndexName] = newIndex;
    }
  });

The callback pattern was widely accepted and used extensively, but it has some drawbacks. First, when several callbacks are chained together, they are nested one inside the other. This creates undue complexity, readability problems, and is difficult to reason about when reading someone else’s code. This flaw is known as callback hell. Callbacks also have no implicit error state (like try/catch does). It is up to the developer writing the callback to explicitly look for an error with an if condition. These obstacles led to the creation of promises. 

Arrow Functions

Arrow function syntax looks like this:

(arg1, arg2) => {...function body...}

Using an arrow function, we can remove the relic of older JavaScript  that = this bit and change the invocation of changeGearsAsync to the following.

  // the anonymous function is now an arrow function
this.changeGearAsync(shiftObject, (err, newIndex)=>{
  if (err) {
    console.log("No Change");
  } else {
    // we reference this instead of that
    this[shiftIndexName] = newIndex;
  }
});

Promises

Promises developed as libraries to handle asynchronous code in a way that made it easier to reason about when your code succeeded or failed. They also contain built-in mechanisms to chain one call after the other. Competing libraries eventually standardized in the browser as the Promise object

Bike.prototype.changeGearAsync = function(shiftObject){
  return new Promise(
    (resolve, reject) => {
      let newIndex = shiftObject.currentIndex + shiftObject.changeBy;
      if (newIndex < 0 || newIndex > shiftObject.maxIndex) {
        reject("New Index is Invalid: " + newIndex);
      } else {
        resolve(newIndex);
      }
    }
  );
};

First, the updated changeGearAsync function takes in the data we pass it and returns a new Promise object. We pass in a single argument: a callback function that itself has two functions passed to it, resolve and reject

When implementing a promise you perform whatever calculations, requests, and so on that you want in the callback function. Once done, if all’s right with the world, you invoke resolve with the data you want to pass back. If you encounter problems you signal that to the function caller by invoking reject along with any relevant errors as the argument. 

// invoke async function that returns a promise
this.changeGearAsync(shiftObject)
  .then(
    (newIndex) => {
      this[shiftIndexName] = newIndex;
      console.log(this.calculateGearRatio());
    }
  )
  .catch(
    (err) => {console.log("Error: " + err);}
  );

Now we have something a lot easier to reason about. If changeGearAsync works, the then function is invoked with the function passed into its argument. If it does not, catch is invoked. 

If the callback function itself returns an instance of Promise, that’s when things get exciting. You can simply chain those two promise functions together.

Bike.prototype.changeBothGears = function(frontChange, rearChange) {
  let shiftFront = {
    currentIndex: this.frontGearIndex,
    maxIndex: this.transmission.frontGearTeeth.length - 1,
    changeBy: frontChange
  };
  let shiftRear = {
    currentIndex: this.rearGearIndex,
    maxIndex: this.transmission.rearGearTeeth.length - 1,
    changeBy: rearChange
  };
  this.changeGearAsync(shiftFront)
    .then(
      (newIndex) => {
        this.frontGearIndex = newIndex;
        console.log(this.calculateGearRatio());
        return this.changeGearAsync(shiftRear);
      }
    )
    .then(
      (newIndex) => {
        this.rearGearIndex = newIndex;
        console.log(this.calculateGearRatio());
      }
    )
    .catch(
      (err) => {console.log("Error: " + err);}
    );
  };

Async/Await

The async and await operators. These build on promises, allowing them to be used in a way that much more closely resembles synchronous JavaScript.

The ES2016+ release introduced async functions and a different way of calling native promises. The structure of the promise remains the same, but what changes is how the promise is called. The call is wrapped in a function that uses the async and await keywords. What is returned is a promise object that contains either the resolved or rejected value.

function doSomething(msg){ 
  return new Promise((resolve, reject) => {
      setTimeout(
        () => {
          try {
            throw new Error('bad error');
            console.log(msg);
            resolve();
          } catch(e) {
            reject(e);
          }
        }, 
        1000);
    }) 
}

async function doSomethingManyTimes() {
  try {
    await doSomething("1st Call");
    await doSomething("2nd Call");
    await doSomething("3rd Call");
  } catch (e) {
    console.error(e.message);
  }
}
      
doSomethingManyTimes();  

Using .then method it would be this way

doSomething("1st Call")
  .then(() => doSomething("2nd Call"))
  .then(() => doSomething("3rd Call"))
  .catch(err => console.error(err.message));  

Server Side JavaScript

Implementation

As Wikipedia states: “Node.js is a packaged compilation of Google’s V8 JavaScript engine, the libuv platform abstraction layer, and a core library, which is itself primarily written in JavaScript.” Beyond that, it’s worth noting that Ryan Dahl, the creator of Node.js, was aiming to create real-time websites with push capability, “inspired by applications like Gmail”. In Node.js, he gave developers a tool for working in the non-blocking, event-driven I/O paradigm.After over 20 years of stateless-web based on the stateless request-response paradigm, we finally have web applications with real-time, two-way connections.

What it really means is that Node.js is not a silver-bullet new platform that will dominate the web development world. Instead, it’s a platform that fills a particular need. And understanding this is absolutely essential. You definitely don’t want to use Node.js for CPU-intensive operations; in fact, using it for heavy computation will annul nearly all of its advantages. Where Node really shines is in building fast, scalable network applications, as it’s capable of handling a huge number of simultaneous connections with high throughput, which equates to high scalability.

How it works under-the-hood is pretty interesting. Compared to traditional web-serving techniques where each connection (request) spawns a new thread, taking up system RAM and eventually maxing-out at the amount of RAM available, Node.js operates on a single-thread, using non-blocking I/O calls, allowing it to support tens of thousands of concurrent connections held in the event loop.

Package Management

To handle the packages, you will need a package manager. Most commonly used package managers include npm, yarn and pnpm.

When discussing Node.js, one thing that definitely should not be omitted is built-in support for package management using NPM, a tool that comes by default with every Node.js installation

npm

npm is the default package manager and is most widely used. It is perfect for beginners and has terrific support. The previous versions had two main issues:

  • Initially, npm didn’t have a lock file. The exact versions of each dependency weren’t precisely stated. The code would be run with the latest versions which caused compatibility problems.
  • Npm wasn’t deterministic — different versions of packages were allowed on different machines.

Yarn fixed these problems and attracted a massive audience. Later, npm updates fixed the issues and made it as usable as Yarn.

Yarn

Yarn was built by Facebook with the goal to fix scalability issues npm had at the time. Three core principles of yarn are speed, reliability, and security. Yarn can install packages faster by using local caching — it is much faster to grab a package from cache memory than to wait for the download. Local caching also enables offline mode — packages in cache can be directly installed. Yarn was first to feature a file with locked package versions so the code wouldn’t be forcibly run on the latest version. Security is ensured with the use of checksums before execution.

There are some unique features of yarn which make it faster than npm. Installation of packages is parallel, while npm installs sequentially. Yarn guarantees that dependencies will be installed the same way on a different machine, regardless of the install order. 

pnpm

If we have ten projects which use the same ten packages, each project will have its own copy in node_modules. This practice isn’t very efficient in terms of disk space. pnpm solves this problem by installing only one copy of the package on disk. The node_modules directory will only contain a hard link to the location of the package on the disk. Developers of the package manager claim it is as fast as yarn and npm — in some cases even faster. pnpm is strict in the sense that it only accesses the dependencies specified in package.json. It is great for multi-package repositories (monoreps).

Rush

Rush technically isn’t a package manager, but it allows you to pick out whether you want to use npm, yarn or pnpm. It enforces that the same version of the package manager is used by all team members.

Core Modules

Some of the most useful npm modules today are:

  • express – Express.js—or simply Express—a Sinatra-inspired web development framework for Node.js, and the de-facto standard for the majority of Node.js applications out there today.
  • hapi – a very modular and simple to use configuration-centric framework for building web and services applications
  • connect – Connect is an extensible HTTP server framework for Node.js, providing a collection of high performance “plugins” known as middleware; serves as a base foundation for Express.
  • socket.io and sockjs – Server-side component of the two most common websockets components out there today.
  • pug (formerly Jade) – One of the popular templating engines, inspired by HAML, a default in Express.js.
  • mongodb and mongojs – MongoDB wrappers to provide the API for MongoDB object databases in Node.js.
  • redis – Redis client library.
  • lodash (underscore, lazy.js) – The JavaScript utility belt. Underscore initiated the game, but got overthrown by one of its two counterparts, mainly due to better performance and modular implementation.
  • forever – Probably the most common utility for ensuring that a given node script runs continuously. Keeps your Node.js process up in production in the face of any unexpected failures.
  • bluebird – A full featured Promises/A+ implementation with exceptionally good performance
  • moment – A JavaScript date library for parsing, validating, manipulating, and formatting dates.

CLI Commands

OptionDescription
v, –versionIt is used to print node’s version.
-h, –helpIt is used to print node command line options.
-e, –eval “script”It evaluates the following argument as JavaScript. The modules which are predefined in the REPL can also be used in script.
-p, –print “script”It is identical to -e but prints the result.
-c, –checkSyntax check the script without executing.
-i, –interactiveIt opens the REPL even if stdin does not appear to be a terminal.
-r, –require moduleIt is used to preload the specified module at startup. It follows require()’s module resolution rules. Module may be either a path to a file, or a node module name.
–no-deprecationSilence deprecation warnings.
–trace-deprecationIt is used to print stack traces for deprecations.
–throw-deprecationIt throws errors for deprecations.
–no-warningsIt silence all process warnings (including deprecations).
–trace-warningsIt prints stack traces for process warnings (including deprecations).
–trace-sync-ioIt prints a stack trace whenever synchronous i/o is detected after the first turn of the event loop.
–zero-fill-buffersAutomatically zero-fills all newly allocated buffer and slowbuffer instances.
–track-heap-objectsIt tracks heap object allocations for heap snapshots.
–prof-processIt processes V8 profiler output generated using the v8 option –prof.
–V8-optionsIt prints V8 command line options.
–tls-cipher-list=listIt specifies an alternative default tls cipher list. (requires node.js to be built with crypto support. (default))
–enable-fipsIt enables fips-compliant crypto at startup. (requires node.js to be built with ./configure –openssl-fips)
–force-fipsIt forces fips-compliant crypto on startup. (cannot be disabled from script code.) (same requirements as –enable-fips)
–icu-data-dir=fileIt specifies ICU data load path. (Overrides node_icu_data)

Testing

There is certainly no shortage of tools to choose from when testing your JavaScript, and the list seems to grow longer each day. One of the more popular behavior-driven tools is Jasmine.

Diagram of a test JavaScript file that contains one Describe function in a box and inside of that box is two more it function boxes that each contain Assertion boxes.

In Jasmine you use a describe function to create a test suite, but you’re really just creating a JavaScript function. Inside of that function, use the it function to specify one or more specs (or tests), which are also just functions. Inside of the spec function, put assertions to check whether the test worked.

This is what a basic test suite looks like with both a positive and a negative test:

describe("A test suite is just a function", function() {
    it("This is a positive test", function() {
      expect(true).toBe(true);
    });
    it("This is a negative test", function() {
      expect(false).not.toBe(true);
    });
});  

Jasmine includes global functions (beforeEach and afterEach) that can be used to execute code before and after each of your test specs have run. This is useful for when you have set up and tear down code that requires a lot of resources, like logging in and out as a certain user. There are also beforeAll and afterAll functions, but these are run only once for each describe block they are part of. 

Example, let’s test these classes

class Parent {
  constructor(name) {
    this.name = name;
  }
       
  getName() {
    return this.name;
  }
}

  
class Child extends Parent {
  constructor(name) {
    super(name);
  }
        
  getMessage() {
    return 'Hello ' + super.getName();
  }
}   

You can add the JavaScript code for the Jasmine test suite to a separate file. It might look something like this:

describe("Test Parent Child Classes", function() {
  it("Get Message Test", function(){
      let someone = new Child('person');
      expect(someone.getMessage()).toEqual("Hello person");
  });
     
});  

Leave a Reply

avatar
  Subscribe  
Notify of