Study Notes: Salesforce JavaScript Developer 1 Certification

Salesforce JavaScript Certification Study Notes


JavaScript (JS):
  • Scripting/Programming language that allows one to implement features on web-pages.
  • One of the 3 core technologies of web development -  HTML, CSS and JavaScript.
  • case-sensitive and loosely typed.
**Commenting:
//Single line
/*Multi line*/

Exam Topics:
  1. Variables, Types, and Collections: 23%
  2. Objects, Functions, and Classes: 25%
  3. Browser and Events: 17%
  4. Debugging and Error Handling: 7%
  5. Asynchronous Programming: 13%
  6. Server Side JavaScript: 8%
  7. Testing: 7%

Session#1: Variables, Types and Collections


Variables:

  • Containers for storing values.
    e.g.: var variable_name; or var  variable_name='value';
  • Naming: [$_a-zA-Z]+[$_a-zA-Z]0-9]* (should start with $, _, capital/small alphabets),
    e.g.: 
           valid --> $name, _name, $, $9, _, _9, full_name, fullName, FullName, a9 
           Invalid--> 123, 123a, full-name, reserved keywords (if, else, switch, case, function) 

Types: 

JS has 8 data-types.
  1. number: All Integers and floating-points numbers, limit +/-(2^53-1). Special values- NaN, +/-Infinity, +/-0.
  2. string: Anything inside a single quote('x'), double quote("x") or back-tick (`x`).
  3. boolean: true or false. Following 7 values are considered falsies(evaluates to false) --> 0, 0n, empty string(''), undefined, null, NaN, false.
  4. bigint: Special to hold Integers of arbitrary length.  --> let x= 123n or BigInt(123) or BigInt('123') 
  5. undefined: unassigned value, not same as empty or null.
  6. null: represents an empty value. typeof(null) == 'object
  7. symbol: unique symbols, usually used for private object keys  let x= Symbol() or Symbol('xyz')
  8. object: Composite data-type, container for properties and methods in a key- value pair.
**Objects are the only Composite type in JS, rest are Primitive

Collections:

JS has 2 types of collections: Keyed & Indexed

Type Coercion:

  • process of converting value from one type to another.
  • There are 2 types of coercion:
    • Explicit:
      • using built-in functions: String(), Boolean(), Number()
      • Sample:
        //strings
        String(anything)=> "anything"
        string([1,2,3]) => "1,2,3"
        string({}) => "[object object]" //also a string just values not accessible properly

        //booleans
        Boolean(0/0n/''/null/NaN/undefined/false) => false // rest are evaluated to true

        //numbers
        Number('23') => 23
        Number(null) => 0
        Number(false) => 0
        Number(true) => 1
        Number([]) => 0
        Number([12]) => 12
        Number([12, 13]) =>NaN
        Number(undefined) => NaN
        Number(anything else)=> NaN //Number('xyz') or Number({}) etc.

    • Implicit:
      • done by JS compiler automatically, like during some operation.
      • string + any other types = if one operand is string converts other to string and does concatenation(Only for +, for other operations [*, -] conversion happens to number)
      • Examples: //keep in mind left to right order of execution
        * 100+ 'World' + null => '100Worldnull'
        * 100+null+'world' => '100world' //(100+Number(null))+'world' = (100+ 0) + 'world' = 100 + 'world' = '100' + 'world'
        * 100 + true + null +20 => 121
        * 100 * 'world' + null +20 => NaN // (100 * Number(world)) + null + 20= (100 * NaN) + null + 20 = NaN + anything = NaN
        **Any Mathematical operation with NaN always results in NaN

Additional Topics:

  • Equality Operators:
    1. Abstract(==) => Matches only values. So, '10' == 10 is true
    2. Strict(===) => Matches value and type. So, '10' === 10 is false
    3. Object.is() : strict + 2 edge cases
      • Object.is(0, -0) => false //strict mode evaluated to true
      • Object.is(NaN, NaN) => true //strict mode evaluated to false


  • var vs let vs const:
    1. var: function scope, hoisting, allows re-defining and updating values.
    2. let: block scope and no hoisting. No re-defining but Allows updating values.
    3. const: same as let but doesn't allow updating values either


  • String Interpolation:
    • Embedding expressions in string, uses backticks (``) with $ and curly brackets.` value of variable is: ${varName}.`
    • Sample:
        let name='PK';
        console.log(`Hello ${name}`); //output: 'Hello PK'


  • String Methods: let str= 'Hello-world';
    1. str.substr(start, length)
    2. str.substring(start, end)     //end is not includes but doesn't allow negative index, Always considers lower value as start and high value as end. Hence str.substring(x,y) = str.substring(y,x); 
    3. str.slice(startIndex, endIndex)     //allows for using -ve values and end is not included
    4. str.includes('xyz')     //returns true or false, based on whether or not value found in the string.
    5. str.indexOf('o') => 4      // returns the first index location between 0 to N or -1 if x is not found
    6. str.lastIndexOf('o') => 7     // returns the index location of last occurrence of x or -1 if x is not found
    7. str.split('-') => ['Hello', 'world']     // returns an array by splitting string on separator string param
    8. str.toUpperCase() or str.toLowerCase()     // returns a new  string with upper or lower case
    9. str.replace('world', 'World') => 'Hello-World'     // returns replaced string, doesn't change original.

Session#2: Arrays, Objects, Functions, and Classes


