JavaScript Glossary

Plain-English definitions for the JavaScript terms you keep running into. No guessing, no searching - just clear explanations with examples.

Beginner Friendly Quick Reference With Examples

Hoisting is the behavior where JavaScript moves variable and function declarations to the top of their scope before any code runs. You can think of it as JavaScript reading through your code first, registering all the declarations, and only then starting execution.

This happens during the creation phase of the execution context. JavaScript scans your code, finds var declarations and function declarations, and sets them up in memory before the first line executes.

Only the declaration is hoisted, not the value. A var variable is initialized to undefined until its assignment line runs.

var hoisting

Variables declared with var are hoisted and initialized to undefined. This means you can reference them before their declaration without getting an error - though the value will be undefined.

javascript

console.log(score); // undefined (not an error - hoisted to top)
var score = 100;
console.log(score); // 100

Function declaration hoisting

Function declarations are fully hoisted - the entire function body is available from the start of the scope. You can call a function before the line where it is defined.

javascript

greet(); // "Hello!" - works because the declaration is fully hoisted

function greet() {
  console.log("Hello!");
}

let and const are also hoisted

let and const are hoisted too, but they are not initialized. Accessing them before their declaration line throws a ReferenceError. This gap is called the Temporal Dead Zone.

javascript

console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "Alice";
Key Takeaway

Hoisting is not magic - it is the result of JavaScript's two-phase execution: a creation phase (where declarations are registered) followed by an execution phase (where code actually runs).

To learn more, see the Execution Context tutorial and the Functions Advanced tutorial.

Asynchronous means that a task can start, then be set aside while other code keeps running, and picked up again later when the task is ready. It is the opposite of synchronous (sequential) code where each line must finish before the next one starts.

JavaScript runs on a single thread, meaning it can only do one thing at a time. Asynchronous programming lets that single thread stay responsive while waiting for slow operations - like fetching data from a server or reading a file - to complete in the background.

JavaScript itself does not do multiple things at once. Asynchronous behavior is made possible by the browser (or Node.js) running certain tasks - like timers or network requests - outside of JavaScript, and notifying JavaScript when they are done.

Synchronous vs Asynchronous

javascript

// Synchronous - runs line by line, in order
console.log("1");
console.log("2");
console.log("3");
// Output: 1, 2, 3

// Asynchronous - setTimeout is set aside; other code keeps running
console.log("1");
setTimeout(() => {
  console.log("2");  // runs after 2 seconds
}, 2000);
console.log("3");
// Output: 1, 3, 2

How JavaScript handles asynchronous code

The Event Loop is the mechanism that coordinates asynchronous tasks. When an async operation finishes, it is placed in a callback queue. The event loop checks the queue and runs the callback when the call stack is empty.

Common async patterns in JavaScript

  • Callbacks - a function passed as an argument to be called when the async task finishes
  • Promises - an object representing a value that will be available in the future
  • async/await - syntax that makes asynchronous code look and read like synchronous code

javascript

// Async/await example
async function loadUser() {
  const response = await fetch("/api/user"); // waits here without blocking
  const data = await response.json();
  console.log(data.name);
}
Key Takeaway

Asynchronous code lets JavaScript start a slow task (like a network request), move on to other work, and handle the result when it arrives - keeping the page responsive.

To learn more, see the Promises tutorial, the Async/Await tutorial, and the Event Loop tutorial.

The Temporal Dead Zone (TDZ) is the period between entering a scope and the line where a let or const variable is declared. During this period, the variable exists in memory (it has been hoisted) but has not been given a value yet. Trying to access it throws a ReferenceError.

The TDZ is what makes let and const safer than var. Instead of silently returning undefined, JavaScript tells you clearly that the variable is not ready yet.

The TDZ starts at the top of the scope, not the top of the file. Each block ({}) creates its own scope and its own TDZ for variables declared with let or const.

TDZ in action

javascript

// TDZ starts here for 'endpoint'
console.log(endpoint); // ReferenceError - still in the TDZ
// TDZ ends here
let endpoint = "/api/users";
console.log(endpoint); // "/api/users" - now safe to use

Comparing var, let, and const

javascript

console.log(a); // undefined  - var is hoisted and initialized to undefined
// console.log(b); // ReferenceError - let is hoisted but in TDZ
// console.log(c); // ReferenceError - const is hoisted but in TDZ

var a = 1;
let b = 2;
const c = 3;

TDZ in a block scope

javascript

let x = "outer";

{
  // TDZ for inner 'x' starts here - shadows the outer x
  // console.log(x); // ReferenceError, not "outer"
  let x = "inner"; // TDZ ends here
  console.log(x);  // "inner"
}
Key Takeaway

The Temporal Dead Zone is JavaScript's way of catching mistakes early. If you try to use a let or const variable before declaring it, you get an error immediately - which is much easier to debug than silently getting undefined.

To learn more, see the Variables and Identifiers tutorial, the Scopes tutorial, and the Functions Advanced tutorial.

A shallow copy creates a new object or array and copies all the top-level values into it. However, if any of those values are themselves objects or arrays (called nested values), the copy only copies the reference to that nested value - not the nested value itself. This means the original and the copy share the same nested objects.

Primitive values (string, number, boolean) are always copied by value, so changing them on the copy will not affect the original. Only nested objects and arrays are shared.

Shallow copy with spread operator

javascript

const original = { theme: "light", lang: "en" };
const copy = { ...original };

copy.theme = "dark";

console.log(original.theme); // "light" - not affected (primitive value)
console.log(copy.theme);     // "dark"

This works fine because theme and lang are primitive strings. But watch what happens with a nested object:

javascript

const original = { theme: "light", notifications: { email: true } };
const copy = { ...original };

copy.notifications.email = false; // modifying nested object

console.log(original.notifications.email); // false - also changed!
// Both copy and original point to the same notifications object

Common ways to make a shallow copy

  • { ...obj } - spread operator for objects
  • [...arr] - spread operator for arrays
  • Object.assign({}, obj) - assigns own enumerable properties
  • arr.slice() - creates a new array with the same elements
Use a shallow copy when your object only contains primitive values (strings, numbers, booleans) at the top level. If it contains nested objects, use a deep copy instead.

To learn more, see the Spread Operator tutorial.

A deep copy creates a completely independent duplicate of an object or array, including all nested objects and arrays. Changing any value in the deep copy - no matter how deeply nested - will not affect the original.

This is the key difference from a shallow copy: a shallow copy shares nested references, while a deep copy duplicates everything at every level.

Deep copy with structuredClone

The modern way to deep copy in JavaScript is structuredClone(), available in all modern browsers and Node.js 17+.

javascript

const original = { theme: "light", notifications: { email: true } };
const deepCopy = structuredClone(original);

deepCopy.notifications.email = false;

console.log(original.notifications.email); // true - not affected
console.log(deepCopy.notifications.email); // false

Deep copy with JSON

An older approach is using JSON.parse(JSON.stringify(obj)). It works for plain data objects but has limitations - it drops undefined values, functions, and special types like Date or Map.

javascript

const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.b.c = 99;
console.log(original.b.c); // 2 - unchanged
Shallow Copy Deep Copy
Top-level values Copied Copied
Nested objects Shared (same reference) Duplicated (independent)
Mutating nested values Affects original Does not affect original
Common methods { ...obj }, Object.assign() structuredClone(), JSON.parse/stringify
Key Takeaway

Use a shallow copy when your data is flat (no nested objects). Use a deep copy when your data has nested objects and you need a fully independent duplicate. Prefer structuredClone() for deep copies in modern code.

To learn more, see the Spread Operator tutorial and the Objects tutorial.