Skip to content
JavaScript Functions & Scope Explained — Complete Step-by-Step Guide

JavaScript Functions & Scope Explained — Complete Step-by-Step Guide

DodaTech Updated Jun 6, 2026 9 min read

JavaScript functions are reusable blocks of code that take inputs, perform actions, and return outputs — like a recipe that you can use again and again with different ingredients.

What You’ll Learn

  • Function declarations vs expressions vs arrow functions
  • Parameters, default values, and rest parameters
  • How scope works (global, function, block, module)
  • Hoisting — why some functions work before their declaration
  • Closures — functions that remember their birthplace
  • IIFE, call, apply, and bind

Why Functions Matter

Functions are the building blocks of any non-trivial JavaScript application. They let you write code once and reuse it everywhere. The Doda Browser uses functions to handle user interactions, process URLs, and manage extensions. The Durga Antivirus Pro dashboard uses functions to scan files, validate license keys, and update threat definitions. Without functions, you’d copy-paste the same code everywhere — a maintenance nightmare.

Learning Path

    flowchart TD
  A[JavaScript Basics] --> B[Control Flow]
  B --> C[Functions & Scope]
  C --> D[Arrays & Objects]
  C --> E[DOM & Events]
  C --> F[Async JS]
  C --> G[You Are Here]
  
Prerequisites: You should understand JavaScript — variables, data types, control flow, and JavaScript.

What Is a Function?

A function is a recipe. A recipe tells you: “Take these ingredients (parameters), do these steps (body), and this is what you get (return value).”

// Recipe for making a greeting
function greet(name) {
  return `Hello, ${name}!`;
}

// Follow the recipe
const message = greet('Alice');
console.log(message);  // 'Hello, Alice!'

Line by line:

  • function greet(name) — Declare a function named greet that takes one parameter: name
  • return \Hello, ${name}!`— The function body.return` sends the result back to where the function was called
  • greet('Alice') — Call the function with the argument 'Alice'. Think of this as “follow the recipe using ‘Alice’ as the ingredient”

Three Ways to Write Functions

// 1. Function Declaration — hoisted (can be called before definition)
function greet(name) {
  return `Hello, ${name}!`;
}

// 2. Function Expression — NOT hoisted
const greet = function(name) {
  return `Hello, ${name}!`;
};

// 3. Arrow Function (ES6) — concise, no own `this`
const greet = (name) => `Hello, ${name}!`;

Which to use? Arrow functions are preferred for callbacks (like array.map()). Use declarations for named functions that will be reused. Use expressions when you need to assign a function to a variable conditionally.

Arrow Functions

Arrow functions are a shorter syntax introduced in ES6:

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

// Multiple parameters — parentheses required
const add = (a, b) => a + b;

// No parameters — empty parentheses required
const hello = () => 'Hello!';

// Block body — needs explicit return
const sum = (a, b) => {
  const result = a + b;
  return result;
};

Key difference: Arrow functions do NOT have their own this. They inherit this from the surrounding scope. This makes them ideal for callbacks but unsuitable for object methods.

Parameters & Default Values

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

console.log(greet());               // 'Hello, Guest!'
console.log(greet('Alice'));        // 'Hello, Alice!'
console.log(greet('Bob', 'Hi'));    // 'Hi, Bob!'

Default values kick in when the argument is undefined:

greet('Alice', undefined);  // 'Hello, Alice!' — default used
greet('Alice', null);       // 'null, Alice!' — null is passed explicitly

Rest Parameters

Collect remaining arguments into an array:

function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sum(1, 2, 3, 4));  // 10

The ... rest parameter captures all extra arguments as a real array (unlike the old arguments object).

Scope: Where Variables Live

Scope determines which variables your code can access at any point:

const global = 'I am global';

function outer() {
  const outerVar = 'I am in outer';

  function inner() {
    const innerVar = 'I am in inner';
    console.log(global);    // Accessible — global scope
    console.log(outerVar);  // Accessible — outer function scope
  }

  console.log(innerVar);    // ReferenceError! Not accessible here
}

console.log(outerVar);      // ReferenceError! Not accessible here

Analogy: Think of scope like rooms in a house. The living room (global scope) is accessible from everywhere. A bedroom (function scope) is only accessible from inside that room. An inner bathroom (inner function) can access the bedroom, but not vice versa.

ScopeVisibilityDeclared With
GlobalEverywhereOutside any function/block
FunctionInside the function onlyvar, let, const
Block ({})Inside the block onlylet, const
ModuleInside the module onlyimport/export

Hoisting

Hoisting is JavaScript’s behavior of moving declarations to the top of their scope before execution:

// Function declarations are fully hoisted
sayHi();  // 'Hi!' — works before declaration
function sayHi() { console.log('Hi!'); }

// var is hoisted (as undefined)
console.log(x);  // undefined (no error!)
var x = 5;

// let and const are hoisted but in the Temporal Dead Zone
console.log(y);  // ReferenceError!
let y = 5;

Closures

A closure is a function that “remembers” the variables from its outer scope even after the outer function has finished running:

function createCounter() {
  let count = 0;
  
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter());  // 1
console.log(counter());  // 2
console.log(counter());  // 3