Array:

  • Index based storage for multiple values (of different types).
  • can be defined 2 ways: e.g.:
    Array Literal:
        let arrName=['XYZ', 30, ['Name', 'age']]
    or 
    Array Constructor:
       let arrName= new Array();arrName[0]='XYZ';
       let arrName=new Array(length);
       let arrName=new array('XYZ', 30);

Array Methods:

let newArr1=new Array(); let newArr2=new Array(1,2,3);
Basic:
  1. newArr1.push('x'); :-Adds element to end of array and Returns the new length.  
  2. newArr1.pop(); :-Removes last element and Returns the removed element.  
  3. newArr1.shift(); :-Removes first element and Returns the removed element.  
  4. newArr1.unshift('1'); :-Adds element to the front of array and Returns the removed element.  
  5. newArr1.reverse(); :-Turns arrays elements from last to first.
  6. newArr1.splice(indx, N, elm1, ..., elmN); :-Starts operation at 'indx': Delete 'N' items and Add 'elm1' through 'elmN'.
  7. newArr1.indexOf('elemX'); :-Returns index of first occurrence of 'elemX' or -1 if not found.
  8. newArr1.lastIndexOf('elemX'); :-Returns index of last occurrence of 'elemX' or -1 if not found.
  9. newArr1.join('separator'); :-Returns  string created from array elements, separated by 'separator'.
  10. newArr1.slice(startIndx, endIndx); :-Returns new array containing elements from 'startIndx' till 'endIndx'(item @endIndx is not included).
  11. newArr1.concat(newArr2); :-Returns new array with combined elements from both arrays.
