Index

Functions and Scope

JavaScript for Beginners

9.1 Declaring Functions (function, arrow functions)

Functions are one of the most fundamental building blocks in JavaScript. They allow you to group reusable code into named blocks that can be called whenever needed. JavaScript supports multiple ways to declare functions, with two of the most common being:

In this section, you'll learn how to use both styles, understand their differences, and see when each is most appropriate.

Traditional Function Declarations

The traditional way to declare a function uses the function keyword.

Syntax:

function functionName(parameters) {
  // code to execute
  return result;
}

Example – Add two numbers:

function add(a, b) {
  return a + b;
}

console.log(add(3, 4)); // 7

Example – Greet a user:

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

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

Arrow Functions

Arrow functions offer a shorter syntax and are commonly used in modern JavaScript, especially for small or anonymous functions.

Syntax:

const functionName = (parameters) => {
  // code to execute
  return result;
};

If the function has only one expression, you can omit the braces and return keyword:

const add = (a, b) => a + b;

Example – Greet a user (arrow version):

const greet = name => `Hello, ${name}!`;

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

Comparing Function Declarations vs. Arrow Functions

Feature Function Declaration Arrow Function
Syntax Verbose Concise
this binding Dynamic (this depends on caller) Lexical (this is inherited from scope)
Hoisting Yes (can be used before defined) No (must be declared first)
Best for Methods, complex logic Callbacks, one-liners

Important: this Behavior

One of the biggest differences between the two styles is how they handle the this keyword.

Function Declaration:

const user = {
  name: 'Alice',
  greet: function () {
    console.log(`Hi, I'm ${this.name}`);
  }
};

user.greet(); // Hi, I'm Alice

Arrow Function (wrong in this case):

const user = {
  name: 'Bob',
  greet: () => {
    console.log(`Hi, I'm ${this.name}`);
  }
};

user.greet(); // Hi, I'm undefined

Arrow functions do not have their own this. They inherit it from the surrounding context, which can lead to unexpected results when used in object methods.

Summary

By mastering both styles, you'll be able to write clean, modern JavaScript code that's both expressive and efficient.

Index

9.2 Parameters and Return Values

Functions in JavaScript behave much like machines: you give them input (parameters), they perform a task, and they may produce output (a return value). Understanding how to pass data into functions and get results back is essential to writing flexible, reusable code.

What Are Parameters?

Parameters are like variables listed in a function’s definition. They represent values that will be passed into the function when it’s called.

Arguments are the actual values you pass when calling the function.

Functions with No Parameters

Sometimes, a function doesn't need any input — it simply performs an action.

Example – Say Hello:

function sayHello() {
  console.log('Hello!');
}

sayHello(); // Output: Hello!

Functions with One Parameter

Example – Greet a user by name:

function greet(name) {
  console.log(`Hello, ${name}!`);
}

greet('Alice'); // Output: Hello, Alice!

Here, name is a parameter, and 'Alice' is the argument passed to it.

Functions with Multiple Parameters

You can define as many parameters as you need, separated by commas.

Example – Add two numbers:

function add(a, b) {
  return a + b;
}

let result = add(5, 3);
console.log(result); // 8

Default Parameter Values

If a caller doesn’t provide an argument, you can define a default value to use instead.

Example – Greet with a default name:

function greet(name = 'Guest') {
  console.log(`Welcome, ${name}!`);
}

greet();          // Welcome, Guest!
greet('Charlie'); // Welcome, Charlie!

This makes your functions more robust and error-tolerant.

Returning Values

A function can return a value using the return keyword. This lets you store or use the result of a computation later in your code.

Example – Calculate area of a rectangle:

function calculateArea(width, height) {
  return width * height;
}

let area = calculateArea(5, 4);
console.log(area); // 20

Without return, the function would perform the calculation but not give the result back to the code that called it.

Real-World Analogy

Think of a function like a coffee machine:

If you press the button without choosing sugar, maybe it adds none (default value). When the brewing is done, you get coffee (the return value), which you can drink (use in your program).

Summary

Concept Example Purpose
No parameters function sayHi() {} Executes fixed code
One parameter function greet(name) {} Accepts a single value
Multiple parameters function add(a, b) {} Accepts two or more values
Default values function greet(name = 'Guest') {} Fallback when no argument passed
Return a value return a + b; Outputs a result from the function

Knowing how to pass data into functions and return results lets you build custom, reusable tools for your code—just like mini-programs inside your program.

Index

9.3 Function Expressions

In JavaScript, functions are first-class citizens, meaning they can be treated like any other value — stored in variables, passed as arguments, or returned from other functions. One way to create functions that leverages this flexibility is through function expressions.

What Is a Function Expression?

A function expression defines a function inside an expression, typically assigning it to a variable.

Syntax:

const sayHello = function() {
  console.log('Hello!');
};

Unlike function declarations (function sayHello() {}), function expressions are not hoisted, which means you cannot call them before they are defined.

Example: Storing Functions in Variables

You can store a function in a variable just like a number or string.

const multiply = function(a, b) {
  return a * b;
};

console.log(multiply(4, 5)); // 20

Passing Functions as Arguments (Callbacks)

Because functions can be stored in variables, they can also be passed as arguments to other functions. This is useful for things like event handling or array processing.

