Skip to content

Instantly share code, notes, and snippets.

@gusalbukrk
Last active October 26, 2021 21:53
Show Gist options
  • Save gusalbukrk/e3405383c37b926aed463f9fb3f96801 to your computer and use it in GitHub Desktop.
Save gusalbukrk/e3405383c37b926aed463f9fb3f96801 to your computer and use it in GitHub Desktop.
#js #javascript

JavaScript

Synchronous JavaScript

  • most things in JS are synchronous, which means one at a time and in top-to-bottom order
  • there're 2 styles of asynchronous code in JS: old-style async callbacks and newer-style promises (async/await is syntactic sugar over promises)
    • async callbacks = function passed as argument to another function when this higher-order function will execute code in the background; example: second parameter of addEvenListener

Event loop

  • keeps running continuously and checks the Main stack if it has any frames to execute, if not then it checks Callback queue
  • because of this your asynchronous code will run only after the Main Stack is done

Execution stack

  • FILO = first in last out
  • is where all the javascript code gets pushed and executed one by one as the interpreter reads the program, and gets popped out once the execution is done

Callback/event queue

  • FIFO = first in first out
  • if Callback queue has codes to execute then it pops the message from it to the Main Stack for the execution

Event table

  • asynchronous code gets removed from Main Stack and forwarded to Event table
  • this table is responsible for moving your asynchronous code to the callback queue after the asynchronous operation is completed
  • this happens with any asynchronous code except promises
    • e.g. setTimeout, ajax(), addEventListener()

Job queue

  • new queue introduced exclusively to handle promises
  • just like callback queue, jobs are executed only when the main stack is empty
  • has higher priority than the callback queue; all jobs in the job queue will be executed before any async code in the callback queue
  • e.g. promises always get executed before setTimeout

code

console.log('start');

const asyncCallback = (fn, timer) => setTimeout(() => fn(), timer);
asyncCallback(() => console.log('async callback'), 0);

(new Promise(res => res('promise'))).then(res => console.log(res));

(async function() { console.log(await (new Promise(res => res('async/await')))); })()

console.log('end'); 

// Result: start, end, promise, async/await, async callback

Execution context

  • is the environment in which a specific block of code is executed

Types

  • global execution context
  • functional execution context

Execution context creates

  • an object:
    • in global context = global object
    • in functional context = arguments object
  • this binding to:
    • in global context = global object
    • in functional context = the object calling the method or the window/global object for non-method functions
  • scope = which variables and functions are available to this code

Execution context phases

  • when an execution context is executed it goes through two phases: creation and execution

Creation phase

  • creates the global or functional execution context
  • set up memory space for variables and functions
  • hoisting: function statements are fully stored in memory and variables declared with var are stored with the initial placeholder value of undefined

Execution phase

  • code gets executed line by line
  • it's in this phase that variables are assigned values and function calls are executed

Execution context stack

  • global execution context is always the first context added to the stack
  • a new execution context is created with a function call, placed on top of the stack and the execution of the previous context is paused until this new context is done
  • after the execution of the topmost block is done, this block will be popped off from the stack and execution goes back to the item that was bellow it

Scope chain

  • every execution context has a reference to its outer scopes (if any), all the way up to the global scope
  • in JS, this reference is lexical - scope is defined by the location of the function definition (as opposed to the location of the function call)

Hoisting

  • JS engine behavior of put variables and functions declarations into memory during the creation phase (which happens before the execution phase)
  • it's relevant when you try to call a variable/function before its declaration

Declaration vs initialization

  • usually only declarations are hoisted, not initializations
    • except for functions statements, which are fully registered in the memory
    • variables declared with var get initialized to undefined
    • variables declared with let and const don't get initialized

Environment records

Declarative Environment Record

  • standard environment that you get when calling a function
  • its bindings are defined in some inaccessible internal data structure

Object Environment Record

  • uses an actual JS object to store bindings

Global Environment Record

  • consists in two environment records:
    • declarative = stores let, const & class declarations
    • object = stored in the global (window) object; includes var declarations, function statements and other globals that the browser (or other global environment) provides

Classes

  • functions that define classes are hoisted (like any function statement)
  • ES6 classes aren't hoisted

Var vs let & const

Scope

  • var is function scoped
  • let and const are block scoped = block scope is anything between curly braces

Arrow function

  • is a function expression that has a shorter syntax
  • are best suited for non-method functions, because the this in arrow functions won't point to the object
  • cannot be used as constructors, doesn't have arguments

this

  • do not bind its own this
  • instead, they inherit the one from the parent scope, which is called "lexical scoping"

For of vs for in

For of

  • for of creates a loop iterating over iterable objects (arrays, strings, NodeLists, iterators)
  • it returns a list of values of the object's properties

For in

  • for in iterates over all the enumerable properties of an object (including inherited properties ) in random order
  • returns a list of the object's key
  • it's not recommended to use the for in in a array because it can return elements out of order and enumerable properties that includes non-integer names and those that are inherited

Shallow vs deep copy

