JavaScript fundamentals every serious developer should understand

February 2026

JavaScript appears simple on the surface. Variables, functions, and objects behave predictably in small examples. As applications grow, subtle runtime behaviors begin to influence debugging, performance, and architectural decisions.

This article focuses on the concepts that define how JavaScript actually works at runtime.

Execution Context and Hoisting

Before any line of JavaScript runs, the engine creates an execution context. During this creation phase, variables and functions are registered in memory.

Function declarations are stored fully. Variables declared with var are initialized with undefined. Variables declared with let and const are created but remain uninitialized until execution reaches them. This uninitialized state is known as the Temporal Dead Zone.

console.log(a); // undefined
var a = 10;

console.log(b); // ReferenceError
let b = 20;

Understanding this two-phase process explains why certain variables can be accessed before assignment while others cannot.

null vs undefined

Undefined typically means a value has not been assigned. Null represents an intentional absence of value. These states are not interchangeable.

const user = {};
console.log(user.name); // undefined

const user2 = { name: null };
console.log(user2.name); // null

In API design and data modeling, distinguishing between missing and intentionally empty values prevents ambiguity.

The meaning of this

The value of this depends on how a function is invoked. It is determined at call time, not definition time, except in arrow functions which inherit it from their surrounding scope.

const obj = {
  name: "Alex",
  getName() {
    return this.name;
  }
};

obj.getName(); // "Alex"

Closures and Lexical Scope

A closure occurs when a function retains access to variables from its lexical scope even after the outer function has completed execution.

function createCounter() {
  let count = 0;

  return function () {
    count++;
    return count;
  };
}

Closures enable private state, factory functions, memoization, and many common performance patterns.

Prototypes and Classes

JavaScript uses prototype-based inheritance. Classes provide a cleaner syntax but rely on the same underlying prototype chain.

class User {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return "Hello " + this.name;
  }
}

When a property is accessed, JavaScript searches the object first, then its prototype, and continues up the chain until it reaches null.

Modules and Isolation

Modules introduce file-level scope and explicit dependencies. Modern JavaScript applications rely heavily on ES Modules for predictable architecture and better tree-shaking.

export function greet() {}
import { greet } from "./greet.js";

Mastering these fundamentals provides a deeper understanding of how JavaScript behaves in real systems. These concepts are not theoretical. They influence everyday engineering decisions.