How this works: createCounter() runs and creates a local variable count = 0. It returns an inner function that increments count and returns it. Even after createCounter() has finished, the returned function still “remembers” the count variable.

Analogy: A closure is like a backpack. When you leave home (the outer function finishes), you pack a few items (the outer variables) and carry them with you. The inner function carries its “scope backpack” wherever it goes.

IIFE (Immediately Invoked Function Expression)

A function that runs immediately after being defined:

(function() {
  const private = 'This is scoped to the IIFE';
  console.log(private);
})();
// 'This is scoped to the IIFE'

Why use IIFE? Before ES6 modules, IIFEs were the only way to create private scope. Today, use modules or block-scoped declarations instead.

Call, Apply, Bind

These methods control what this refers to inside a function:

const person = { name: 'Alice' };

function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

// call — arguments listed individually
greet.call(person, 'Hello', '!');   // 'Hello, Alice!'

// apply — arguments as an array
greet.apply(person, ['Hi', '!']);   // 'Hi, Alice!'

// bind — returns a new function with this permanently bound
const boundGreet = greet.bind(person);
boundGreet('Hey', '!');              // 'Hey, Alice!'

Common Mistakes

1. Forgetting return in arrow functions with {}

const double = (x) => { x * 2 };  // Returns undefined!

Without {}, the expression is implicitly returned. With {}, you need return.

2. Using arrow functions as object methods

const obj = {
  name: 'Alice',
  greet: () => { console.log(this.name); }  // undefined
};

3. Closure bug with var in loops

for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100);  // Prints 5 five times!
}

Fix: Use let instead of var, which creates a new binding for each iteration.

4. Not handling default parameter edge cases

function greet(name = 'Guest') { }
greet('');        // name = '' (empty string is passed)
greet(null);      // name = null (null is passed)
greet(undefined); // name = 'Guest' (default triggers)

5. Confusing declaration hoisting with expression hoisting

foo();  // Works — function declarations are hoisted
function foo() {}

bar();  // TypeError — function expressions are NOT hoisted
const bar = function() {};

Practice Questions

  1. What’s the difference between a function declaration and a function expression? Declarations are hoisted (usable before definition). Expressions are not.

  2. What is a closure? A function that retains access to its outer scope’s variables even after the outer function has returned.

  3. What does this refer to in arrow functions? Arrow functions inherit this from their surrounding lexical scope. They don’t have their own this.

  4. What’s the difference between call, apply, and bind? call invokes with arguments listed. apply invokes with arguments as an array. bind returns a new function with this bound.

Challenge: Create a multiplyBy function that takes a number n and returns a function that multiplies its argument by n. Use it to create double = multiplyBy(2) and triple = multiplyBy(3).

FAQ

What is the difference between a function declaration and expression?
Declarations are hoisted (usable before definition). Expressions are not. Declarations must have a name. Expressions can be anonymous.
What is a closure in JavaScript?
A function that retains access to its outer scope’s variables even after the outer function has returned. Used for data privacy and factory functions.
What does this refer to in arrow functions?
Arrow functions inherit this from their surrounding (lexical) scope. They don’t have their own this binding.
What is the difference between call, apply, and bind?
call invokes with arguments listed individually. apply invokes with arguments as an array. bind returns a new function with this bound.
What is hoisting?
JavaScript’s behavior of moving declarations to the top of their scope. Function declarations are fully hoisted. var is hoisted as undefined. let/const are hoisted but in the Temporal Dead Zone.
What is an IIFE?
A function that runs immediately after being defined. Used historically for module patterns to create private scope.

Try It Yourself

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Functions Playground</title>
  <style>
    body { font-family: Arial, sans-serif; padding: 2rem; }
    pre { background: #f4f4f4; padding: 1rem; border-radius: 5px; }
  </style>
</head>
<body>
  <h1>Functions Sandbox</h1>
  <pre id="output"></pre>
  <script>
    const out = document.getElementById('output');
    let result = '';

    // Basic function
    function greet(name) {
      return `Hello, ${name}!`;
    }
    result += `${greet('Alice')}\n`;

    // Arrow function
    const double = x => x * 2;
    result += `Double 5: ${double(5)}\n`;

    // Closure
    function createCounter() {
      let count = 0;
      return () => ++count;
    }
    const counter = createCounter();
    result += `Counter: ${counter()}, ${counter()}, ${counter()}\n`;

    // Default parameters
    function power(base, exp = 2) {
      return base ** exp;
    }
    result += `3^2 = ${power(3)}, 3^3 = ${power(3, 3)}\n`;

    out.textContent = result;
  </script>
</body>
</html>

What’s Next

Now that you understand functions, apply them to real DOM manipulation:

LessonDescription
JavaScript HomeBack to the JavaScript hub
https://tutorials.dodatech.com/programming-languages/javascript/js-dom/DOM & Browser APIs — manipulate web pages
https://tutorials.dodatech.com/programming-languages/javascript/js-async/Async JavaScript — callbacks, promises, fetch
https://tutorials.dodatech.com/programming-languages/javascript/js-modules/Modules & Error Handling — organize code
Node.js FunctionsServer-side JavaScript with Node.js

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

What’s Next

Congratulations on completing this Js Functions tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro