0 / 51 read
All topics

JavaScript Concepts

Core JavaScript concepts including closures, hoisting, prototypes, and async patterns

51 questions0 read13 interview prep

Questions38 shown

JavaScript is a high-level, interpreted, dynamically typed programming language.

Key characteristics:

Multi-paradigm — supports OOP, functional, and event-driven programming

Single-threaded — runs on one thread with an event loop for async operations

Dynamically typed — variables can hold any type without declaration

Prototype-based — uses prototypal inheritance instead of classical

First-class functions — functions are values that can be assigned, passed, returned

JavaScript runs in browsers (client-side) and on servers via Node.js (server-side). It is the only language that runs natively in web browsers.

JavaScript is single-threaded — it has one call stack and one memory heap.

However, it achieves concurrency through:

1. Event Loop — Continuously checks if the call stack is empty, then moves callbacks from the task queue

2. Web APIs — Browser provides setTimeout, fetch, DOM events in separate threads

3. Microtask Queue — Promises, queueMicrotask (higher priority)

4. Macrotask Queue — setTimeout, setInterval, I/O

Execution order:

1. Synchronous code (call stack)

2. Microtasks (Promise.then, queueMicrotask)

3. Macrotasks (setTimeout, setInterval)

Web Workers allow true multi-threading for CPU-intensive tasks but cannot access the DOM.

1. External script file:

Code
<script src="app.js"></script>

2. Inline script:

Code
<script>
  console.log("Hello World");
</script>

Best practices:

Place scripts at the bottom of <body> or use defer/async

External files are preferred for caching and maintainability

Use type="module" for ES modules

Code
<script src="app.js" defer></script>

Primitive types (7):

1. string"hello"

2. number42, 3.14, NaN, Infinity

3. booleantrue, false

4. undefined — declared but not assigned

5. null — intentional absence of value

6. symbol — unique identifiers

7. bigint — large integers 123n

Reference type:

object — includes arrays, functions, dates, regex, maps, sets

Key difference: Primitives are immutable and compared by value. Objects are mutable and compared by reference.

Code
typeof "hello"    // "string"
typeof 42         // "number"
typeof null       // "object" (historic bug)
typeof undefined  // "undefined"

Hoisting is JavaScript's behavior of moving variable and function declarations to the top of their scope during the compilation phase.

Function declarations are fully hoisted:

Code
sayHello(); // Works!
function sayHello() { console.log("Hi"); }

`var` declarations are hoisted but initialized as undefined:

Code
console.log(x); // undefined
var x = 5;

`let`/`const` are hoisted but NOT initialized (Temporal Dead Zone):

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

Function expressions are NOT hoisted:

Code
greet(); // TypeError: greet is not a function
var greet = function() { };
Code
// This works because function declarations are hoisted
sayHello(); // "Hi"
function sayHello() { console.log("Hi"); }

// This fails - TDZ
console.log(x); // ReferenceError
let x = 10;

A closure is a function that retains access to its lexical scope even when executed outside that scope.

Code
function createCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    getCount: () => count,
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2
// count is not accessible directly

Use cases:

1. Data privacy — Encapsulate variables

2. Factory functions — Create specialized functions

3. Memoization — Cache expensive computations

4. Event handlers — Preserve state in callbacks

5. Module pattern — Create private/public interfaces

Code
function createCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    getCount: () => count,
  };
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2

An IIFE (Immediately Invoked Function Expression) is a function that runs as soon as it is defined.

4 ways to create an IIFE:

Code
// 1. Parentheses wrapper
(function() { console.log("IIFE 1"); })();

// 2. Parentheses around call
(function() { console.log("IIFE 2"); }());

// 3. Arrow function
(() => { console.log("IIFE 3"); })();

// 4. Void operator
void function() { console.log("IIFE 4"); }();

Use cases:

Avoid polluting the global scope

Create private variables

Execute async code at the top level (before top-level await)

Module pattern implementation

Code
// IIFE with parameters
const result = ((x, y) => x + y)(3, 4);
console.log(result); // 7

| Feature | var | let | const |

|---------|-------|-------|--------|

