Chapter 4.6☕ 18 min read

Strict Mode: Every Restriction & V8 Performance Wins

'use strict' — V8 ko bol do ki ab registers mein rakho parameters. Speed milti hai.

0101 — Strict Mode Enable Karna — File, Function, Auto

'use strict' — three words. Three words that put V8 into a completely different execution mode. Strict mode isn't just about restrictions — it's a performance contract with the engine. When V8 knows there are no accidental globals, no arguments aliasing, no with statement — it can make optimizations impossible in sloppy mode.

// File-level strict mode
'use strict'; // ← must be first statement
// Everything in this file is now strict

// Function-level strict mode
function strictFunc() {
  'use strict'; // ← only this function is strict
  // ...
}

// Class body — ALWAYS strict (no directive needed)
class MyClass {
  method() {
    // strict mode — always, no directive needed
    console.log(typeof this); // 'object' or 'undefined' (not window)
  }
}

// ES6 module — ALWAYS strict
// import './something'; // this file is auto-strict

// What counts as "first statement":
'use strict'; // ✅ — first statement
// comments above are OK
/* multi-line comment OK */
'use strict'; // ✅ — still first real statement

var x = 1;
'use strict'; // ❌ — NOT first statement — ignored silently!
V8 parsing behavior: V8 checks for the 'use strict' directive during parsing — before any bytecode is generated. If found, the entire parsing pass runs in strict mode, changing which AST nodes are generated. Functions compiled in strict mode get a different set of bytecode instructions for variable access — with performance optimizations baked in from the start.
0202 — Har Restriction — Poori List

Here is every restriction strict mode enforces. Know them all — interviews love this list:

'use strict';

// 1. No accidental globals
function noGlobals() {
  'use strict';
  leaked = 'oops'; // ReferenceError: leaked is not defined
  // In sloppy mode: window.leaked = 'oops' — silent pollution!
}

// 2. No duplicate params
// function dup(a, a) { } // SyntaxError: Duplicate parameter name

// 3. this in plain function call = undefined
function showThis() {
  'use strict';
  console.log(this); // undefined — not window!
}
showThis();

// 4. arguments not aliased
function noAlias(x) {
  'use strict';
  arguments[0] = 99;
  console.log(x); // still the original value — NOT 99!
  // In sloppy: x would change to 99 (aliased to arguments[0])
}
noAlias(1); // x is 1, not 99

// 5. delete restrictions
var obj = {};
Object.defineProperty(obj, 'fixed', { configurable: false, value: 1 });
// delete obj.fixed; // TypeError: Cannot delete property (strict)
var x = 1;
// delete x; // SyntaxError: Cannot delete variable 'x'

// 6. No octal
// var n = 010; // SyntaxError: Octal literals are not allowed
var n = 0o10; // ✅ use 0o prefix for octal
console.log(n); // 8
📋 The most important restriction in practice: Accidental globals. Forgetting var/let/const in sloppy mode creates a property on window — a global that persists for the page lifetime. In strict mode it immediately throws ReferenceError, making the bug impossible to miss. This alone is worth enabling strict mode everywhere.
0303 — V8 Performance — Strict Mode Mein Speed Kyon

Strict mode isn't just about catching bugs — it makes your code genuinely faster. Here are the five V8 performance wins:

Win 1: Parameters in CPU registers — no arguments aliasing means V8 can put params in registers. In sloppy: params and arguments[] must stay in sync on the stack. In strict: arguments[] not aliased — params go to CPU registers (much faster).

Win 2: Function inlining — no arguments.callee means the function can be inlined by TurboFan. arguments.callee prevents inlining because the inlined copy has no self-reference.

Win 3: No with statement — V8 knows all variable locations at compile time. No dynamic scope injection.

Win 4: Faster this check — this === undefined is a fast comparison, not a window object lookup.

Win 5: Fewer deoptimization triggers — strict mode has fewer dynamic scope scenarios that cause V8 to bail out of optimized code.

// Performance difference — measurable in tight loops

// SLOPPY MODE (default) — parameters on stack
function sloppyAdd(a, b) {
  // V8 must maintain: a on stack, arguments[0] = a (aliased!)
  // Cannot put a or b in CPU registers safely
  return a + b;
}

// STRICT MODE — parameters can go to registers
function strictAdd(a, b) {
  'use strict';
  // V8 knows: arguments[0] is NOT aliased to a
  // a and b can live in CPU registers EAX, EBX
  // No stack read needed — registers are fastest possible
  return a + b;
}

// Benchmark:
console.time('sloppy');
let s = 0;
for (let i = 0; i < 10000000; i++) { s += sloppyAdd(i, 1); }
console.timeEnd('sloppy');

