Home

JavaScript Under the Hood

Javascript is single threaded language. The thread has a call stack and memory heap. This cheat sheet goes through how those things work and other things on how javascript works under the hood.

LIFO/FIFO

LIFO - Last in first out.

FIFO - First in first out.

Call Stack

The call stack is LIFO and is a list of functions that need to be ran. The bottom is always the Global Execution Context.

Example 1

function first(){console.log("first")}
function second(){console.log("second")}
function third(){console.log("third")}

first()
second()
third()
         | Call Stack |
---------|------------|---------
third -> | second     | -> first
         | Global EV  |

Example 2

When you have functions inside functions it is added to the call stack.

function first(){
  console.log("first")
  second()
}

function second(){
  console.log("second")
  third()
}

function third(){
  console.log("third")
}

first()
Call Stack
third
second
first
Global EC

Callback Queue and Event Loop

This is a good resource.

When an async function is called it runs synchronously like any other function. If an await is encountered, it passes the code to the webAPI or the NodeAPI.

The webAPIs or the NodeAPIs run in a different threads and are called when async operations need to be performed.

When the async operation finishes, like the timer in setTimeout or fetch gets back data, the callback function(with async, the code after the await) is added to the callback queue.

The event loop checks if the callback stack is empty. If it is empty it checks for the next callback function in the event queue. It then puts that function in the callback stack.

The synchronous functions always have higher priority than any function in the callback queue.

Global and Function Execution Context

The global execution context is the outermost scope in JS.

The function execution context is created when a function is invoked and variables declared in the function can only be used in the function.

Use Strick

Use strick is used to convert bad syntax to errors.

To use use strick you can either put "use strick" at the top of your js file or put "use strick" at the top of functions to just use strick for that function.

Useful terms

Term Definition
Higher order functions Functions with other functions as arguments
Shallow copy Pass by reference
Deep copy Pass by value
Lexical Environnement  

Closures

Closures are functions that are returned from another function, that allow you to access private variables in the outer function.

When a function is returned a closure, in the backend, is made which holds the private variables.

function bankAccount() {
  const checking = 400
  const savings = 1000

  return {
    displayFunds: function () {
      console.log(`You have $${checking} in your checking account and $${savings} in your savings account`)
    },
  }
}

Factory functions are like closures, but are given arguments.

Var/Let trick question

// var is scoped to the global scope therefore it doesn't change closures
// If i were let it would be scoped to the for loop and would have its own closure.
  // If it was let it would be 0, 1, 2
for(var i = 0; i < 3; i++){
  const log = () => {
    consol.log(i)
  }

  setTimeout(log, 100)
}

// This will output 3 3 3

Big O Notation

Because an increasingly complex computer can run any algorithm faster, in order to compare algorithms we use a standard notation called Big O Notation.

Big O Notation removes any constants and only accounts for the number of inputs so you don’t have to worry about different computers.

Big O notation uses the syntax of O().

This is the efficiency in order of worst to best when n is very large.

The Big O of a for loop is O(n)

The Big O of a nested for loop in another for loop is O(n^2)

Big O notation is always the worst case scenario

Progressive Web Apps (PWAs)

A progressive web app(PWA) is a web application that performs like a native app.

Need to research

When you code for a PWAs you need to check if a feature is there in order to use it.

This is js on the front end.

// Note: window.navigator. is the same as navigator.
if("serviceWorker" in navigator){}
if("bluetooth" in navigator){}
if("geolocation" in navigator){
  navigator.geolocation.getCurrentPosition()
}
if("contacts" in navigator && "ContactsManager" in window){
  const props = ["name", "email", "tel", "address", "icon"]
  const opts = {multiple: true} // Allows selecting multiple contacts
  const contacts = await navigator.contacts.select(props, opts)
}
// etc

Manifest

Meta data and settings for the PWA.

https://developer.mozilla.org/en-US/docs/Web/Manifest/

The only settings that are required are name and short_name