| Scope | Function-scoped | Block-scoped | Block-scoped |

| Hoisting | Hoisted + initialized undefined | Hoisted + TDZ | Hoisted + TDZ |

| Re-declaration | Allowed | Not allowed | Not allowed |

| Re-assignment | Allowed | Allowed | Not allowed |

| Global object | Added to window | Not added | Not added |

Code
if (true) {
  var x = 1;   // accessible outside block
  let y = 2;   // only in this block
  const z = 3; // only in this block
}
console.log(x); // 1
console.log(y); // ReferenceError

Best practice: Use const by default, let when reassignment is needed, avoid var.

Code
const PI = 3.14;
let count = 0;
count = 1; // OK
// PI = 3.15; // TypeError

A Promise is an object representing the eventual completion or failure of an asynchronous operation.

Three states:

1. Pending — Initial state, neither fulfilled nor rejected

2. Fulfilled — Operation completed successfully

3. Rejected — Operation failed

Code
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Success!");
    // or reject(new Error("Failed!"))
  }, 1000);
});

promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log("Done"));

Promise combinators:

Promise.all() — Waits for all, fails on first rejection

Promise.allSettled() — Waits for all, returns all results

Promise.race() — Returns first settled

Promise.any() — Returns first fulfilled

Code
const fetchData = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve("Data!"), 1000);
});

fetchData().then(console.log).catch(console.error);

async/await is syntactic sugar over Promises that makes async code look synchronous:

Code
// With Promises
function getUser() {
  return fetch("/api/user")
    .then(res => res.json())
    .then(user => fetch(`/api/posts/${user.id}`))
    .then(res => res.json());
}

// With async/await
async function getUser() {
  const res = await fetch("/api/user");
  const user = await res.json();
  const postsRes = await fetch(`/api/posts/${user.id}`);
  return postsRes.json();
}

Error handling: Use try/catch instead of .catch()

Code
try {
  const data = await fetchData();
} catch (error) {
  console.error(error);
}

Key: await can only be used inside async functions (or with top-level await in modules).

Code
async function loadData() {
  try {
    const response = await fetch("/api/data");
    const data = await response.json();
    return data;
  } catch (err) {
    console.error("Failed:", err);
  }
}

Currying transforms a function with multiple arguments into a sequence of functions each taking a single argument.

Code
// Normal function
function add(a, b, c) { return a + b + c; }
add(1, 2, 3); // 6

// Curried version
function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}
curriedAdd(1)(2)(3); // 6

Benefits:

1. Partial application — Pre-fill arguments: const add5 = curriedAdd(5);

2. Reusability — Create specialized functions from generic ones

3. Composition — Easier to compose small functions

4. Cleaner codearray.map(multiply(2)) instead of array.map(x => multiply(2, x))

Code
const curry = (fn) => {
  const arity = fn.length;
  return function curried(...args) {
    if (args.length >= arity) return fn(...args);
    return (...nextArgs) => curried(...args, ...nextArgs);
  };
};

const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3)); // 6
console.log(add(1, 2)(3)); // 6

An Execution Context is the environment where JavaScript code is evaluated and executed.

Types:

1. Global Execution Context — Created when the script starts. Sets up window (browser) or global (Node.js) and this.

2. Function Execution Context — Created each time a function is called.

3. Eval Execution Context — Created by eval() (avoid using).

Two phases:

1. Creation Phase:

- Creates variable object (hoisting)

- Sets up scope chain

- Determines this value

2. Execution Phase:

- Assigns values to variables

- Executes code line by line

The Call Stack manages execution contexts — new contexts are pushed when functions are called and popped when they return.

this refers to the object that is executing the current function. Its value depends on how the function is called:

1. Global context: this = window (browser) or global (Node.js)

2. Object method: this = the object

3. Constructor (new): this = new object being created

4. Arrow function: this = lexical this (from enclosing scope)

5. Event handler: this = element that triggered the event

6. call/apply/bind: this = explicitly set object

Code
const obj = {
  name: "Alice",
  greet() { console.log(this.name); },         // "Alice"
  greetArrow: () => { console.log(this.name); } // undefined
};