Example – Using a callback with setTimeout:

setTimeout(function() {
  console.log('This message appears after 2 seconds');
}, 2000);

Here, the anonymous function (a function without a name) is passed directly as an argument to setTimeout.

Anonymous Functions

Functions without a name are called anonymous functions. They are often used in function expressions or as callbacks:

const greet = function(name) {
  console.log(`Hello, ${name}!`);
};

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

You can also write anonymous arrow functions:

setTimeout(() => console.log('Done!'), 1000);

Function Declarations vs. Function Expressions

Feature Function Declaration Function Expression
Syntax function foo() {} const foo = function() {}
Hoisting Hoisted (can call before defined) Not hoisted (must define first)
Naming Usually named Can be anonymous or named
Usage General purpose When assigning or passing functions

Summary

Understanding function expressions unlocks much of JavaScript’s flexibility and lets you write more dynamic and functional code.

Index

9.4 Variable Scope and Hoisting

Understanding variable scope and hoisting is essential for writing reliable JavaScript code. These concepts determine where variables are accessible and how they behave during code execution.

What Is Variable Scope?

Scope defines the region of your code where a variable is available. In JavaScript, there are three main types of scope:

Global Scope

Variables declared outside any function or block have global scope and can be accessed anywhere in your code.

let globalVar = 'I am global';

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

showVar();               // Output: I am global
console.log(globalVar);  // Output: I am global

Function Scope

Variables declared inside a function are only accessible within that function.

function myFunc() {
  let funcVar = 'I am local to myFunc';
  console.log(funcVar); // Works here
}

myFunc();
console.log(funcVar); // Error: funcVar is not defined

Variables declared with var and let inside functions are both function-scoped, meaning they exist throughout the function.

Block Scope

Block scope means variables are only accessible inside the nearest pair of {}, such as in if statements, loops, or blocks.

Example:

if (true) {
  let blockVar = 'I exist only in this block';
  var varVar = 'I am function-scoped';
}

console.log(blockVar); // Error: blockVar is not defined
console.log(varVar);   // Output: I am function-scoped

var vs. let vs. const

Keyword Scope Can be Reassigned? Can be Redeclared in Same Scope?
var Function scope Yes Yes
let Block scope Yes No
const Block scope No (constant) No

Example of let and const:

let count = 1;
count = 2; // Allowed

const max = 10;
max = 20; // Error: Assignment to constant variable

What Is Hoisting?

Hoisting is JavaScript’s behavior of moving variable and function declarations to the top of their scope during compilation, before code runs.

This means you can sometimes use variables and functions before they are declared — but the details vary depending on how they were declared.

Hoisting with var

var declarations are hoisted and initialized with undefined.

console.log(myVar); // Output: undefined
var myVar = 10;

Behind the scenes, JavaScript treats it like:

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

Hoisting with let and const

let and const declarations are hoisted but not initialized. Accessing them before declaration causes a ReferenceError due to the Temporal Dead Zone (TDZ).

console.log(myLet); // ReferenceError
let myLet = 5;

Hoisting with Functions

sayHi(); // Works!

function sayHi() {
  console.log('Hi!');
}
sayHello(); // Error or undefined behavior depending on declaration

const sayHello = function() {
  console.log('Hello!');
};

Summary

Concept Description Example
Global scope Variables available anywhere let x = 5;
Function scope Variables available only inside a function function() { var y = 10; }
Block scope Variables available only inside {} blocks (let, const) if (true) { let z = 3; }
Hoisting Declarations moved to top; var initialized to undefined, let/const not initialized console.log(a); var a = 1;
Temporal Dead Zone (TDZ) Time before let or const declaration when accessing them throws an error console.log(b); let b = 2;

Understanding scope and hoisting helps you avoid bugs, write cleaner code, and debug issues related to variable accessibility and initialization.

Index

9.5 Closures (Introductory)

A closure is a powerful and important concept in JavaScript. Simply put, a closure is when a function remembers the environment in which it was created, even after that environment has finished executing.

This might sound complicated, but closures let you write functions that keep access to variables defined outside themselves — which can be incredibly useful.

What Does a Closure Mean?

When a function is defined inside another function, the inner function retains access to the variables of the outer function, even after the outer function has returned. This saved environment is called a closure.

Simple Example: A Counter Function

Let's build a function that creates counters — each counter remembers its own count:

function createCounter() {
  let count = 0;  // This variable is "closed over"

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

const counter = createCounter();

console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

What’s happening here?

Another Example: Greeting Generator

Closures also help when you want to generate customized functions:

function greetGenerator(greeting) {
  return function(name) {
    return `${greeting}, ${name}!`;
  };
}

const sayHello = greetGenerator('Hello');
const sayHi = greetGenerator('Hi');

console.log(sayHello('Alice')); // Hello, Alice!
console.log(sayHi('Bob'));       // Hi, Bob!

Here, each generated function remembers the greeting passed to the outer function.

Why Are Closures Useful?

Closures are everywhere in JavaScript — in event handlers, callbacks, and many libraries — because they let you write more flexible, modular, and powerful code.

Summary

By understanding closures, you'll unlock deeper JavaScript concepts and write code that’s both elegant and effective.

Index