Shallow copy

  • a new object is created that has an exact copy of the values in the original object
  • if any of the fields of the object are references to other objects, just the reference addresses are copied
  • to shallow copy a literal array/object use Array.from() , array.slice() or the spread operator

Deep copy

  • an object is copied along with the objects to which it refers
  • to deep copy it use JSON stringify and parse. To deep copy objects with their prototype, a user defined function is needed

This

  • this gets set to the object used in the function call
    • e.g. myObj.f() = this is myObj
  • if there's no object in the function call (non-method function), defaults to global object or undefined in strict mode

Scope

  • this is the only dynamically scoped mechanism in JS, because
  • its value is defined by how it's called (which object precedes it)
  • and not by where it's defined (lexical scope)
  • the only exception is this inside arrow function, which is lexically scoped
let x = {
  f: function () {
    console.log(this);
  }
}

x.f(); // object x

f = x.f;
f(); // window

this in function calls inside methods

"use strict";

let x = {
  f: function() {
    
    function g() {
      console.log(this);
    }
    
    g();
  }
}

// the g function call is inside the x.f function and doesn't reference any object
x.f(); // window or undefined

lexically scoped this in arrow functions

let x = {
  f: function() {
    const g = () => console.log(this);
    g();
  }
}

// this in an arrow function inherit its this from its parent
x.f(); // object x

Changing 'this' value

  • allows for a function/method belonging to one object to be assigned and called for a different object
const foo = {
  x: 10
};

const bar = {
  x: 20,
  sum: function(y) {
    console.log(this.x + y);
  }
};

bar.sum.call(foo, 5); // takes a list of arguments
bar.sum.apply(foo, [5]); // takes an array of arguments
bar.sum.bind(foo, 5)(); // returns a function

Pass by value or reference

  • variable's value gets copied to another variable (b = a)
  • variables gets passed as function's arguments

By value

  • applies to primitives values (numbers, string, boolean...)
  • value is copied
  • any change made to this new variable or argument doesn't affect the passed value

By reference

  • applies to arrays & objects
  • doesn't copy the value, only saves a reference pointing to the passed value
  • any changes will affect both variables

Closure

let vs var

function f() {
  let x = [];

  for(var i = 0; i < 3; i++) {
    x.push(
      (function() {
        let j = i;
        return () => console.log(j)
      })()
    );
  }

  // returns an array of functions
  // these functions reference an outer variable ('i')
  // therefore closure happens
  return x;
}

let x = f();

// using var in the for loop, results will be all 3
// using let in the for loop, result will be 1 2 3
// thats because let creates a different variable every iteration of the loop,
// while var is the same (just gets reassigned)

x[0]();
x[1]();
x[2]();

Callback

  • a function passed into another function as an argument, which is then invoked inside the outer function at a given time
    • usually after another code (maybe asynchronous) got executed

Examples

  • functions that take a callback: forEach, promise, setTimeout, addEventListener

Partial application

  • bind = const add5 = sum.bind(null, 5);

OOP in JS

Primitives & objects

  • everything in JS is a
    • primitive (string, number, bigint, boolean, undefined, null and symbol)
    • or an object (arrays, objects, functions)

Prototypal inheritance

  • only objects (not primitives) have inheritance
  • inheritance is not class-based like Java and C++, it's prototypal

Primitives wrapper

  • when trying to access properties in a primitive
    • strings, numbers, boolean & symbols
  • the primitive will be wrapped in an object
  • after the property access is done, wrapper will be discard and everything goes back to normal

Prototype chain

  • each object (and subsequent object) has a private property which holds a link to another object called its prototype
  • the second to last prototype of every object is the Object prototype and the last one is the null

Access property

  • property will be sought on the object
  • if not found, sought on its first prototype and so on

Get an object's prototype

  • Object.getPrototypeOf(obj) or obj.__proto__

Iterators & generators

// when called, generators functions (function*) return a generator
function* counter() {
  let c = 0;
  
  yield ++c;
  yield ++c;
}

// an iterator is any object which implements the next() method which returns { value, done }
  // generators can return (yield) multiple values in iterative mode (one-at-a-time)
const iterator = counter(); // it returns a special type of iterator, called Generator

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next()); // done

Promises

Using then, catch and finally

// like callbacks, promises allow us to wait on certain code to finish execution prior to running the next bit of code

const myPromise = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const resp = true;

      resp ? resolve("done") : reject("error");
    }, 2000);
  })
};

myPromise()
  .then(resp => console.log(resp))
  .catch(err => console.warn(err))
  .finally(() => console.log("end"));

Using async and await

const myPromise = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const resp = false;

      resp ? resolve("done") : reject("error");
    }, 2000);
  })
};

(async () => {
  try {
    const result = await myPromise();
    console.log(result);
  } catch {
    console.warn("error");
  }
})()

Error handling

  • you can throw exceptions (include an error object) using the throw statement
  • handle exceptions using the try...catch statements
try {

  if (true) {
    const err = new TypeError("There's a type error...");
    throw err;
  }
  
} catch (err) {
  console.warn(err);
}

Eval

  • executes JavaScript code represented as a string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment