JavaScript Functions Advanced

In JavaScript, functions are treated like any other value, which means they can be stored in variables. This makes functions flexible and easy to use in different parts of your code.

You can also pass functions as arguments to other functions and use them when needed. This allows you to create more reusable and dynamic code.

Functions can even return other functions, which is another important feature of JavaScript. Because of this flexibility, JavaScript functions are called first-class citizens.

first class function

javascript

const greet = function() { return "Hello!"; };
console.log(greet()); // Hello!

A higher-order function is a function that takes another function as an argument or returns a function as its result.

This pattern is commonly used to make code more reusable, flexible, and easier to manage.

You will often see higher-order functions with methods like map, filter, and reduce.

higher order function

javascript

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 to another function as an argument, allowing it to be executed later when needed. Callbacks are commonly used in timers, event listeners, and asynchronous JavaScript code. They are especially useful when you want a function to run only after a particular task or event has been completed. This makes JavaScript code more flexible and helps handle operations that may take some time to finish.

call back function

javascript

function processOrder(orderId, callback) {
  const message = "Order " + orderId + " confirmed!";
  callback(message);
}
processOrder("ORD-1234", msg => console.log(msg)); // Order ORD-1234 confirmed!

setTimeout schedules a function to run once after a specified delay in milliseconds. It returns a timer ID that can be used with clearTimeout to cancel the scheduled function. This is commonly used when you want to delay an action or execute code after a specific amount of time. The timer runs asynchronously, so JavaScript can continue executing other code while waiting for the delay to finish. After the specified time has passed, the callback function is placed in the event queue and runs when the call stack is available.

set time out function

javascript

const timer = setTimeout(() => {
  console.log("Runs after 2 seconds");
}, 2000);

// Cancel before it runs
clearTimeout(timer);

setInterval repeatedly executes a function after a specified time interval. Unlike setTimeout, it continues running the function again and again until it is stopped. It returns a timer ID that identifies the active interval. You can pass this ID to clearInterval to stop the repeated execution. It is commonly used for clocks, countdowns, live updates, counters, polling, and other recurring tasks.

set intervel function

javascript

let count = 0;
const interval = setInterval(() => {
  count++;
  console.log("Count:", count);
  if (count === 3) clearInterval(interval);
}, 1000);

call() and apply() are methods used to invoke a function with a specific this value. They are useful when you want to control which object a function should work with. With call(), function arguments are passed individually, one after another. With apply(), the arguments are passed together inside an array. Both methods are commonly used when working with reusable functions and different object contexts.

call apply

javascript

function showDetails(region, timezone) {
  console.log(this.label + " - " + region + ", " + timezone);
}
const server = { label: "API-East" };
showDetails.call(server, "US-East", "UTC-5");
showDetails.apply(server, ["US-East", "UTC-5"]);

bind() creates a new function with a fixed this value. Unlike call() and apply(), it does not execute the function immediately. Instead, it returns a new function that can be stored in a variable and called later. You can also provide arguments to bind() that will be remembered by the new function. This is useful when you need to preserve the correct this context for later use.

bind

javascript

function logStatus() {
  console.log("Status: " + this.status);
}
const service = { status: "running" };
const boundLog = logStatus.bind(service);
boundLog(); // Status: running

An IIFE (Immediately Invoked Function Expression) is a function that runs immediately after it is defined. It creates its own private scope, which helps prevent variables and functions from leaking into the global scope. This makes IIFEs useful for keeping code isolated and avoiding naming conflicts. They are commonly used for initialization code that only needs to run once. An IIFE can also access variables inside its own scope without exposing them to the rest of the application.

iife

javascript

(function() {
  const secret = "hidden";
  console.log("IIFE runs immediately!");
})();
// console.log(secret); // ReferenceError

A closure is created when a function remembers variables from the scope where it was originally defined. Even after the outer function has finished executing, the inner function can still access those variables. This allows functions to preserve and use data between different executions. Closures are commonly used to create private variables and maintain state in JavaScript. They are also an important concept when working with callbacks, event handlers, and asynchronous code.

closure

javascript

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 processing declarations before the code is executed. Function declarations are fully hoisted, which means you can call a function before its declaration appears in the code. Variables declared with var are also hoisted, but their value is initially undefined. Variables declared with let and const are hoisted but remain uninitialized until their declaration is reached. Understanding hoisting helps you avoid unexpected behavior when working with functions and variables.

hosting

javascript

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 reaching the declaration of a let or const variable. During this period, the variable exists but cannot be accessed before its declaration. Trying to use it inside the TDZ results in a ReferenceError. This behavior helps prevent variables from being used before they are properly initialized. Unlike let and const, accessing a var variable before its declaration returns undefined.

TDZ

javascript

console.log(endpoint); // ReferenceError: Cannot access 'endpoint' before initialization
let endpoint = "/api/users";

DRY stands for "Don't Repeat Yourself" and is a common principle in programming. It means you should avoid writing the same logic multiple times in different parts of your code. Instead, repeated logic can be placed inside a reusable function or component. This keeps your code cleaner, easier to understand, and simpler to maintain. When you need to make a change, you only have to update the logic in one place, reducing the chance of bugs.

dry principal

javascript

// 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 is a technique that transforms a function with multiple arguments into a sequence of smaller functions. Each function takes one argument and returns another function that waits for the next argument. This allows you to provide arguments step by step instead of passing them all at once. Currying can make functions more reusable and easier to combine with other functions. It is also useful for creating pre-configured functions with some values already provided.

currying

javascript

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 containing JavaScript code and executes that string as actual JavaScript. While it can dynamically run code, using it is generally considered a bad practice in modern JavaScript. It can make your code slower, harder to read, maintain, and debug. More importantly, executing untrusted input with eval() can create serious security vulnerabilities. In most situations, safer alternatives such as normal functions, objects, or direct JavaScript expressions should be used instead.

eval

javascript

const code = "2 + 2";
console.log(eval(code)); // 4

// Avoid eval - use safer alternatives instead

Recursion is a programming technique where a function calls itself to solve a problem step by step. Each recursive call usually works with a smaller or simpler version of the original problem. The most important part of recursion is the base case, which defines when the function should stop calling itself. Without a proper base case, the function can continue calling itself indefinitely and cause an error. Recursion is commonly used for tasks such as traversing trees, processing nested data, and solving mathematical problems.

recursion

javascript

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; var is hoisted but not its value.
  • TDZ is the zone where let/const variables 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.

What's next? Now that you've seen more powerful function concepts, let's learn how to spot and fix problems in the next tutorial.

Videos for this topic will be added soon.

Reviewed by

SimplyJavaScript Editorial Team

Technical editors and JavaScript educators with hands-on experience building frontend projects, writing learning material, and reviewing tutorials for clarity, accuracy, and beginner-friendly guidance.