Skip to main content

Functions & Scope

Functions are first-class citizens in JavaScript. They can be assigned to variables, passed as arguments, and returned from other functions. This makes JavaScript incredibly flexible and powerful.

1. Function Declarations vs Expressions

Function Declaration (Hoisted)

// Can be called before it's defined (hoisting)
greet('Alice'); // Works!

function greet(name) {
    return `Hello, ${name}!`;
}

Function Expression (Not Hoisted)

// Cannot be called before assignment
greet('Alice'); // ❌ ReferenceError

const greet = function(name) {
    return `Hello, ${name}!`;
};

Arrow Functions (ES6)

Concise syntax, and they don’t have their own this.
// Basic syntax
const add = (a, b) => a + b;

// With body block
const greet = (name) => {
    const message = `Hello, ${name}!`;
    return message;
};

// Single parameter (parentheses optional)
const double = x => x * 2;

// No parameters
const sayHi = () => 'Hi!';
When to use arrow functions? Use them for short, anonymous functions (callbacks, map/filter). Use regular functions when you need this binding or hoisting.

2. Parameters & Arguments

Default Parameters (ES6)

function greet(name = 'Guest') {
    return `Hello, ${name}!`;
}

greet();        // 'Hello, Guest!'
greet('Alice'); // 'Hello, Alice!'

Rest Parameters

Collect remaining arguments into an array.
function sum(...numbers) {
    return numbers.reduce((total, n) => total + n, 0);
}

sum(1, 2, 3, 4); // 10

Spread Operator

Expand an array into individual arguments.
const nums = [1, 2, 3];
console.log(Math.max(...nums)); // 3

3. Scope

Scope determines where variables are accessible. JavaScript has three types of scope.

Global Scope

Variables declared outside any function or block.
const globalVar = 'I am global';

function test() {
    console.log(globalVar); // Accessible
}

Function Scope

Variables declared inside a function (with var, let, or const).
function test() {
    var functionScoped = 'Only here';
}
console.log(functionScoped); // ❌ ReferenceError

Block Scope (ES6)

Variables declared with let or const inside {}.
if (true) {
    let blockScoped = 'Only in this block';
    const alsoBlockScoped = 'Same here';
}
console.log(blockScoped); // ❌ ReferenceError

Lexical Scope (Static Scope)

Functions are executed using the scope in which they were defined, not where they are called.
const name = 'Alice';

function outer() {
    const name = 'Bob';
    
    function inner() {
        console.log(name); // 'Bob' — looks up the scope chain
    }
    
    return inner;
}

const fn = outer();
fn(); // 'Bob' — remembers its lexical environment

4. Closures

A closure is a function that remembers its lexical scope even when executed outside that scope. This is one of the most powerful concepts in JavaScript.
function createCounter() {
    let count = 0; // Private variable
    
    return {
        increment: () => ++count,
        decrement: () => --count,
        getCount: () => count
    };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount();  // 2

// count is not accessible directly
console.log(counter.count); // undefined

Practical Use Cases

1. Data Privacy (Module Pattern)
const bankAccount = (function() {
    let balance = 0; // Private
    
    return {
        deposit: (amount) => balance += amount,
        withdraw: (amount) => balance -= amount,
        getBalance: () => balance
    };
})();

bankAccount.deposit(100);
bankAccount.getBalance(); // 100
2. Function Factories
function multiply(factor) {
    return (number) => number * factor;
}

const double = multiply(2);
const triple = multiply(3);

double(5); // 10
triple(5); // 15
3. Event Handlers with State
function createClickHandler(buttonId) {
    let clickCount = 0;
    
    return function() {
        clickCount++;
        console.log(`Button ${buttonId} clicked ${clickCount} times`);
    };
}

const handler = createClickHandler('submit');
// Each click remembers the count

5. The Execution Context & Call Stack

Every time a function is called, JavaScript creates an Execution Context. These contexts are managed in a Call Stack (LIFO).
function first() {
    console.log('First');
    second();
    console.log('First again');
}

function second() {
    console.log('Second');
    third();
}

function third() {
    console.log('Third');
}

first();
Call Stack:
1. [Global]
2. [Global] → [first()]
3. [Global] → [first()] → [second()]
4. [Global] → [first()] → [second()] → [third()]
5. [Global] → [first()] → [second()] ← third() returns
6. [Global] → [first()] ← second() returns
7. [Global] ← first() returns
Stack Overflow: If you have infinite recursion (a function calling itself without a base case), the call stack overflows and throws an error.

6. Higher-Order Functions

A higher-order function either takes a function as an argument or returns a function. They are central to functional programming.

Functions as Arguments

const numbers = [1, 2, 3, 4, 5];

// map: Transform each element
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]

// filter: Keep elements that pass a test
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]

// reduce: Accumulate to a single value
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15

// find: Get first matching element
const found = numbers.find(n => n > 3); // 4

// some/every: Boolean tests
numbers.some(n => n > 4);  // true (at least one)
numbers.every(n => n > 0); // true (all)

Chaining

const result = [1, 2, 3, 4, 5]
    .filter(n => n % 2 === 0)  // [2, 4]
    .map(n => n ** 2)          // [4, 16]
    .reduce((a, b) => a + b);  // 20

7. IIFE (Immediately Invoked Function Expression)

A function that runs immediately after it’s defined. Used to create a private scope.
(function() {
    const secret = 'hidden';
    console.log('IIFE executed');
})();

// Arrow function version
(() => {
    console.log('Arrow IIFE');
})();

Summary

  • Function Types: Declarations are hoisted, expressions are not. Arrow functions are concise.
  • Scope: Global → Function → Block. JavaScript uses lexical (static) scoping.
  • Closures: Functions remember their lexical environment. Use for data privacy.
  • Higher-Order Functions: map, filter, reduce are your bread and butter.
  • Call Stack: LIFO structure for managing function execution.
Next, we’ll explore Objects & Prototypes, the foundation of JavaScript’s object model.