Skip to content

JavaScript

JavaScript Characteristics

  • Interpreted and just-in-time compiled (via the V8 Engine)
  • Java-like syntax, but influenced by functional languages
    • Early JavaScript syntax was functional, similar to Scheme (e.g., Racket)
  • Prototype-based object-oriented language
  • Dynamically typed (like Python)
    • Not statically typed (like Java)
  • Loosely/weakly typed
    • Not strongly typed (like Python)
    • e.g., "5" - 2 → coerces to 3
  • Based on an evolving standard
    • ECMAScript (e.g., ES6 and later)
  • Supports:
    • Strict mode (safer, stricter syntax)
    • Non-strict mode ("sloppy mode")
    • Vite and TypeScript default to strict mode such that x = 10; // ❌ ReferenceError: x is not defined

Basics

  • Semicolon line endings are optional
    • But it's recommended to use them.
  • Console output (e.g., in a browser)

    console.log(...)
    
  • Control structures (similar to C/Java):

    if (...) { ... } else { ... }
    for (...) { ... }
    while (...) { ... }
    switch (...) { ... }
    
  • Ternary operator

    A ? B : C  // equivalent to "if A then B else C"
    

Variables and Types

  • Use let and const to declare variables.

    • var is outdated (non-block scope, hoisting issues).
    • Hoisting means JavaScript moves declarations to the top of their scope (function or global) during compilation — before any code runs.

      • Only declarations are hoisted — not initializations.
      console.log(x); // undefined
      var x = 5;
      

      Internally, JavaScript interprets this as:

      var x;         // hoisted declaration
      console.log(x); // undefined
      x = 5;         // assignment stays in place
      

    Note

    🚫

    WARNING

    When you declare a variable with var inside a block ({}), it’s not scoped to that block — it’s scoped to the entire enclosing function (or global if outside a function).

    function test() {
      if (true) {
        var x = 10;
      }
      console.log(x);  // ✅ Outputs: 10 (because `var` is function-scoped)
    }
    
  • Primitive types:

    • boolean, number, string, null, undefined
    • Everything else is an object (including arrays).
  • undefined means the variable has been declared but not assigned.
  • JavaScript supports automatic type conversion.
  • Use typeof to check the type of a variable (returns a string).
    • Note: typeof often returns "object" even for arrays.

Truthy and Falsy in JavaScript

JavaScript uses automatic type conversion, which can lead to surprising behavior with ==.

Examples with == (loose equality)

0 == ""   // true!
1 == "1"  // true!

These are true because of type coercion.

Prefer === (strict equality) to avoid unexpected results

0 === ""   // false
1 === "1"  // false
  • === checks both value and type, avoiding type coercion.

Logical OR and Nullish Coalescing

  • || is the logical OR operator

    • Commonly used to assign a default if the variable is falsy

      let v;
      v = v || 456;  // v is 456 since v was undefined
      
    • ⚠️ May lead to bugs due to truthy/falsy behavior

      let v = 0;
      v = v || 456;  // v is 456 since 0 is falsy
      
  • ?? is the nullish coalescing operator

    • Only uses default if the value is null or undefined

      let result = value ?? defaultValue;
      
      let v = 0;
      v = v ?? 456;  // v is 0 since it's not null or undefined
      

Functions

  • Function declaration
function add1(a, b) { return a + b; }
console.log(add1(1, 2));  // 3
  • Function expression
const add2 = function (a, b) { return a + b; }
console.log(add2(1, 2));  // 3
  • Arrow function expression (also called "lambda notation")
const add3 = (a, b) => a + b;
console.log(add3(1, 2));  // 3

First Class Functions

  • Functions can be assigned to variables
function sayHello() { return "Hello, "; }
const saySomething = sayHello;
console.log(saySomething());  // "Hello, "
  • Functions can be passed to other functions (e.g., callback functions)
    • A callback function is a function that is passed as an argument to another function and is invoked (called) later during the execution of that function.
function greeting(msg, name) { return msg() + name; }
console.log(greeting(sayHello, "Sam"));  // "Hello, Sam"
  • Functions can be returned from other functions (e.g., factory functions)
function makeGreeting() {
  return function (name) { return "Hi " + name; };
}

const greet = makeGreeting();
console.log(greet("Sam"));  // "Hi Sam"

Closures

A closure occurs when an inner function retains access to the outer function’s state.

  • Inner function referencing outer scope
function makeRandomGreeting() {
  const sal = Math.random() > 0.5 ? "Hi" : "Hello";
  return function (name) { return sal + " " + name; }
}

const greeting = makeRandomGreeting();
console.log(greeting("Sam"));  // ?? Sam
  • Closure with function parameter
function makeGreeting(sal) {
  return function (name) { return sal + " " + name; }
}

const greeting1 = makeGreeting("Hello");
console.log(greeting1("Sam"));  // Hello Sam

Passing Functions to Factory Functions

  • Factory function that accepts another function as input:
