AiTechWorlds
AiTechWorlds
A function is a reusable block of code that takes input, does something with it, and optionally returns a result. Everything in modern JS React components, event handlers, API calls, async operations is built on functions. Understanding every form of function and when to use each is what separates junior from senior developers.
// 1. Function Declaration
function greet(name) {
return `Hello, ${name}!`;
}
// 2. Function Expression
const greet = function(name) {
return `Hello, ${name}!`;
};
// 3. Arrow Function (ES6+)
const greet = (name) => `Hello, ${name}!`;
// All three work the same way for basic calls:
console.log(greet("Alice")); // "Hello, Alice!"They're not identical and the differences matter.
// You can call a function declaration BEFORE it's defined
sayHello(); // "Hello!" — works because of hoisting
function sayHello() {
console.log("Hello!");
}
// Function expressions are NOT hoisted
sayBye(); // TypeError: sayBye is not a function
const sayBye = function() {
console.log("Bye!");
};Hoisting means function declarations are moved to the top of their scope before execution. Function expressions behave like variables they must be defined before use.
Practical rule: Use const + arrow functions as your default. Reserve function declarations for top level functions you want to call from anywhere in the file.
// No parameters — use empty parens
const sayHi = () => "Hi!";
// One parameter — parens optional
const double = x => x * 2;
// Multiple parameters — parens required
const add = (a, b) => a + b;
// Multi-line body — requires curly braces AND explicit return
const processUser = (user) => {
const name = user.name.trim();
const email = user.email.toLowerCase();
return { name, email };
};Key difference this binding:
Arrow functions do NOT have their own this. They inherit this from the surrounding scope. This is critical in React and event driven code:
// Problem with regular function
class Timer {
constructor() {
this.seconds = 0;
}
start() {
setInterval(function() {
this.seconds++; // BUG: 'this' is undefined or window
}, 1000);
}
}
// Fixed with arrow function
class Timer {
constructor() {
this.seconds = 0;
}
start() {
setInterval(() => {
this.seconds++; // 'this' is the Timer instance ✓
}, 1000);
}
}function createUser(name, role = "user", active = true) {
return { name, role, active };
}
createUser("Alice") // { name: "Alice", role: "user", active: true }
createUser("Bob", "admin") // { name: "Bob", role: "admin", active: true }
createUser("Carol", "mod", false) // { name: "Carol", role: "mod", active: false }...args)// Collect remaining arguments into an array
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3) // 6
sum(10, 20, 30, 40) // 100
// Combine named + rest
function logMessage(level, ...messages) {
console.log(`[${level}]`, messages.join(" "));
}
logMessage("ERROR", "File", "not", "found") // [ERROR] File not foundIn JavaScript, functions are values. You can:
// Assign to variables (already seen this)
const add = (a, b) => a + b;
// Pass as arguments (callbacks)
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
const total = numbers.reduce((sum, n) => sum + n, 0); // 15
// Return from functions (higher-order functions)
function createMultiplier(factor) {
return (n) => n * factor;
}
const triple = createMultiplier(3);
const tenX = createMultiplier(10);
console.log(triple(7)); // 21
console.log(tenX(5)); // 50A closure is a function that retains access to variables from its outer scope, even after that outer function has finished executing:
function createCounter(start = 0) {
let count = start; // This variable is "closed over"
return {
increment() { count++; },
decrement() { count--; },
value() { return count; }
};
}
const counter = createCounter(10);
counter.increment();
counter.increment();
counter.decrement();
console.log(counter.value()); // 11
// 'count' is private — can't access it directly
console.log(counter.count); // undefinedClosures power module patterns, React hooks, and many design patterns.
// Pure function — same input always gives same output, no side effects
function add(a, b) {
return a + b;
}
// Impure — has side effects (modifies external state)
let total = 0;
function addToTotal(n) {
total += n; // Side effect: modifies outer variable
return total;
}
// Impure — depends on external state
function getCurrentUser() {
return window.currentUser; // Output depends on external state
}Prefer pure functions they're easier to test, debug, and reason about. This is core to React's model.
// Creates a private scope — used in older code to avoid polluting global scope
const result = (function() {
const privateData = "secret";
return {
getData: () => privateData
};
})();
result.getData() // "secret"Less common with modern modules, but you'll see it in legacy codebases.
| Hoisted | this | Use When | |
|---|---|---|---|
| Function declaration | Yes | Own this | Top level named functions |
| Function expression | No | Own this | Named callback or method |
| Arrow function | No | Inherited | Default for almost everything |
Next lesson: Arrow Functions & this Binding the deep dive into JavaScript's most confusing concept.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises