Functions Advanced
In JavaScript, functions are first-class citizens. This means a function can be stored in a variable, passed as an argument to another function, and returned as a value from another function. Functions are treated just like any other value.
const greet = function() { return "Hello!"; };
console.log(greet()); // Hello!
A higher-order function is a function that takes another function as an argument, returns a function, or both. They are used heavily in JavaScript for things like array methods (map, filter, reduce) and event handling.
function applyTwice(fn, value) {
return fn(fn(value));
}
const double = x => x * 2;
console.log(applyTwice(double, 3)); // 12
A callback function is a function passed into another function as an argument. The outer function calls it at the right time. Callbacks are commonly used for handling asynchronous operations like API calls, timers, and event listeners.
function greetUser(name, callback) {
const message = "Hello, " + name + "!";
callback(message);
}
greetUser("Alice", msg => console.log(msg)); // Hello, Alice!
setTimeout schedules a function to run once after a given delay in milliseconds. It returns a timer ID that you can pass to clearTimeout to cancel the scheduled call before it runs.
const timer = setTimeout(() => {
console.log("Runs after 2 seconds");
}, 2000);
// Cancel before it runs
clearTimeout(timer);
setInterval runs a function repeatedly at a fixed time interval (in milliseconds). Use clearInterval with the returned interval ID to stop it from running again.
let count = 0;
const interval = setInterval(() => {
count++;
console.log("Count:", count);
if (count === 3) clearInterval(interval);
}, 1000);
call() and apply() let you invoke a function with a specific this value. The difference is how you pass arguments: call() takes them one by one, while apply() takes them as an array.
function introduce(city, country) {
console.log(this.name + " from " + city + ", " + country);
}
const person = { name: "Alice" };
introduce.call(person, "Paris", "France");
introduce.apply(person, ["Paris", "France"]);
bind() creates a new function with a fixed this value. Unlike call() and apply(), it does not call the function right away. You can store the bound function and call it later.
function greet() {
console.log("Hi, " + this.name);
}
const user = { name: "Bob" };
const boundGreet = greet.bind(user);
boundGreet(); // Hi, Bob
An IIFE is a function that runs immediately when it is defined. It creates its own private scope, so variables inside it do not leak into the global scope. IIFEs are useful for initialization code and avoiding naming conflicts.
(function() {
const secret = "hidden";
console.log("IIFE runs immediately!");
})();
// console.log(secret); // ReferenceError
A closure is a function that remembers the variables from its outer scope even after the outer function has finished running. This happens because the inner function keeps a reference to the environment where it was created.
function counter() {
let count = 0;
return function() {
count++;
return count;
};
}
const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
Hoisting is JavaScript's behavior of moving declarations to the top of their scope before code runs. Function declarations are fully hoisted, so you can call them before they appear in the code. var declarations are hoisted but their value is not, so they return undefined if accessed early. let and const are hoisted but are not initialized until the line is reached.
console.log(sayHi()); // "Hi!" - works because declaration is hoisted
function sayHi() { return "Hi!"; }
console.log(x); // undefined - var is hoisted but not its value
var x = 5;
The Temporal Dead Zone (TDZ) is the period between entering a scope and the point where a let or const variable is declared. If you try to access the variable during this period, JavaScript throws a ReferenceError. This is different from var, which just returns undefined when accessed early.
console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "Alice";
DRY stands for "Don't Repeat Yourself." It means you should extract repeated logic into a function so it lives in one place. This makes your code easier to maintain and reduces the chance of bugs when something needs to change.
// Without DRY
console.log("Tax for 100:", 100 * 0.18);
console.log("Tax for 200:", 200 * 0.18);
// With DRY
function calculateTax(amount) { return amount * 0.18; }
console.log("Tax for 100:", calculateTax(100));
console.log("Tax for 200:", calculateTax(200));
Currying transforms a function that takes multiple arguments into a chain of functions, each taking one argument at a time. It lets you create reusable, partially applied functions.
function multiply(a) {
return function(b) {
return a * b;
};
}
const triple = multiply(3);
console.log(triple(4)); // 12
console.log(multiply(2)(5)); // 10
eval() takes a string and executes it as JavaScript code. It should be avoided in most cases because it is slow, hard to debug, and can be a security risk if used with untrusted input.
const code = "2 + 2";
console.log(eval(code)); // 4
// Avoid eval - use safer alternatives instead
Recursion is when a function calls itself to solve a problem by breaking it into smaller sub-problems. Every recursive function needs a base case to stop the calls, otherwise it runs forever and causes a stack overflow.
function factorial(n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive call
}
console.log(factorial(5)); // 120
- First-class functions can be stored in variables, passed as arguments, and returned from other functions.
- Higher-order functions take a function as an argument or return one.
- Callback functions are passed into another function and called inside it.
- setTimeout runs a function once after a delay; clearTimeout cancels it.
- setInterval runs a function repeatedly; clearInterval stops it.
- call() and apply() invoke a function with a specific
this; apply uses an array for arguments. - bind() returns a new function with a fixed
this, without calling it immediately. - IIFE is a function that runs immediately and creates a private scope.
- Closures let inner functions remember variables from their outer scope after the outer function has returned.
- Hoisting moves declarations to the top of their scope. Function declarations are fully hoisted;
varis hoisted but not its value. - TDZ is the zone where
let/constvariables exist but cannot be accessed yet. - DRY means "Don't Repeat Yourself" - extract repeated logic into reusable functions.
- Currying converts a multi-argument function into a chain of single-argument functions.
- eval() runs a string as code - avoid it due to performance and security concerns.
- Recursion is when a function calls itself, always with a base case to stop the loop.