Search lands in PR-5.1 (Pagefind).

Explanation Beginner

Chapter 4 Updated

Functions & Variable Environments

How each function creates its own isolated world of variables — and why same-named names never collide.

  • Full 14m
  • Revision 3m
  • Flow 2m

Core Concepts

Every Function Invocation = New Execution Context

This is the single most important idea in this topic. When a function is invoked (called), JavaScript creates a brand new execution context for it. This context has:

  • Its own Variable Environment (memory space) — completely independent
  • Its own Code Execution phase — runs the function body line by line
  • Its own copy of local variables — even if they share names with global variables

When the function finishes (returns), its entire execution context is destroyed. The local variables cease to exist.

Complete Walkthrough — The Master Example

JavaScript
var x = 1;
a();
b();
console.log(x);
 
function a() {
  var x = 10;
  console.log(x);
}
 
function b() {
  var x = 100;
  console.log(x);
}

Step-by-step Execution:

  1. GEC Memory Phase: x → undefined, a → {fn}, b → {fn}. GEC is pushed onto Call Stack.
  2. GEC Execution: x = 1 (replaces undefined with 1).
  3. a() is called: A new execution context for a is created and pushed onto the stack. In a’s memory phase: x → undefined. In a’s execution: x = 10, then console.log(x) prints 10 (the local x). Context for a is destroyed and popped.
  4. b() is called: New execution context for b. In b’s memory: x → undefined. In b’s execution: x = 100, then console.log(x) prints 100. Context for b is destroyed and popped.
  5. Back in GEC: console.log(x) prints 1 — the global x was never touched by a() or b().
  6. GEC is destroyed and popped. Call Stack is empty. Program done.

Final Output: 10 → 100 → 1

Call Stack During the Example

Here’s how the Call Stack evolves:

[GEC][GEC, a()][GEC][GEC, b()][GEC]Empty ✓

Variable Environment — The Technical Term

The Variable Environment is the memory component of an execution context. It’s where all local variables, function declarations, and function parameters are stored. Each execution context has its own independent variable environment.

When people say “a function has its own scope,” they’re really saying it has its own variable environment inside its own execution context.

What Happens to Variables When a Function Returns?

When a function finishes execution:

  • Its execution context is completely destroyed
  • All local variables in its variable environment are garbage collected
  • The memory is freed up
  • The return value (if any) is passed back to the calling context

This is why local variables are not accessible outside their function — they literally don’t exist anymore after the function returns. (The exception to this is closures, covered in Topic 10.)

Parameters Are Local Variables Too

Function parameters are treated as local variables within the function’s execution context. They are allocated in the function’s variable environment during its memory creation phase.

Example
function greet(name, greeting) {
  // name and greeting are local variables in greet's variable environment
  console.log(greeting + ", " + name);
}
greet("Akshay", "Hello");
console.log(name); // ReferenceError: name is not defined

With vs Without Local Declaration — Critical Difference

WITH local var — Global is safe
var x = 1;
function change() {
  var x = 999;  // local x — doesn't affect global
}
change();
console.log(x); // 1 ✓ global is untouched
WITHOUT local var — Global gets modified!
var x = 1;
function change() {
  x = 999;  // NO var/let/const — modifies global x!
}
change();
console.log(x); // 999 — global was changed!

Interview Questions & Answers

Q1. What is a Variable Environment in JavaScript?

The Variable Environment is the memory component of an execution context. It stores all the variables, function declarations, and function parameters as key-value pairs within that specific execution context. Each function invocation creates a new execution context with its own independent variable environment, ensuring variables in one function don’t interfere with variables in another — even if they share the same name.

Q2. Can two different functions have variables with the same name? Will they conflict?

No, they will not conflict. Each function invocation creates its own execution context with its own variable environment (memory space). A variable named x inside function a() and another x inside function b() are completely independent variables stored in separate memory locations. They have no knowledge of each other and don’t interact in any way. When each function returns, its execution context is destroyed along with its local variables.

Q3. What happens to local variables when a function finishes execution?

When a function finishes execution (either by reaching the end or hitting a return statement), its entire execution context is destroyed and popped off the Call Stack. All local variables in its variable environment are deallocated and become eligible for garbage collection. They are no longer accessible. The only thing that survives is the return value, which is sent back to the calling context. The key exception is closures, where inner functions retain references to outer function variables even after the outer function returns.

Q4. What is the difference between local scope and global scope?

Global scope: Variables declared outside any function, in the Global Execution Context. They are accessible from anywhere in the program and live for the entire duration of the program. With var, they are also properties of the window object.