{
  "name": "Web App Name",
  "short_name": "name", // Name on the device home screen or app launcher
  "description": "The description of the web app.",
  "start_url": "/", // Specifies the starting url path when the PWA is launched. Ex: "start_url": "/home" will be https://yourwebsite.com/home
  "icons": [
    {
      "src": "icons/icon-32.png",
      "sizes": "32x32",
      "type": "image/png"
    },
    // More icons
    // SVG icon
  ],
  "orientation": "portrait", // default orientation for the PWA
  "display": "standalone", // fullscreen(Puts the webapp in fullscreen like a video), standalone(standalone app), minimal-ui(mini ui for controlling navigation), browser(normal browser tab)
  "theme_color": "#4285F4", // The ui color theme of the browser
  "background_color": "#FFFFFF", // Sets the background color of the splash screen
}

You have to link the manifest file in your html

<link rel="manifest" href="/manifest.json">

You can put the manifest file in the webpack.config.js with npm install webpack-pwa-manifest --save-dev

const WebpackPwaManifest = require('webpack-pwa-manifest')
// Put in the plugins array
  new WebpackPwaManifest({
    name: "Just Another Text Editor",
    // More manifest fields
  })

Service worker

The service worker is a separate thread that runs in the background which allows for offline functionality, push notification, and background synchronization(app keeps running even if its not on the screen).

The 3 steps for service worker.

In your main js file

// Register the service worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(registration => {
      console.log(`Service Worker registered: ${registration}`)
    })
    .catch(error => {
      console.error(`Error registering Service Worker: ${error}`)
    })
}

In your service-worker.js file in the client

This can all be automatically generated by workbox-webpack-plugin

const cacheName = "my-cache-name"

// Install the service worker. Cache the files
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(cacheName).then(cache => {
      return cache.addAll([ // The files built by webpack
        '/',
        '/index.html',
        '/bundle.js',
        '/images/logo.png'
      ])
    })
  )
})

// Activate the service worker. Used to clean up past caches
self.addEventListener("activate", (event) =>
  event.waitUntil(
    caches.keys().then((keyList) =>
      Promise.all(
        keyList.map((key) => {
          if (key !== cacheName) {
            return caches.delete(key)
          }
        })
      )
    )
  )
)

// Claim the service worker so the service worker intercepts requests to other servers
  // Server/Websites -> Service worker -> Client's webpage
self.addEventListener("activate", (event) => {
  event.waitUntil(clients.claim())
})

// When a request is fetched
self.addEventListener("fetch", event => {
  event.respondWith((async () => {
    // Check if resource is cached and if so return cache
    const result = await caches.match(event.request)
    if(result) return result
    // If not, retrieve the resource
    const response = await fetch(event.request)
      // Cache it, and then return it. You may not want this
        // const cache = await caches.open(cacheName)
        // cache.put(event.request, response.clone())
    return response
  })())
})

Push notifications

Importing and Exporting

// You can either import the defaulted export(without the {}s) or just the exported value(with the {}s)
import defaultExport from "npmPackage"
import {regularExport} from "./javascriptFile"
import {obj: {key1, key2}} from "./anotherJSFile" // dereferencing an object inside an export
import "jsFile" // This will just run the jsFile.
import defaultExport as anotherDefaultName, {regularExport as anotherName} from "./javascriptFile" // Importing as a different name

// In separate files
export default function defaultThing(){console.log("default export")}
export function regularExport(){console.log("regular export")}
export const obj ={
  key1: "value1",
  key2: "value2"
}

Lighthouse

Test the performance, accessability, SEO, and other things for your web app.

Inspect element and Lighthouse tab.

Client-Server Folder Layout

You want to separate the package.json/dependencies into separate client and server side for organization reasons.

client
  - uses webpack
  - dist
  - src
  - package.json
server
  - uses express
  - package.json
package.json

In your express server add app.use(express.static("../client/dist")) in order to load the webpack built files which are usually in the dist folder inside client.

In the main package.json file

Scripts

{
  "name": "Project Name",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start:dev": "concurrently \"cd client && npm run build\" \"cd server && npm run server\" ",
    "start": "npm run build && cd server && node server.js",
    "server": "cd server node server.js --ignore client",
    "build": "cd client && npm run build",
    "install": "cd server && npm i && cd ../client && npm i",
    "client": "cd client && npm start"
  },
  "devDependencies": {
    "concurrently": "^5.2.0"
  }
}

Run npm run start