On this page

Modern functions

15 min read TextCh. 1 — Fundamentals

Arrow functions

Arrow functions are the most concise way to write functions in modern JavaScript. Introduced in ES6, they have special rules:

  • They do not have their own this (they inherit from the parent scope)
  • They do not have arguments (use rest parameters instead)
  • They cannot be used as constructors (new)

Concise vs block syntax

// Implicit return (single expression)
const square = x => x * x;

// Implicit return of object (needs parentheses)
const createPoint = (x, y) => ({ x, y });

// Block with explicit return
const calculate = (a, b) => {
  const result = a + b;
  return result;
};

Default parameters

Parameters with default values avoid manual undefined checks:

// Before ES6
function old(name) {
  name = name || 'Anonymous';
}

// ES6+
function modern(name = 'Anonymous') {
  // name already has a default value
}

Rest parameters

The rest operator (...) captures a variable number of arguments into a real array (unlike the old arguments):

function largest(...numbers) {
  return Math.max(...numbers);
}
largest(3, 7, 2, 9); // 9

Closures

A closure is a function that remembers the variables from the scope where it was created, even when it is executed outside that scope. It is one of the most powerful concepts in JavaScript:

  • They allow you to create private state
  • They are the foundation of patterns like factory functions and modules
  • Each closure has its own copy of the captured variables

Higher-order functions

A higher-order function is one that receives functions as arguments or returns functions. They are fundamental in functional programming:

Pattern Example Use
Callback setTimeout(fn, 1000) Deferred execution
Factory createValidator(min, max) Function generator
Composition compose(f, g) Combining transformations

IIFE

An IIFE (Immediately Invoked Function Expression) executes immediately upon definition. It is useful for creating an isolated scope:

const api = (() => {
  let state = 'ready';
  return {
    getState: () => state,
    execute: () => { state = 'running'; },
  };
})();

Practice

  1. Create an arrow function with default parameters: Write a function calculateFinalPrice that receives price and discount = 0 and returns the price with the discount applied. Test it with and without the second argument.
  2. Implement a closure counter: Create a function createCounter that returns a function. Each time the returned function is called, it should increment and return an internal counter. Verify that two separately created counters are independent.
  3. Build a factory function: Write a function createMultiplier(factor) that returns another function which multiplies any number by factor. Create double and triple from it and test them.

In the next lesson we will explore objects and arrays in depth.

Arrow functions and this
Arrow functions do not have their own this. They inherit the this from the scope where they were created. This makes them ideal for callbacks, but not for object methods that need to access this.
Pure functions
A pure function always returns the same result for the same arguments and has no side effects. Preferring pure functions makes your code more predictable and easier to test.
javascript
// Arrow functions
const add = (a, b) => a + b;
const greet = name => `Hello, ${name}!`;

// Arrow with body
const calculateTax = (price, rate = 0.13) => {
  const tax = price * rate;
  return price + tax;
};

// Default parameters
function createUser(name, role = 'student', active = true) {
  return { name, role, active };
}

// Rest parameters
function logMessage(level, ...messages) {
  messages.forEach(msg => {
    console.log(`[${level}] ${msg}`);
  });
}
logMessage('INFO', 'Server started', 'Port 3000');

// Closures
function createGreeting(prefix) {
  return (name) => `${prefix}, ${name}!`;
}
const hello = createGreeting('Hello');
const goodDay = createGreeting('Good morning');
console.log(hello('Carlos'));      // "Hello, Carlos!"
console.log(goodDay('Maria'));     // "Good morning, Maria!"

// IIFE (Immediately Invoked Function Expression)
const config = (() => {
  const env = 'production';
  return { env, debug: env !== 'production' };
})();
javascript
// Higher-order functions
function repeat(n, action) {
  for (let i = 0; i < n; i++) {
    action(i);
  }
}
repeat(3, i => console.log(`Iteration ${i}`));

// Returning functions (factory)
function createValidator(min, max) {
  return (value) => value >= min && value <= max;
}
const validateAge = createValidator(0, 120);
const validatePercentage = createValidator(0, 100);

console.log(validateAge(25));        // true
console.log(validatePercentage(150));  // false

// Function composition
const double = x => x * 2;
const increment = x => x + 1;
const compose = (f, g) => x => f(g(x));
const doubleAndAdd = compose(increment, double);
console.log(doubleAndAdd(5)); // 11 (5*2 + 1)