Back to Notes

JavaScript

Data Types

8 types: Number, BigInt, String, Boolean, Null, Undefined, Symbol, Object

typeof "abc"        // "string"
typeof 42           // "number"
typeof null         // "object" ← JS bug, null is NOT an object
typeof undefined    // "undefined"
typeof {}           // "object"
typeof []           // "object" (use Array.isArray())
typeof function(){} // "function"

Primitives are passed by value. Objects/Arrays are passed by reference.


var / let / const

varletconst
ScopeFunctionBlockBlock
HoistedYes (as undefined)Yes (TDZ)Yes (TDZ)
Re-declareYesNoNo
Re-assignYesYesNo

TDZ (Temporal Dead Zone): let/const exist in scope from declaration start but cannot be accessed before the line they're declared on — ReferenceError.

Variable Shadowing:

function test() {
  var a = "Hello";
  let b = "Bye";
  if (true) {
    let a = "Hi";      // ✅ legal shadowing
    let b = "Good";    // ✅ legal shadowing
    // var b = "Bad";  // ❌ illegal — var leaks to function scope, conflicts with let b
  }
}

Hoisting

JS executes in 2 phases:

  1. Creation: Allocates memory — varundefined, functions → full definition, let/const → TDZ
  2. Execution: Runs code line by line
console.log(x);  // undefined (var is hoisted)
var x = 5;

console.log(y);  // ReferenceError (let TDZ)
let y = 5;

greet();         // "Hello" (function declaration fully hoisted)
function greet() { console.log("Hello"); }

hi();            // TypeError: hi is not a function (function expression not hoisted)
var hi = function() { console.log("Hi"); }

Scope & Closures

Closure: A function that remembers its outer variables even after the outer function has returned.

function createCounter() {
  let count = 0;          // private — not accessible outside
  return function() {
    return ++count;
  };
}
const counter = createCounter();
counter(); // 1
counter(); // 2

Common Gotcha (loop + closure):

// Bug: all callbacks print 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

// Fix: use let (block-scoped per iteration)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

this Keyword

this is determined at call time, not definition time.

Call stylethis value
obj.method()obj
method() (standalone)undefined (strict) / window (sloppy)
new Constructor()new object
Arrow functionInherits from lexical scope

Arrow functions never have their own this.call(), .apply(), .bind() cannot override it.

bind / call / apply

const user = { name: "Alice" };
function greet(greeting) { console.log(`${greeting}, ${this.name}`); }

greet.call(user, "Hello");       // runs immediately, args individually
greet.apply(user, ["Hello"]);    // runs immediately, args as array
const bound = greet.bind(user);  // returns new function, does NOT run
bound("Hello");

Objects & Prototype

Reference semantics: copying an object copies the reference.

const a = { x: 1 };
const b = a;
b.x = 2;
console.log(a.x); // 2 — same object

Prototype chain: JS looks up missing properties along __proto__ chain up to Object.prototype.

const animal = { breathes: true };
const dog = Object.create(animal);  // dog.__proto__ === animal
dog.name = "Rex";
console.log(dog.breathes); // true — found on prototype

Writing always goes to the object itself, not the prototype.


Promises & Async/Await

3 states: Pending → Fulfilled / Rejected (immutable once settled)

Promise static methods

MethodBehaviorFailure
Promise.all(arr)Wait for all to fulfillFail-fast on first rejection
Promise.allSettled(arr)Wait for all to finishNever rejects, returns {status, value/reason}
Promise.race(arr)First to settle winsReturns first result/error
Promise.any(arr)First to fulfill winsRejects only if all fail (AggregateError)
// Sequential — slow (each waits for previous)
const a = await fetchA();
const b = await fetchB();

// Parallel — fast
const [a, b] = await Promise.all([fetchA(), fetchB()]);

Microtask queue: Promise callbacks (.then, .catch) run before setTimeout/setInterval macrotasks.


Event Loop

Call Stack → Microtask Queue → Macrotask Queue
             (Promises, queueMicrotask)  (setTimeout, setInterval, I/O)
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Output: 1, 4, 3, 2
// Microtasks (Promises) always run before macrotasks (setTimeout)

Array Operations Complexity

OperationTime
push() / pop()O(1)
shift() / unshift()O(n)
slice() / filter() / map() / reduce()O(n)
sort()O(n log n)
concat()O(m + n)
indexOf() / find()O(n)

Map & Set Complexity

OperationMapSet
get/set/has/deleteO(1)O(1)
clear()O(n)O(n)

Map vs Object: Map keys can be any type (including objects). Map is iterable and has .size. Object keys are always strings/Symbols.

WeakMap: Keys must be objects. Weak references — entries auto-removed when key is GC'd. Not iterable.


JSON

JSON.stringify(obj)  // Object → JSON string
JSON.parse(json)     // JSON string → Object

See Also

  • [[JavaScript DOM Notes]] — DOM tree, manipulation, events
  • [[JavaScript Function Code Examples for Interviews]] — debounce, throttle, currying, polyfills, Promise implementations
  • [[Java Script Language Topics]] — complexity tables reference