Advanced: These take a function as parameter.
  1. newArr1.map( (currItem, index, actualArray)=>{ return //process each item; }); :-Returns Array of same size with each item transformed.
  2. newArr1.filter( (currItem, index, actualArray)=>{ return //truthy statement on item; }); :-Returns Array of N items passing the truthy check.
  3. newArr1.every( (currItem, index, actualArray)=>{ return //truthy statement on item; }); :-Returns true if all items pass the truthy check.
  4. newArr1.some( (currItem, index, actualArray)=>{ return //truthy statement on item; }); :-Returns true if at least one item passes the truthy check.
  5. newArr1.sort( (a, b)=>{ return (a-b);}); :-Returns new array sorted in ascending order (return (b-a) for descending).
  6. newArr1.reduce( (total, currItem, index, actualArray)=>{ return total + currItem; }, initValOfTotal); :-Returns a single value of total after processing all items.
  7. newArr1.reduceRight( (total, currItem, index, actualArray)=>{ return total + currItem; }, initValOfTotal); :-Returns single value of total after processing all items, starting from right/end of array.

Looping in JS:

  1. for loop:
    for(let i=0; i<arr1.lehgth; i++){
      console.log('array item is:', arr1[i]);
    }
  2. forEach:
    arr1.forEach( (currItem, index, actualArray) => {
      console.log(currItem.name);
    });
  3. for...of:
    for(let item of arr1){
      console.log(item);
    }
  4. for...in:
    for(let key in obj1){
      console.log(`${key} is ${obj1[key]}`);
    }
  5. while:
    var counter=0;
    while (counter<10){
      console.log(counter);
      counter++;
    }
    **counter initialization, condition and increment are separate statements.
  6. do...while:
    var number=10;
    do{
      number = prompt('Enter a random number: ');
      console.log('You entered ', number)
    } while (number<10);
    **this construct is used where you want the code in loop(do) to run at least once, irrespective of loop condition.

Objects:

  • These are used to define complex datatypes in JS, with properties(key-value pair) & methods(functions inside object)
  • There are 3 primary ways of creating objects, each ahs its own use case:
    1. Using Object Literal: most commonly used and preferred way
      let x= {} or let x = { key1: 'some value', key2: 10 }
    2. Using Object constructor: Used for inheritance
      let x= new Object();
    3. Using Object's create method: Used to set prototype
      let x= {name: 'pk'}; let y= Object.create(x);
  • Accessing Object Properties: This can be done 2 ways:
    1. Dot Notation: x.name
    2. Array Notation= x['name'] //this becomes necessary if key has spaces or a it's stored in a variable (dynamic)
  • Object Methods:
    1. let x=Object.keys(objName); //Array of keys //Only own enumerable properties, not from __proto__
    2. let x=Object.values(objName);  //Array of values
    3. let x= Object.entries(objName); //Array of Object(2 value array) // [[key1, value1], [key2, value2]]
    4. Object.freeze(objName); //can't update object properties
    5. Object.seal(objName); //can't add, update or delete Object properties
    6. Object.is(obj1, obj2); // write way of comparing 2 objects
    7. let x=Object.assign(target, sourceObj1, sourceObj2); // all source objects will be merged into target and same value will also be assigned to x;
    8. let newObj=Object.create(existingObj);
  • String & Object conversion Methods:
    • let string1= JSON.strigify(obj1); //object to string //Another Syntax for formatted string/pretty: JSON.strigify(obj, null, separator)
    • let obj1= JSON.parse(string1); //string to object

Advanced Topics:

  • Hoisting: 
    • let and const are not hoisted
    • var is hoisted. Only declaration not the initialization
    •  function definition is also hoisted, but not the expression syntax.
  • Strict Mode:
    • Explicitly done using the following keyword at top of your script- 'use strict'
    • Helps in eliminating some silent errors:
      1. Using a variable or object without declaration => Error 
      2. Deleting a variable or object is not allowed.
      3. Duplicating parameter name on functions is not allowed.
      4. Value of 'this' is undefined in function
  •  'this' keyword in JS:
    • In a method (function inside an object), it refers to owner of object
    • Standalone code, it refers to global object (window in chrome)
    • In a function, it refers to global object (window)
    • In a function, in strict mode, this = undefined.
    • In an event, it refers to the element that is currently handling the event.
    • We can use salesforce methods like - call(), apply() and bind() on any function/method to change the 'this' binding.
    • sample code:
      let person = { firstName: 'P',
          lastName: 'K', id:1,
          getName: function(){return this.firstName + ' ' + this.lastName;}
          };
      let person1= { firstName: 'Pr',
          lastName: 'Ku',
          getName: function(param1, param2){return this.firstName + ' ' + this.lastName + ' ' + param1 + ' ' + param2;}
          }
      let person2= {firstName:'Ethan', lastName:'Hunt'}
      person.getName('A', 'B') => 'P K'
      person.getName.call(person1);
      //call and apply work same except how we pass the arguments
      person1.getName.call(person2, 'Mission', 'Impossible') => 'Ethan Hunt Mission Impossible' //all params passed as comma separated
      person1.getName.apply(person2, ['Mission', 'Impossible']) => 'Ethan Hunt Mission Impossible' //all params passed wrapped in an array
  • Object Destructuring:
    • Object values can be destructured if local variable name is same as key
      let employee= {firstName: 'pr', lastName:'ku', DOJ: 2012};
      let { firstName, lastName, position='Dev'} = employee; // instead of let firstName=emplo.firstName; let lastName= employee.lastName
  • Array Destructuring:
    • Array values can be assigned in index sequence, local variable name can be anything
      let arr= ['a', 'b', 'c'];
      let [first, second, fourth='d'] = arr;
      let [first, , third]= arr; //can skip values which are not required using N commas
  • Date Object:
    • This object is used to work with Date & Time
    • JS uses Browser's time zone and displays as a full text string
    • 4 ways to create date:
      1. new Date()
      2. new Date(year, month, day, hours, minutes, seconds, milliseconds)
      3. new Date(milliseconds) //takes an integer value since 1st Jan 1970 UTC
      4. new Date(date string) //'wed Jul 15 2020 14:30:59'
    • Sample: let x= new Date();
      x.getFullYear() => 2020
      x.getMonth() => 06
      x.getDate() => (1-31)
      x.getDay() => (0-6)
      x.getHours() => (0-23)
      x.getMinutes() => (0-59)
      x.getSeconds() => (0-59)
      x.Milliseconds() => (0-999)
      x.getTime() => Milliseconds since 1st Jan 1970
      Date.now() => getTime in ES5
  • Spread and Rest Operator:
    Spread:
    • Allows to spread values of array/Iterable in single arguments
    •  Create new copy of array/object
    • Concatenation of value / property
    • Pass value of array to function as separate params
    • Convert string to array of characters
    Rest:
    • Same syntax as Spread.
    • Used to Collect remaining elements into an array
    • Rest can gather any number any number of arguments into an array.
    • Rest params are always last in function.
    • Sample:
      function add(a, ...args){
        console.log(a);
        let sum=a;
        for(let i of args){sum+=i;}
        return sum;
      }
      add(1, 2, 3)=> a=1, args=[2,3] => sum=6

Functions in JS:

  • Functions are block of re-usable code 
  • Various syntaxes for defining a functions, each has it's own use cases.
    1. Function Declaration: function x(a, b){ return a*b; }
    2. Function Expression: let x= function(a,b){ return a*b; }
    3. Function Constructor: let x = new Function('a', 'b', 'return a*b');
    4. Immediately Invoked Function Expressions (IIFE): (function(a,b){return a*b;})(2,3) //declaration and call happening in same line
    5. Arrow functions: 
      • Introduced in ES6, new light weight syntax for function declaration
      • Removes excess keywords like function, return etc. and using a fat arrow (=>)
      • Sample:
        1. No Params: 
          old:
          let abc= function(){ return 42;} || arrow: let abc= ()=> 42;
        2. Single Param:
          old:
          let abc= function(x){return x*2;} || arrow: let abc = x => x*2; //parenthesis around param is optional
        3. Multi Params:
          old:
          let abc= function(x,y){return x+y;} || arrow: let abc= (x,y) => x+y;
        4. Block Body:
          old:
          let abc= function(x,y){ let z=10; return x+y+z;} || arrow: let abc = (x,y) => { let z=10; return x+y+z;} //using explicit return is required if functions with multi line of code is returning a value
        5. Object Literal:
          old:
          let abc = function(){return {name:'PK'};} || arrow: let abc= ()=> ({name: 'PK'}) // if returning an object using {} syntax, need to wrap it in (parenthesis)
      **We never convert the object methods to array function notation, Since arrow function take 'this' from parent function and not the object. 

Closures:

  • Any Inner function has access to:
    1. Its own variable
    2. Outer function's variables & parameters
    3. Global variables
  • Sample:
    function outer(){
      let counter=0;
      return function inner(){
        counter++;
        return counter;
      }
    }
    let c= outer(); // c will be a function and outer scope is over and counter should be destroyed
    c(); => 1// but the inner function kept a closure reference to 'counter' and hence counter variable was still alive.

Object Prototype and prototype chain:

  • Objects inheritance uses - 'Prototype' and '__proto__', for simple understanding they can be thought as same.
  • 'prototype' is a property of function constructor
  • '__proto__' is property of an instance created from object constructor. It acts as the historical getter & setter for prototype.
    • Now newer methods are available like - getPrototypeOf() & setPrototypeOf()
  • Both 'prototype' & '__proto__' can only be set to an object or null.
  • Object Functions:
    • Special function that act as a blueprint for creating objects
    • Sample:
      //Capitalize to show its a special function, an object blueprint (not mandatory but a best & wildly used practice)
      let Person= function(firstName, lastName){
        this.firstName=firstName;
        this.lastName=lastName;
        this.getFullName= function(){return this.firstName + ' ' + this.lastName;}
      }
      //use of new keyword is best practice
      let user1= new Person('p', 'k');
      user1.getFullName(); // output= 'p k'
      let user2=new Person('Pr', 'Ku');
      user2.getFullName(); //output= 'Pr Ku'
    • But the above code has disadvantage, that the 'getFullName' method is repeated every time, inside each user object created
    • A better solution comes in form of 'prototype'
      //take out the getFullName from Person function and instead add it to its prototype
      Person.prototype.getFullName = function(){return this.firstName + ' ' + this.lastName;}

      let newUser1= new Person('P', 'K');
      newUser1.getFullName(); //stil works same, but this time its being accessed from Person prototype chain and not copied on indivial objects
  • Object Inheritance:
    • One object gets access to properties and methods of another object
    • Every object has a prototype (__proto__) property, which makes inheritance possible, hence object inheritance is also known as prototype inheritance.
    • So, any property or method that you want inherited, will have to be added to 'prototype' property.
    • When a method or property is called on an object instance, the search start in current object and if not found moves to objects prototype which has another reference to parent object's prototype, this process is known as 'prototype chain'
    • Usually you would do something like: Dog.prototype = Object.create(Animal.prototype) //set child prototype to refer to parent

Class and Class Inheritance:

  • Class is a type of function in JS, just a syntactical sugar.
  • Instead of 'function', we use 'class' keyword and property assignments happen inside 'constructor()' method.
  • Syntax:
    class Animal{
      //mandatory constructor, with properties defined inside using this keyword
      constructor(name, age){
        this.name=name;
        this.age=age;
      }
      //additional object methods
      eats(){
        console.log(this.name'+ ' is eating';);
      }
    }
  • Usage:
    let animal1 = new Animal('Cat', 2);
  • Inheritance Syntax:
    class Dog extends Animal{
      constructor(name, age, breed){
        super(name, age); //we use super keyword to call parent class construct
        this.breed= breed;
      }
      //additional methods
      logBreed(){
        console.log(this.name + ' is of ' + this.breed + ' breed.');
      }
    }
  • Inheritance Usage:
    let dog1= new Dog('Pup', 1, 'Retriever');
    dog1.logBreed(); // output: 'Pup is of Retriever breed.'
    dog1.eats(); // output: 'Pup is eating';// can access Animal prototype

JavaScript Decorators:

  • Decorators are a way of wrapping a piece of code within another code for extending some generic functionality.
  • Previously it was also known as 'function composition' or 'higher order functions'
  • These functions usually take 3 arguments: - 'target', 'name' and 'descriptor'
  • Sample code:
    class greet{   @readOnly // we are using a decorator
      greeting(){return 'hello';}
    }
      //defining the decorator
      function readOnly(target, name, descriptor){
        descriptor.writable = false; //changing the property to disable write
        return descriptor;
      }
  • Usage:
    let greet =new Greet();
    console.log(greet.greeting()); //output: hello

    //try to overwrite grret method
    greet.greeting= ()=>'hurray';
    console.log( greet.greeting()); //still outputs 'hello'

    ** The 'descriptor' has more such properties - configurable, enumerable, value (which is the entire function code)

Modules:

  • Modules are used for packaging reusable code bundles in JS
  • These are defined with help of 'export' keyword and later can be 'imported' to be used elsewhere
  • We will Discuss about the 2 import phases/parts in using modules:
    1. Export:
      • Named Export: 
        • Used when we have multiple exports per file
        • Can be done 2 ways- export individually or export together
      • Default Export: 
        • Usually used when we have just one export per file (in real-world, you will usually see 1 default and multiple names exports per module file)
        • We use 'export default' keyword to a variable/function/object
        • These are useful in utilities or constant files.
    2. Import:
      • Named Multiple: import {x, y, z} from './utilModuleFile.js'
      • With Alias: import * as UTIL from './utilModuleFile.js'
      • Default Import: import default Property from './utilModuleFile.js' //no curly braces

Session#3: Browser & Events (DOM, BOM , Events & APIs)


Document Object Model (DOM):

  • Whenever a web page is loaded, the browser creates DOM as a tree of objects/nodes.
  • The DOM comes with a list of APIs to add, remove or modify part of the document.
  • In DOM tree, 'document' is the root node with one child, the <html> element.

DOM Manipulation: (document APIs, <methods>)

  1. Selecting Elements: (returns htmlElement)
    1. document.querySelector('cssSelector')
    2. document.querySelectorAll('cssSelector')
    3. document.getElementById('Id')
    4. document.getElementByClassName('className')
    **css selectors= '#Id', '.className', 'tagName'
  2. Traversing Elements: <methods>
    1. htmlElement.children()
    2. htmlElement.firstElemntChild()
  3. Manipulating Elements: <properties>
    1. htmlElement.innerHTML
    2. htmlElement.textContent
    3. htmlElement.innerText
  4. Manipulating Attributes:
    1. htmlElement.setAttribute('attName', 'value')
    2. htmlElement.getAttribute('attrName')
  5. Manipulating Element's Style: <properties>
    1. style: 'htmlElement.style.visibility=hidden' or 'htmlElement.style.display=none' (hidden + spaces removed from web page doc)
    2. classList: htmlElement.classList.add('xyz') or htmlElement.classList.remove('xyz')

Browser Object Model (BOM):

  • Provides APIs used to interact with browser, default object of a browser is 'window'.
  • Grouping of APIs: window -> document, history, screen, navigator, location
    1. window APIs:
      1. alert()
      2. confirm()
      3. prompt()
      4. setTimeout()
      5. clearTimeout()
      6. setInternval()
      7. clearInterval()
      8. localStorage()
      9. sessionStorage()
    2. location API: Deals with the URL: https://www.google.com/search?xyz='abc'
      1. location.href >> Returns complete url of the current web page (="URL").
      2. location.hostname >> Returns the domain of current web page (="/www.google.com").
      3. location.pathname >> Returns path & file name of current web page (="/search").
      4. location.protocol >> Returns protocol used by current web page (="https").
    3. history API: Contains info on history of URLs visited by user in a browser window.
      1. history.back() >> To move backward
      2. history.forward() >> To move forward
      3. history.go(+/-n) >> 0=Reload current page, -1=backward, 1=forward
      4. history.length >> Number of URLs in history stack
      **browser triggers a 'popstate' eventon every action, using 'pushState' & 'removeState'
    4. navigator API: Info about visitor's browser
      1. navigator.language
      2. navigator.online
      3. navigator.geolocation.getCurrentPosition( pos => console.log(pos) )
    5. screen API: Get/set attributes of screen on which current window is being rendered.
      1. screen.height
      2. screen.width

JavaScript Events:

  • Events are actions that occurs in web-browsers, which is fed-back for developers to respond.
  • Event handler: Block of code that's executed when a particular event happens.
  • Events can be attached to - Element, Set of Elements, Document or Window.
There are 3 ways of handling events in JS:
  1. HTML Attribute: Set on the attribute of HTML tag, usually named as onevent, like 'onclick'.
      eg: <button onclick="showAlert();">Click Me</button>
  2. JS Property: Set the handler function as a property on element in .js file.
      eg: let btn= document.querySelector('button');
      btn.onclick = function(){alert('Handler from Property'); };
  3. Event Listeners: This is best approach as provides way of registering and de-registering handlers, Using methods like addEventListener() and removeEventListener()
      eg: let btn= document.querySelector('button');
      let showAlert= function(){alert('Handler for Event Listener');}
      btn.addEventListener('click', showAlert, false);
***Event listener takes 3 params- (string eventname, function handler(e){}/var handlerFunctionName, Boolean useCapture )
Page-load events: These are added to window
  1. Load events:
    1. DOMContentLoaded: When browser fully loaded HTML and completed building DOM tree.
    2. load: Fully loaded html and external resources like images, stylesheet and JS.
  2. Unload Events:
    1. beforeunload: Fired before page & resources are unloaded.
    2. unload: Fired when page has completed unloading.
Event Flows: 
  • Event flow explains the order in which events are received (handlers for particular event executed) on the page from element where it occurs and propagates through the DOM tree.
  • Both happen by default and propagate 
  • There are 2 main models:
    1. Event Bubbling: Starts at most specific element and flows upward towards least specific (till document or even window)
    2. Event Capturing: Starts at least specific and flows downward towards most specific.
    *Only difference between definition is using the third parameter of addEventListener method.
    **These flows behave differently for the Target element (Target Phase), where handlers are executed in order in which they are defined, ignoring the sequencing for bubble or capture.
    ***Target Phase: Runs in order the handlers are added.

    **So, the usual sequence is:
    1. All Capture (till stopPropagation)
    2. All target in order of added. (all target handlers are executed, stopPropagation is not applicable)
    3. All bubble (till stopPropagation)
    JavaScript Event Bubbling and Capture Diagram




Custom Events:
  • In JS we can create and use our own custom events.
  • IT involves 3 steps:
    1. Create a 'CustomeEvent' with/without data.
    2. Attach the event to any element.
    3. Dispatch the event.
  • Sample Code: eg:-
        //define custom event
        let helloEvent= new CustomEvent('showmsg',{bubble: true, cancelable: true, detail: 'Hello World'});
        //dispatch on some action
        let elem=document.querySelector('some html');
        elem.dispatchEvent(helloEvent);
        //listener for the event
        elem.addEvenetListener('showmsg',  function(e){console.log(e.detail);} );
Event Object:
  • Whenever an event occurs, the web browser passes an Event object to the handler method, which is only accessible from handler and immediately destroyed afterwards.
  • **onerror is a special case where the handler receives : (event, source, lineno, colno, error) as parameter
  • This object has following properties and methods:
    1. currentTarget : The element currently handling the event.
    2. target : Always refers to the original element that fired the event.
    3. type : type of event that was fired
    4. detail : Actual data, if any passed with the event.
    5. preventDefault() : Cancel the default behavior of the event, (like navigation on button click)
    6. stopPropagation() : Cancel any further event capture/bubble.

Session#4: Debugging & Error Handling


Errors:

  • These are the mistakes or faults in program that cause our program to behave unexpectedly.
  • There are various types of error, broadly categorized as:
    1. Syntax: Most common, also known as 'parsing errors', these occur at compilation time for traditional languages and at interpretation time for JS. eg: let x=2; let y=3; console.log(x+y; //missing closing parenthesis for log method
    2. Runtime: These are also known as 'exceptions', occurs during the actual code execution. eg: window.callSomeRandomMethod(); //method not found
    3. Logical: Not syntax or runtime, instead these occur when programmer makes a mistake in the logic that drives your script and hence don't get the expected result.
Error Handling:
  • like many programming languages JS provides a 'try...catch...finally' construct for error handling.
  • Only try...catch or try...finally can also be used as per your requirement.
  • sample: 
    try{
    //code
    }
    catch(error){
    //error handling
    }
    finally{
    //optional clean up code
    }
    **finally is optional, but if provided always executes and if try or catch have return statement, then finally is executed just before that return statement.
    ***The try/catch works synchronously so it won't catch something if code has some asynchronous method. So, always put it's own try/catch in the asynchronous methods.
Error Object:
  • JS generates an object containing details of the exception/error, which is passed to catch as argument.
  • It has following properties:
    1. name: Name of error, like- Error, SyntaxError etc.
    2. message: A Text message about the error.
    3. stack: Current call stack with information about the sequence of nested call that led to the error.
throw: 
  • Statement used to generate user defined exception, during runtime when a throw is encountered, execution is stopped and control passed to first catch clause. If no catch found then program is terminated.
  • Syntax:
    throw 'message'
    or
    throw new Error('Some Custom Error Message'); //error object with name=Error and message

Console Methods:
  • Console is web browser tool that helps log information about the web page, like error, warning network requests etc. 
  • JS also has a 'console' object which provides you access to the browser's debugging console.
  • 'console' object provides several methods for interacting with browser console.
    1. console.log('message', args)
    2. console.warn('message', args)
    3. console.error('message', args)
    4. console.info('message', args) **only above 4 support string substitution(more on it later)
    5. console.table(tableData, tableColumns) **prints tabular format, tableData can be an object, array or array of objects. The optional tableColumn param can only be used with array of objects to display selected values from object.
    6. console.time('label') and console.timeEnd('label') **measuring time taken by code between a pair of these method
    7. console.group('some string') and console.groupEnd('some string');  **groups all console logging between these pair, making the logged info more readable and relatable.

debugger:
  • keyword/statement invokes the available debugging functionality, such as setting breakpoints. If no debugging functionality is available this statement has no effect.

Session#5:  Asynchronous Programming


Synchronous JavaScript: 

  • JS is a single threaded programming language, hence only 1 thing can happen at a time.
  • JS executes the script from top and works its way down creating 'execution context' and pushing and popping functions in and off the 'call stack'

Asynchronous JavaScript: 

  • With Synchronous approach, we can't perform long running operations like network access without blocking main thread, hence asynchronous approach cam into play.
  • In async approach, if JS has to wait for an operation to complete, it will execute rest of the code while waiting.

Execution Context:

  • Whenever JS engine executes any script, it creates execution context.
  • Each execution context has 2 phases:
    1. Creation Phase:
      • When script executes for the first time, JS engine creates a 'Global Execution Context'
      • During this phase it performs following tasks:
        1. Create a Global Object (window for browser, for node = global)
        2. Create a This object binding (this = window)
        3. Setup memory heap for storing variables and function references
        4. Store the function definition in memory heap and variables within the global execution context with initial value as 'undefined'
    2. Execution Phase:
      • In this phase JS engine executes code line by line.
      • This is where it assigns values to variables &executes function calls.
      • A separate 'Function Execution Context' is created with it's own 'Creation' and 'Execution' phase for each function call.

Call Stack:

  • JS engine uses call stack to store all execution context created during the code execution
  • The call stack is a stack with LIFO (Last in First out) structure
  • Sample Code:
    function add(a, b){} return a + b;} function average(a, b){ return add(a, b)/2;} let x= average(10,20); --Diagram
  • Stack Overflow:
    • A common error in JS, whenever the maximum number of functions in 'call stack' are exceeded for any 'execution context' based on heap size of 10mb.
    • Can happen during recurring function calls.

Callback Functions:

  • In JS, call back is function passed into another function as an argument
  • Since JS is single threaded it handles callbacks with help of 'Event Loop' and a 'Callback Queue' 
  • Callbacks can be used in ways:
    1. Synchronous: Like map, filter, reduce etc.
    2. Asynchronous: Execute rest of the code while waiting for server calls, like setTimeout, fetch etc.
  • Event Loop (with Callback Queue):
    • JS engine uses 'Event Loop' to achieve asynchronousity with hep of 'Callback Queue' or 'Job Queue'(similar to callback queue but reserved for the new functionality of 'Promises')
    • Its job is to monitor the 'Call Stack' and determine when call stack is empty.
    • Whenever Call stack is found to be empty, 'Event Loop' looks into message queue (Job & Callback in that order) to see if any pending action waiting to be executed. If yes, push same to call stack.
    • Demo Code:
      console.log('first comment');
      setTimeout(function(){console.log('second comment');}, 5000);
      console.log('third comment');

      Output:
      first comment
      third comment
      //after 5 seconds
      second comment
      --diagram--
  • Callback Hell:
    • Too much nesting of callbacks, makes the code hard to read and manage.
    • Callbacks made for Poor Readability, Testing, Debugging, maintainability and Error handling.
    • Some solutions to these problems are:
      1. Write Proper Comments
      2. Split functions into smaller functions
      3. Using Promises(which come with chaining feature)
      4. Using Async/Await (a syntactical sugar built on top of Promises)

Promises:

  • It's a new approach to avoid 'callback hell'
  • A JS 'Promise' is a placeholder object that returns as value, which you hope to receive in the future (but not right now)
  • Any object returned 'new Promise()'  constructor has 2 internal properties: 
  • A 'state':
    1. Pending
    2. Fulfilled
    3. Rejected
  • A 'result'
    1. undefined (while promise is in pending state)
    2. value (actual value returned by promise when fulfilled)
    3. error (when promise is rejected)
  • Every promise finally moves one of the 2 states - 'fulfilled' or 'rejected', these 2 are known as 'settled'. So, when a promise is said to be 'settled', it can be in either state 'fulfilled' or 'rejected'.
  • Creating a Promise:
    • Promises are created using Promise constructor
    • The constructor accepts a function as an argument, this function is known as 'executor'
    • The 'executor' in turn takes 2 functions as argument, usually named 'resolve' and 'reject' by convention, but can be anything
    • When we call new Promise(executor), the executor is called automatically (usually when code reaches the first 'resolve' or 'reject'  method is reached)
    • Syntax:
      new Promise(executor);
      or
      new Promise( (resolve, reject)=> {});
      or
      new Promise( function(resolve, reject){
          //write code for success and call resolve('value');
          ///write code for  error and call reject('error');
      });
    • Sample Code:
      let completed = true;
      //initially task will be in 'pending' state
      let task = new Promise(function(resolve, reject){
         //call a setTimeout to achieve asyn
         setTimeout(()=> {
            if(completed){
            resolve('task resolved'); //moves promise(task object) to 'fulfilled' state with result value='task resolved'
            } else {
            reject('task rejected'); //moves promise(task object) to 'rejected' state with result error='task resolved'
            }
         }, 50000);
      });
      **as you will notice we didn't do anything to explicitly call the function(executor) in Promise constructor, the async code executes on it's own, after 5secs in this case, since it's a timeout
  • Consuming a Promise:
    • promise object provides several methods for consuming/chaining promises
      1. then(): takes 2 callback functions- 'onFulfilled': Called when promise is fulfilled, ignored otherwise and 'onRejected': called when promise is 'rejected', ignored otherwise. usually only onfulfilled is passed, as catch() method is preferred over 'onRejected'
      2. catch(): Executed when the promise is 'rejected' or an exception occurs in the then.
      3. finally(): Put the piece of code that needs to be executed, whether promise is fulfilled, rejected or errors out due to some exception.
  • Promise Chaining:
    • Promises have in-built support for chaining as the return value of a then() method is also a promise. So, we keep calling N number of then() method successively.
  • Promise.all():
    • Accepts a list of promises and returns a Promise that:
      1. resolved when every input promise has resolved. Or
      2. rejected when any input promise has been rejected.
    • Sample Code:
      Promise.all([job1, job2, job3])
       .then( results=> console.log(results)) //only after all jobs successful //results holds result for each job in same order as input
       .catch(err=> console.log(err)); //first rejected job
  • Promise.race()
    • Just like .all, this also accepts an array of promises.
    • But it return when first promise is settled( either resolved or rejected)
    • Sample code:
      Promise.race([job1, job2, job3])
       .then( result=> console.log(result)) //first successful jobs
       .catch(err=> console.log(err));//first rejected job

async/await keywords:

  • async & await are built on top of promises, allowing to write asynchronous code that looks more like synchronous with more readability
  • 'async' is added before a function, JS understands that such function will return a promise.
  • 'await' keyword makes JS wait until that promise is settled and returns result.
  • The 'await' keyword can only be used inside a function defined using async keyword.
  • Sample Code:
    async function hello(){ return 'hello';}
    async function greeting(){
      let msg = await hello();
      return msg + ' world'
    }
    greeting(); //a promise with 'hello world' value

Job Queue:

  • Recall the 'Event Loop' ? Apart from 'Callback Queue' for callback methods, Browsers have introduced a separate new queue for promises.
  • This queue is explicitly for promises.
  • When 'Call Stack' is empty, 'Event Loop' first picks message/action from 'Job Queue' (Promises) and only then 'Callback Queue'(old callback functionality)
  • --Diagram--
        **Ideal order of execution will be as following
  1. synchronous statements
  2. Promise statements //Job Queue has priority
  3. Callback statements //then callback queue

Generator Functions:

  • Generators are created by generator functions- function* f(){//some code with yield statement}
  • Generators do not execute body immediately when invoked
  • Generators can pause midway and resume their executions from where they were paused(yield statement)
  • The yield statement pauses the execution and returns a value (can be undefined as well)
  • Generators are great for creating Iterable, so we can use with 'for...of' loop
  • Sample Code:
    function* generator(){
        console.log('1st call');
        yield 1;
        console.log(''2nd call);
        yield 2;
    } let gen = generate() // generator function body is not called at this point
    //we have to use methods from generator object to start execution
    gen.next(); => returns an object with value:1 & done: false, usually you will keep calling till done is true

Session#6: Server Side JavaScript (SSJ)


Server Side Programming

  • Code that can run on the web server are known as server side programming.
  • Code running in browser is know as client-side programming
  • Server side code has full access to server's OS and developers can choose which programming language they wish to use as well as their versions.

SSJ: Programs that uses JS on server are called SSJ.

  • Though JS has traditionally been a client side language, Node.js makes it possible to write application in JS on the server.
  • Node.js:
    • It's a platform built on Chrome's JS runtime/engine(V8), for easily building fast and scalable network applications.
    • Features of Node.js:
      1. Asynchronous & Event Driven
      2. Fast
      3. Single Threaded (but highly scalable)
      4. No buffering (doesn't stream but sends data in chunks)
      5. MIT License(open source)
    • Download NodeJs : https://nodejs.org/
      **Installer includes 'npm' package manager
    • Check successful installation:
      > node -v or node --versions
      > npm -v or npm --versions

NPM

  • Node package Manager (but it contains other packages as well, not just node)
  • Creating a 'package.json' is typically first step in a node project
  • There are 2 ways to do it:
    1. Manual creation: npm init
    2. Automatic creation: npm init -y (with default values)
package.json:
  • This file holds metadata relevant to the project
  • It's used to give info to NPM that allows it to identify the project as well as handle it's dependencies.
  • Can contain other metadata like project description, version info of project in a particular distribution.
  • Also configuration metadata, all of which is vital to both npm and end user of project.
  • It's usually located at the root directory of any node.js project
  • sample from npm init -y command:
    name: current directory
    version: 1.0.0
    description: info from README or empty string
    main: index.html
    scripts: {test: default created} //add others and can be run using: npm run scriptCommandName
    keywords: []
    author: empty
    license: ISC
    bugs: from current directory if present
    homepage: from current directory if present
    //Non-default but important properties
    devDependencies: {}
    dependencies: {}
    peerdependencies:{} 
package naming: [a-z0-9~-]*+
  • Can start with: small letter, numbers, tilde or hyphen
Installing packages using npm:
  • We can use 3 version of npm install command for this:
    1. npm install package-name -g
      --> install package globally, use across projects
    2. npm install package-name --save
      --> Add to dependencies property, usually packages which are required for the app to run
    3. npm install package-name --save-dev
      --> Add to devDependencies property, usually packages which are required for development like unit test or minification.
Semantic Versioning: 
  • Or semver is a standard for package versioning.
  • Contains 3 parts, representing: Major.Minor.Patch
  • Few Sample versioning case scenarios:
    First Release (New Product) : 1.0.0
    Bug Fix (Backward Compatible): 1.0.1 (increment patch)
    New Feature (Backward Compatible): 1.1.0 (increment minor and reset patch)
    Changes that Breaks Backwards Compatibility: 2.0.0 (increment Major and reset minor and patch)
  • Semver Shorthand:
    1. latest: Always obtain latest release
    2. version: must match exact version, eg: 1.2.3
    3. 1.2.x: x can be anything, match rest, eg: 1.2.0, 1.2.1 etc. but not 1.3.0
    4. ^version: Compatible with version, akin to Maj.x.x, i.e. consider all future Minor and patches
    5. ~version: Approximately equivalent to the version, akin to Maj.Min.x , i.e. consider only future patches
Node core modules: 
  • comes with node, so no need to explicitly install, but need to import using 'require' keyword.
    1. http: Includes classes, methods and events to create node http server
    2. url:  Methods for url resolution and parsing
    3. querystring:  Method to deal with query string
    4. path:  Methods to deal with file paths
    5. fs:  Includes classes, methods and events to work with file I/O
    6. util:  Includes utility functions for programmers
    7. events:  Includes 'EventEmitter' class for creating and handling custom events
  • sample code for creating a node web-server:
    //load http library
    var http= require('http');
    //create server using http library
    var server = http.createServer(function(request, response){
        response.writeHead(200, {"Content-Type": "text/plain"});
        response.write('This goes in body');
        response.end('Hello World\n'); //mandatory as it sends response
      }
    );
    //make server listen on a port with optional function param for logging
    server.listen(8080, function(){ console.log('node server was started on port 8080'); } );
  • Custom Node Events:
    • These allow us to create and handle custom events by using 'events' module
    • The events module includes an 'EventEmitter' class, which can be used to raise (using 'emit') and handle (using 'on') custom events
    • Sample Code (Basic):
      var events= require('events');
      var eventEmitterObj= new events.EventEmitter();
      //create handler, a simple js function
      var eventHandler= function(){ console.log('Hello');}
      //assign handler to an event, just like addEventListener
      eventEmitterObj.on('greet', eventHandler);
      //fire/dispatch event
      eventEmitterObj.emit('greet');
Node.js Frameworks:
  • Frameworks give extra features to help quickly build & scale apps
  • Some Popular frameworks:
    1. AdonisJs
    2. Express.js
    3. Meteor.js
    4. Nest.js
    5. Sails.js
    6. koa.js
    7. LoopBack.js
    8. Hapi.js
    9. Derby.js
    10. Total.js
    11. socket.js

These notes have been complied from the video-series by Salesforce Troop YouTube channel.
I highly recommend watching his series.

Comments