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?
- Code Reusability: Write once, use many times.
- Organization: Break programs into smaller, manageable pieces.
- Readability: Make the code easier to understand.
- Maintainability: Update logic in one place.
How to Create a Function
You can create a function using the function keyword.
Basic Syntax
function functionName() { // Code to run }
Example:
function sayHello() { console.log("Hello, World!"); }
How to Call a Function
To use a function, write its name followed by parentheses ().
Example:
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
function functionName(parameter1, parameter2) { // Code using parameters }
Example:
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:
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:
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:
const functionName = (parameters) => { // Code to run };
Example:
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:
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:
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:
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:
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:
(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:
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:
- The ...numbers collects all arguments into an array.
- 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:
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:
- Works with regular functions but not with arrow functions.
- 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?
- Data Privacy: They help you create private variables.
- State Management: You can remember data between function calls.
Example 1: Simple Closure
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
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:
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:
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.