Local scope (Function scope): Variables declared inside a function, within that function’s execution context. They are only accessible within that function and are destroyed when the function finishes. They exist only for the lifetime of that function call.

Q5. What happens if you assign to a variable without declaring it with var/let/const?

In non-strict mode, if you assign a value to a variable without declaring it (e.g., x = 10 without var/let/const), JavaScript creates it as a property on the global object (window.x in browsers). This is called an “implicit global” and is considered a bug-prone behavior. In strict mode ("use strict"), this throws a ReferenceError: x is not defined, which is the safer behavior.

Q6. Are function parameters part of the variable environment?

Yes. Function parameters are treated as local variables within the function’s execution context. During the memory creation phase of the function’s execution context, parameters are allocated memory and initialized with the argument values passed during the call. They exist in the function’s variable environment and are destroyed when the function returns, just like any other local variable.

Q7. If a function is called multiple times, does it use the same execution context?

No. Every single function call creates a new, fresh execution context with its own variable environment. If you call square(2) and then square(4), two separate execution contexts are created (and destroyed) one after the other. They share no state. This is why each call can have different parameter values and produce different results independently.

Q8. Explain the concept of “scope isolation” in JavaScript functions.

Scope isolation means each function creates its own enclosed environment where its variables are private and inaccessible from outside. This is achieved through the execution context model — each function call gets its own variable environment. Variables declared inside a function cannot be read or modified from outside. The global execution context cannot access local variables of a function, though a function CAN access global variables (via the scope chain). This one-way access pattern is fundamental to JavaScript’s scoping.

Q9. What is the arguments object in a function?

The arguments object is an array-like object available inside every non-arrow function. It contains all the arguments passed to the function, regardless of how many parameters were defined. It’s part of the function’s variable environment. Key points:

  • It has a length property and can be accessed by index (arguments[0], etc.)
  • It is NOT a real array — it doesn’t have methods like map, forEach etc.
  • Arrow functions do NOT have their own arguments object — they inherit from the enclosing function.
Example
function sum() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}
console.log(sum(1, 2, 3, 4)); // 10

Q10. What is the difference between pass by value and pass by reference in JavaScript?

Primitives (number, string, boolean, null, undefined, symbol, bigint) are passed by value. The function receives a copy. Modifying the parameter inside the function does NOT affect the original variable.

Objects (arrays, objects, functions) are passed by reference (technically, a copy of the reference). The function receives a reference to the same object in memory. Modifying properties of the object inside the function DOES affect the original. However, reassigning the parameter to a new object does NOT affect the original reference.

Example
let num = 10;
let obj = { a: 1 };
 
function modify(x, o) {
  x = 99;         // won't affect num (copy of value)
  o.a = 99;        // WILL affect obj (same reference)
  o = { a: 0 };    // won't affect obj (reassigns local ref)
}
modify(num, obj);
console.log(num);   // 10 (unchanged)
console.log(obj.a); // 99 (property was modified)

Input/Output Interview Questions

I/O 1. What will be the output?

JavaScript
var x = 1;
a();
b();
console.log(x);
 
function a() {
  var x = 10;
  console.log(x);
}
 
function b() {
  var x = 100;
  console.log(x);
}

Output: 10 → 100 → 1

Three separate x variables in three separate execution contexts. Each function logs its own local x. The global x = 1 is never modified.

I/O 2. What will be the output? (No var inside function)

JavaScript
var x = 1;
 
function a() {
  x = 10;  // NO var — modifies global x
  console.log(x);
}
 
a();
console.log(x);

Output: 10 → 10

Without var/let/const, x = 10 modifies the global x. Both logs print 10.

I/O 3. What will be the output?

JavaScript
var x = 10;
 
function a() {
  console.log(x);
}
 
function b() {
  var x = 20;
  a();
}
 
b();

Output: 10

Very important! Even though a() is called from inside b(), function a is lexically defined in the global scope. So a’s scope chain goes to the global scope, not to b’s scope. It finds the global x = 10, not b’s x = 20. JavaScript uses lexical (static) scoping, not dynamic scoping.

I/O 4. What will be the output?

JavaScript
var count = 0;
 
function increment() {
  count++;
  console.log(count);
}
 
increment();
increment();
increment();

Output: 1 → 2 → 3

No local count is declared inside increment(), so it accesses and modifies the global count. Each call creates a new execution context, but all three access the same global variable. The state persists between calls because it lives in the global scope.

I/O 5. What will be the output?