Rule of thumb: Regular functions get this from the call site. Arrow functions get this from where they are defined.

Code
const person = {
  name: "Alice",
  greet() { console.log(`Hi, ${this.name}`); }
};
person.greet(); // "Hi, Alice"

const greet = person.greet;
greet(); // "Hi, undefined" (this is window)

These methods explicitly set the this value for a function:

`call(thisArg, arg1, arg2, ...)` — Calls immediately with individual args:

Code
function greet(greeting) { console.log(`${greeting}, ${this.name}`); }
greet.call({ name: "Alice" }, "Hello"); // "Hello, Alice"

`apply(thisArg, [argsArray])` — Calls immediately with array of args:

Code
greet.apply({ name: "Bob" }, ["Hi"]); // "Hi, Bob"

`bind(thisArg, arg1, ...)` — Returns a new function with bound this:

Code
const boundGreet = greet.bind({ name: "Carol" });
boundGreet("Hey"); // "Hey, Carol"

Key difference: call and apply invoke immediately; bind returns a new function.

Code
const person = { name: "Alice" };
function greet(greeting, punct) {
  return `${greeting}, ${this.name}${punct}`;
}

greet.call(person, "Hello", "!");  // "Hello, Alice!"
greet.apply(person, ["Hi", "."]); // "Hi, Alice."
const bound = greet.bind(person, "Hey");
bound("?"); // "Hey, Alice?"

Every JavaScript object has an internal `[[Prototype]]` link to another object, forming a **prototype chain**. [code block] **How property lookup works:** 1. Look on the object itself 2. Look on `ob...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

Interview Preparation

Premium

High-signal questions that come up most in real interviews. These are the ones worth spending extra time on.

A **Set** is a collection of unique values — no duplicates allowed. [code block] **Differences from Arrays:** | Feature | Set | Array | |---------|-----|-------| | Duplicates | Not allowed | Allowed...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

A **stale closure** occurs when a closure captures a variable but the variable has changed since the closure was created. [code block] **Problem:** `var` is function-scoped, so all closures share th...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

A **stale closure** occurs when a closure captures a variable that has since changed, but the closure still references the old value. [code block] **Common in React:** [code block] **Fix:** Use ref...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

The `==` operator performs **type coercion** before comparing: [code block] **Coercion rules (simplified):** 1. `null == undefined` → true 2. Number vs String → convert String to Number 3. Boolean v...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

**Map** — Key-value pairs where keys can be any type: [code block] **WeakMap** — Keys must be objects and are weakly referenced: [code block] **Differences:** | Feature | Map | WeakMap | |---------|...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

A `Symbol` is a **unique, immutable** primitive value used as an identifier for object properties. [code block] **Use cases:** 1. **Private-like properties:** [code block] 2. **Well-known symbols:*...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

**`for...in`** iterates over **enumerable property names** (keys): [code block] **`for...of`** iterates over **iterable values**: [code block] | Feature | `for...in` | `for...of` | |---------|------...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

Functional programming (FP) is a paradigm that treats computation as the evaluation of mathematical functions. **Core principles:** 1. **Pure functions** -- Same input always produces same output, no...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

Inversion of Control (IoC) is a design principle where the **control flow is inverted** -- instead of your code calling library code, the framework calls your code. **With callbacks (IoC problem):** ...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

A tagged template literal lets you parse template literals with a custom function: [code block] **Real-world uses:** - `styled-components` -- CSS-in-JS: `` styled.div`color: red;` `` - `graphql-tag`...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

**Map** -- Key-value pairs where **keys can be any type**: [code block] **WeakMap** -- Keys must be **objects**, and they are **weakly held** (garbage-collectable): [code block] | Feature | Map | We...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

`WeakRef` creates a **weak reference** to an object, allowing it to be garbage collected even while the reference exists. [code block] **Use cases:** 1. **Caching** -- Cache objects without preventi...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.

**`setTimeout`** -- Executes a callback after a minimum delay: [code block] **`requestAnimationFrame`** -- Executes before the next repaint, synced with display refresh rate: [code block] | Feature ...

Sign in to continue reading

Create a free account to unlock login-level content, or go premium for everything.