Functions

Functions are reusable blocks of code that perform a specific task. Instead of writing the same code multiple times, you can write it once in a function and call it whenever needed.

Why Use Functions?

  1. Code Reusability: Write once, use many times.
  2. Organization: Break programs into smaller, manageable pieces.
  3. Readability: Make the code easier to understand.
  4. Maintainability: Update logic in one place.

How to Create a Function

You can create a function using the function keyword.

Basic Syntax

Javascript
Copy
function functionName() {
  // Code to run
}

Example:

Javascript
Copy
function sayHello() {
  console.log("Hello, World!");
}

How to Call a Function

To use a function, write its name followed by parentheses ().

Example:

Javascript
Copy
function sayHello() {
  console.log("Hello, World!");
}

sayHello(); // Outputs: Hello, World!

Functions with Parameters

Parameters allow you to pass data into a function, making it flexible and reusable.

Syntax with Parameters

Javascript
Copy
function functionName(parameter1, parameter2) {
  // Code using parameters
}

Example:

Javascript
Copy
function greet(name) {
  console.log("Hello, " + name + "!");
}

greet("Alice"); // Outputs: Hello, Alice!
greet("Bob");   // Outputs: Hello, Bob!

Functions with Return Values

A function can return a value using the return keyword. This value can be stored in a variable or used directly.

Example:

Javascript
Copy
function addNumbers(a, b) {
  return a + b;
}

let result = addNumbers(5, 3); // Stores 8 in result
console.log(result);          // Outputs: 8

Function Expressions

Functions can also be assigned to a variable. These are called function expressions.

Example:

Javascript
Copy
const multiply = function (x, y) {
  return x * y;
};

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

Arrow Functions (Modern Syntax)

Arrow functions are a shorter way to write functions. Use => to define them.

Syntax:

Javascript
Copy
const functionName = (parameters) => {
  // Code to run
};

Example:

Javascript
Copy
const subtract = (a, b) => a - b;

console.log(subtract(10, 4)); // Outputs: 6

Default Parameters

You can set default values for parameters in case they are not provided.

Example:

Javascript
Copy
function greet(name = "Guest") {
  console.log("Hello, " + name + "!");
}

greet("Alice"); // Outputs: Hello, Alice!
greet();        // Outputs: Hello, Guest!

Function Scope

Variables declared inside a function are local to that function. They cannot be accessed outside of it.

Example:

Javascript
Copy
function showMessage() {
  let message = "Hello!";
  console.log(message);
}

showMessage(); // Outputs: Hello!
// console.log(message); // Error: message is not defined

Nested Functions

You can define functions inside other functions. The inner function can only be used inside the outer function.

Example:

Javascript
Copy
function outerFunction() {
  console.log("Outer Function");

  function innerFunction() {
    console.log("Inner Function");
  }

  innerFunction();
}

outerFunction();
// Outputs:
// Outer Function
// Inner Function

Anonymous Functions

Functions without a name are called anonymous functions. They're often used as arguments in other functions.

Example:

Javascript
Copy
setTimeout(function () {
  console.log("This runs after 2 seconds");
}, 2000);

Immediately Invoked Function Expression (IIFE)

An IIFE is a function that runs as soon as it is defined.

Example:

Javascript
Copy
(function () {
  console.log("IIFE runs immediately!");
})();

Rest Parameters

Rest parameters allow a function to accept an indefinite number of arguments as an array. This is useful when you don't know how many arguments will be passed to your function.

Syntax

Use three dots ... followed by the name of the rest parameter.

Example:

Javascript
Copy
function sum(...numbers) {
  let total = 0;
  for (let number of numbers) {
    total += number;
  }
  return total;
}

console.log(sum(1, 2, 3));       // Outputs: 6
console.log(sum(5, 10, 15, 20)); // Outputs: 50

Key Points:

  1. The ...numbers collects all arguments into an array.
  2. You can use array methods like forEach, map, or reduce on rest parameters.

The Arguments Object

The arguments object is a built-in feature in JavaScript functions that provides all the arguments passed to the function. It’s like an array, but it doesn’t have array methods like map or filter.

Example:

Javascript
Copy
function showArguments() {
  for (let i = 0; i < arguments.length; i++) {
    console.log("Argument", i, ":", arguments[i]);
  }
}

showArguments("Hello", 42, true);
// Outputs:
// Argument 0 : Hello
// Argument 1 : 42
// Argument 2 : true

Key Points:

  1. Works with regular functions but not with arrow functions.
  2. Useful for handling variable numbers of arguments in older code.

Closures

A closure is when a function "remembers" and has access to variables from its parent scope, even after the parent function has finished running. Closures are created naturally when you define a function inside another function.

Why Are Closures Useful?

  1. Data Privacy: They help you create private variables.
  2. State Management: You can remember data between function calls.

Example 1: Simple Closure

Javascript
Copy
function outerFunction() {
  let count = 0; // Variable in the outer function

  return function innerFunction() {
    count++;
    console.log("Count:", count);
  };
}

let counter = outerFunction();
counter(); // Outputs: Count: 1
counter(); // Outputs: Count: 2

In the above example, innerFunction remembers the count variable from outerFunction, even after outerFunction is done.

Example 2: Closures for Private Variables

Javascript
Copy
function createCounter() {
  let count = 0; // Private variable

  return {
    increment: function () {
      count++;
      console.log("Count:", count);
    },
    reset: function () {
      count = 0;
      console.log("Count reset to:", count);
    }
  };
}

let counter = createCounter();
counter.increment(); // Outputs: Count: 1
counter.increment(); // Outputs: Count: 2
counter.reset();     // Outputs: Count reset to: 0

In the above example, the count variable is private and only accessible through the methods increment and reset.

Example 3: Closure in a Loop

Closures can sometimes behave unexpectedly in loops, but modern JavaScript solves this using let.

Problem Without Closures:

Javascript
Copy
function createFunctions() {
  let functions = [];

  for (var i = 0; i < 3; i++) {
    functions.push(function () {
      console.log(i);
    });
  }

  return functions;
}

let funcs = createFunctions();
funcs[0](); // Outputs: 3 (not 0 as expected)
funcs[1](); // Outputs: 3
funcs[2](); // Outputs: 3

In the above example, i is shared across all functions.

Solution Using Closures:

Javascript
Copy
function createFunctions() {
  let functions = [];

  for (let i = 0; i < 3; i++) { // Use let instead of var
    functions.push(function () {
      console.log(i);
    });
  }

  return functions;
}

let funcs = createFunctions();
funcs[0](); // Outputs: 0
funcs[1](); // Outputs: 1
funcs[2](); // Outputs: 2

Now in the above example, each function remembers its own i value.