JavaScript
function a() {
  var x = 10;
  console.log(x);
}
 
function b() {
  console.log(x);
}
 
a();
b();

Output: 10 → ReferenceError: x is not defined

a() logs its local x = 10 fine. b() tries to access x but has no local x and there’s no global x either. a’s local x was destroyed when a() finished. b cannot access a’s variables — they are in separate execution contexts.

I/O 6. What will be the output? (Pass by value vs reference)

JavaScript
var a = 5;
var obj = { val: 5 };
 
function change(num, o) {
  num = num * 10;
  o.val = o.val * 10;
}
 
change(a, obj);
console.log(a);       // ?
console.log(obj.val); // ?

Output: 5 → 50

a is a primitive — passed by value. Modifying num doesn’t affect a. obj is an object — passed by reference. Modifying o.val changes the actual object’s property.

I/O 7. What will be the output? (Tricky — hoisting + variable environment)

JavaScript
var x = 21;
 
var fun = function() {
  console.log(x);
  var x = 20;
  console.log(x);
};
 
fun();

Output: undefined → 20

Inside fun(), var x = 20 causes x to be hoisted within the function’s scope as undefined. The first console.log(x) finds this local x (which is undefined), NOT the global x = 21. The local declaration shadows the global one entirely. After x = 20 runs, the second log prints 20.

I/O 8. What will be the output? (Nested function calls)

JavaScript
var x = 1;
 
function outer() {
  var x = 2;
 
  function inner() {
    var x = 3;
    console.log("inner:", x);
  }
 
  inner();
  console.log("outer:", x);
}
 
outer();
console.log("global:", x);

Output: "inner: 3" → "outer: 2" → "global: 1"

Three completely separate x variables: global (1), outer (2), inner (3). Each function logs its own local x. None affect each other.

I/O 9. What will be the output? (Strict mode)

JavaScript
"use strict";
 
function test() {
  y = 20;
}
 
test();
console.log(y);

Error: ReferenceError: y is not defined

In strict mode, assigning to an undeclared variable is not allowed. Without "use strict", this would silently create a global variable y = 20. Strict mode prevents this dangerous behavior.

I/O 10. What will be the output? (Tricky — same function called twice)

JavaScript
function counter() {
  var count = 0;
  count++;
  console.log(count);
}
 
counter();
counter();
counter();

Output: 1 → 1 → 1

Each call to counter() creates a new execution context with a fresh count = 0. The variable doesn’t persist between calls because it’s local and gets destroyed after each return. This is why closures are needed to maintain state (Topic 10).

I/O 11. What will be the output? (Advanced — arguments object)

JavaScript
function test(a, b, c) {
  console.log(arguments.length);
  console.log(a, b, c);
}
 
test(1, 2);

Output: 2 → 1 2 undefined

arguments.length is 2 (number of arguments passed, not parameters declared). c was not passed any argument, so it defaults to undefined in the function’s variable environment.

I/O 12. What will be the output? (Tricky — reassigning parameter)

JavaScript
var arr = [1, 2, 3];
 
function addItem(list) {
  list.push(4);      // modifies original array
  list = [99];       // reassigns local reference only
  list.push(100);    // pushes to the NEW local array
}
 
addItem(arr);
console.log(arr);

Output: [1, 2, 3, 4]

list.push(4) modifies the original arr because list points to the same array. But list = [99] reassigns the local parameter to a new array — it doesn’t change what arr points to. list.push(100) only affects the local [99] array, which is destroyed when the function returns.

Quick Revision — Cheat Sheet

Remember These Points

  • Every function call → new execution context → new variable environment
  • Same-named variables in different functions → completely independent
  • Local variables are destroyed when the function returns
  • var x inside function → creates local x, does NOT touch global x
  • No var/let/const → modifies global (dangerous!) or throws error in strict mode
  • Parameters = local variables allocated in function’s variable environment
  • Calling same function N times = N separate execution contexts (no shared state)
  • Primitives → passed by value (copy), Objects → passed by reference
  • Modifying object properties inside function → affects original
  • Reassigning object parameter → only changes local reference
  • arguments object available in regular functions (not arrow functions)
  • Lexical scoping: function’s scope chain is based on where it’s defined, not where it’s called
  • Local var shadows global even before assignment line (hoisting within function)
  • "use strict" prevents accidental global variable creation

Comments

Comments are disabled in this environment. Set PUBLIC_GISCUS_REPO, PUBLIC_GISCUS_REPO_ID, and PUBLIC_GISCUS_CATEGORY_ID to enable.