function makeGreeting(msg) {
  return function (name) { return msg() + name; }
}

function sayHello() { return "Hello, "; }

const greeting2 = makeGreeting(sayHello);
console.log(greeting2("Sam"));  // Hello, Sam
  • Using anonymous arrow function (lambda):
const greeting3 = makeGreeting(() => "Howdy! ");
console.log(greeting3("Sam"));  // Howdy! Sam

String Template Literals

  • String literals delimited by backticks ``` enable:
    • String interpolation
    • (Also supports multi-line strings and tagged templates, not covered here)
  • Example
const v = 15.7;
const units = "cm";
  • Without string interpolation:
let msg = "Length is " + v + " " + units + ".";
  • With string interpolation:
let msg = `Length is ${v} ${units}.`;
  • Using expressions in template literals:
let msg = `Length is ${((v / 100).toFixed(2) * 100)} cm.`;

JavaScript Objects

  • JavaScript objects can be defined using JSON-like syntax:
const square = {
  colour: "red",
  size: 10,
  draw: function () {
    return `A ${this.size} pixel ${this.colour} square.`;
  }
};
  • Get a property:
console.log(square.colour); // red
  • Set a property:
square.colour = "blue";
  • Call a method (function property):
console.log(square.draw()); // A 10 pixel blue square.

Class (like a “template” for creating objects)

  • The class keyword is syntactic sugar over JavaScript's prototypal inheritance.
class Shape {
  constructor(colour) {
    this.colour = colour;
  }
  draw() {
    return `A ${this.colour} shape.`;
  }
}
  • Inheritance is done using extends, and super() calls the parent constructor:
class Square extends Shape {
  constructor(colour, size) {
    super(colour);          // Calls the Shape constructor
    this.size = size;
  }
  draw() {
    return `A ${this.colour} square size ${this.size}`;
  }
}
  • Create a new instance:
const square = new Square("red", 10);

Arrays

Arrays are an example of an iterable object.

  • Declaring Arrays
let arr1 = [];                // empty array with length 0
let arr2 = Array(5);          // empty array with length 5
let arr3 = [1, 2, 3, 4, 5];   // populated array
let arr4 = Array(5).fill(99); // 5 elements, all 99
  • Iterating Over Arrays
for (let i = 0; i < arr3.length; i++) {
  console.log(arr3[i]);
}

for (const x of arr3) {
  console.log(x);
}

arr3.forEach((x) => console.log(x));

Array Methods

  • foreach
  • sort
  • reverse
  • splice
  • indexOf
  • … many more

Note some mutate the array and some don't

Common Functional Array Methods

let arr3 = [1, 2, 3, 4, 5];
  • map returns array with transformed elements:
const arr4 = arr3.map((x) => x * 10);
// [10, 20, 30, 40, 50]
  • find returns first element that satisfies condition:
const a = arr3.find((x) => x % 2 == 0);
// 2
  • filter returns all elements that satisfy condition:
const arr5 = arr3.filter((x) => x % 2 == 0);
// [2, 4]
  • reduce executes a function that accumulates to a single return value:
const arr6 = arr3.reduce((acc, x) => acc + x, 0); // 0 is the initial value of acc
// 15

Destructuring Assignment

Unpack array elements or object properties into distinct variables

  • From Arrays
let arr3 = [1, 2, 3, 4, 5];
let [a, b] = arr3;  // a = 1, b = 2
  • From Objects
let obj = { "a": 1, "b": 2, "c": 3 };
let { a, b } = obj;  // a = 1, b = 2
  • Renaming destructured variables from objects
let obj = { "a": 1, "b": 2, "c": 3 };
let { a: x, b: y } = obj;  // x = 1, y = 2

b: y means "unpack value for b and store in y"

Spread Syntax and Rest Syntax

  • Spread: expands an iterable object (like arrays or strings)
let arr3 = [1, 2, 3, 4, 5];
let arr4 = [-1, 0, ...arr3, 6, 7];
console.log(arr4);  // [-1, 0, 1, 2, 3, 4, 5, 6, 7]
  • Rest: condenses multiple elements into a single variable
let arr3 = [1, 2, 3, 4, 5];
let [a, b, ...c] = arr3;
console.log(c);  // [3, 4, 5]
  • Rest with objects
const obj = { a: 1, b: 2, c: 3 };
let { a, ...x } = obj;
console.log(x);  // { b: 2, c: 3 }

let { b, ...y } = obj; // Extract the property named b from obj and store it in variable b.
// y is { a: 1, c: 3 }

Create a Prepopulated Array using spread and map

let arr5 = [...Array(5)].map((_, i) => i * 10);

// Breakdown:
// [...Array(5)] → creates an array with 5 undefined elements and spreads them into a new array
// .map((_, i) => i * 10) → maps each index to a value by multiplying it by 10
// _ is used to ignore the undefined value

console.log(arr5); // [0, 10, 20, 30, 40]

The index parameter i is optional in map