console.time('strict');
let t = 0;
for (let i = 0; i < 10000000; i++) { t += strictAdd(i, 1); }
console.timeEnd('strict');
// strict is faster — parameter register allocation
The register optimization: In sloppy mode, the arguments object must mirror the named parameters — a change to arguments[0] must also change the named parameter a, and vice versa. This aliasing forces V8 to allocate both on the stack so they can be kept in sync. Without aliasing (strict mode), parameters become independent bindings that can live in CPU registers — the fastest memory available to the processor.
0404 — arguments.callee — Kyun Bana, Kyun Hataya

arguments.callee was a way for an anonymous function to reference itself — used primarily for anonymous recursion. arguments.caller (non-standard) referenced the calling function. Both are banned in strict mode.

Why banned: arguments.callee prevents function inlining. TurboFan cannot inline a function that references itself via callee — the callee reference must remain valid after inlining, which it wouldn't be. By banning callee, V8 is free to inline aggressively.

// OLD: anonymous recursion via arguments.callee (sloppy only)
var factorial = function(n) {
  if (n <= 1) return 1;
  return n * arguments.callee(n - 1); // calls "current function"
  // In strict mode: TypeError: 'caller', 'callee', and 'arguments'
  // properties may not be accessed on strict mode functions
};
console.log(factorial(5)); // 120 (sloppy mode only)

// FIX: named function expression — works everywhere including strict
var factorial = function fact(n) {
  'use strict';
  if (n <= 1) return 1;
  return n * fact(n - 1); // 'fact' is the name — accessible inside!
};
console.log(factorial(5)); // 120 ✅

// 'fact' is NOT accessible OUTSIDE:
// console.log(fact); // ReferenceError — name is only inside

// Arrow function recursion via outer variable
const fib = (n) => n <= 1 ? n : fib(n-1) + fib(n-2);
// Captures 'fib' from outer scope via closure — works in strict

// Why inlining matters:
// arguments.callee prevents inlining because the inlined copy
// has no "self reference" — the callee reference would be wrong.
// Without callee, V8 can inline the entire function body at the
// call site — eliminating function call overhead in hot loops
0505 — Modern Code Mein — Kab Chahiye, Kab Nahi

Most modern JavaScript contexts are automatically strict — you rarely need the directive yourself:

// Modern code — strict is automatic in most contexts:

// ES6 Class — always strict
class Calculator {
  add(a, b) { return a + b; } // strict, no directive
}

// ES Module — always strict
// math.js:
// export const add = (a, b) => a + b; // strict, auto

// TypeScript — always compiles to strict mode
// (tsconfig.json: "strict": true is additional TS checks on top)

// Node.js CommonJS — NOT automatic, add manually:
// utils.js (CommonJS):
'use strict'; // ← needed here!
const add = (a, b) => a + b;
module.exports = { add };

// Node.js ESM — automatic:
// utils.mjs or package.json "type": "module"
// export const add = (a, b) => a + b; // auto-strict

// The practical checklist:
// Writing a .js file with require/module.exports? Add 'use strict'
// Writing a .js file with import/export? Already strict
// Writing a class? Already strict
// Writing TypeScript? Already strict
// Using a modern bundler (Vite, Webpack)? Usually strict

// ESLint config to enforce:
// "rules": { "strict": ["error", "global"] } for CJS
// "rules": { "strict": ["error", "never"] } for ESM (already strict)
📋 In 2024: If you are writing TypeScript, using ES modules, or writing class-based code, strict mode is already active — you do not need the directive. The directive 'use strict' is mainly needed in legacy CommonJS Node.js scripts and old-style script tag JS files. Check your bundler and TypeScript config — they likely enable it for you already.
🏁 Stage 4 Complete! You have mastered Scope, Closures, IIFE, and Strict Mode. You now understand how variables live in memory, how closures capture their environment, and why strict mode makes V8 faster. Stage 5 — Function Objects in Memory — is next. Functions are objects: [[Call]], [[Scope]], prototypes. Get ready!

Lo kar liya — Stage 4 Complete!

  • ✅ 'use strict' must be the FIRST statement — after any code, it's silently ignored
  • ✅ ES modules and class bodies are automatically strict — no directive needed
  • ✅ Key restrictions: no accidental globals, no duplicate params, no with, this=undefined in plain calls, no arguments aliasing
  • ✅ V8 performance win: strict mode allows parameters in CPU registers because arguments aliasing is eliminated
  • arguments.callee prevents function inlining — banned in strict mode. Use named function expressions instead
  • ✅ Modern code: TypeScript, ES modules, and class bodies are all already strict — 'use strict' only needed in CommonJS Node.js files
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase