JavaScript Functions and Methods: A Topic-by-Topic Reference · Lesson 9 of 12

Lesson 9: Functions: call, apply, bind, Closures and Recursion

Declarations, arrows, default and rest parameters, call, apply, bind, closures, higher-order functions, recursion, memoization and timers.

Lesson 9: Functions: call, apply, bind, Closures and Recursion

Ways to write a function

Declaration, expression and arrow

Three styles. Declarations are hoisted (usable before they appear); arrows are short and do not have their own this.

console.log(double(4));          // works: declarations are hoisted
function double(n) { return n * 2; }

const triple = function (n) { return n * 3; };
const square = (n) => n * n;
const makePair = (a, b) => ({ a, b });   // wrap an object in ( )
console.log(triple(4), square(4), makePair(1, 2));

Output:

8
12 16 { a: 1, b: 2 }

Default, rest and spread

Defaults fill missing arguments; ...rest collects extras into an array; spread passes an array as arguments.

function greet(name = "friend", ...others) {
  return `Hello ${name} and ${others.length} others`;
}
console.log(greet());
console.log(greet("Ram", "Sita", "Hari"));
const nums = [3, 9, 2];
console.log(Math.max(...nums));

Output:

Hello friend and 0 others
Hello Ram and 2 others
9

Named parameters with an object

Pass one object and destructure it: order does not matter and names make calls readable.

function createUser({ name, age = 18, admin = false } = {}) {
  return { name, age, admin };
}
console.log(createUser({ name: "Sita", admin: true }));

Output:

{ name: 'Sita', age: 18, admin: true }

arguments and return values

A function returns undefined unless it returns something. Return an array or object to give back several values.

function minMax(arr) { return { min: Math.min(...arr), max: Math.max(...arr) }; }
const { min, max } = minMax([4, 9, 2]);
console.log(min, max);
console.log((function () {})());

Output:

2 9
undefined

Controlling this: call, apply, bind

call, apply, bind

Run a function with a chosen this. call takes arguments one by one, apply as an array, and bind returns a new function with this fixed.

function intro(greeting, punct) {
  return `${greeting}, I am ${this.name}${punct}`;
}
const ram = { name: "Ram" };
console.log(intro.call(ram, "Hello", "!"));
console.log(intro.apply(ram, ["Namaste", "."]));
const ramIntro = intro.bind(ram, "Hi");
console.log(ramIntro("?"));

Output:

Hello, I am Ram!
Namaste, I am Ram.
Hi, I am Ram?

Partial application with bind

Fix some arguments in advance.

const multiply = (a, b) => a * b;
const double = multiply.bind(null, 2);
console.log(double(21));

Output:

42

Closures

A closure is a function that remembers the variables around it, even after the outer function has finished. It is how you keep private state.

A counter

count cannot be reached from outside, only through the returned functions.

function makeCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    value: () => count,
  };
}
const a = makeCounter(), b = makeCounter();
a.increment(); a.increment(); b.increment();
console.log(a.value(), b.value());

Output:

2 1

Function factories

A function that builds specialised functions.

const multiplier = (factor) => (n) => n * factor;
const triple = multiplier(3);
console.log(triple(5), multiplier(10)(5));

Output:

15 50

The classic loop trap

var shares one variable across all loop rounds; let makes a new one for each round.

const withVar = [], withLet = [];
for (var i = 0; i < 3; i++) withVar.push(() => i);
for (let j = 0; j < 3; j++) withLet.push(() => j);
console.log(withVar.map((f) => f()), withLet.map((f) => f()));

Output:

[ 3, 3, 3 ] [ 0, 1, 2 ]

Higher-order functions

Functions that take or return functions

once, compose and pipe are small building blocks.

const once = (fn) => { let done = false, r; return (...a) => (done ? r : ((done = true), (r = fn(...a)))); };
const init = once(() => { console.log("initialising"); return 42; });
console.log(init(), init());
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
console.log(pipe((x) => x + 1, (x) => x * 2)(5));

Output:

initialising
42 42
12

Memoization

Remember results so slow work is done once. A closure holds the cache.

const memo = (fn) => { const cache = new Map(); return (n) => cache.has(n) ? cache.get(n) : (cache.set(n, fn(n)), cache.get(n)); };
const fib = memo((n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)));
console.log(fib(50));

Output:

12586269025

Recursion

A function that calls itself

Needs a base case to stop. Good for trees, nested data and divide-and-conquer.

const factorial = (n) => (n <= 1 ? 1 : n * factorial(n - 1));
console.log(factorial(5));
const sumNested = (a) => a.reduce((t, x) => t + (Array.isArray(x) ? sumNested(x) : x), 0);
console.log(sumNested([1, [2, [3, 4]], 5]));

Output:

120
15

Timers

setTimeout, setInterval, clearTimeout

Run code later or repeatedly. The callback runs after the current code finishes, even with a delay of 0.

console.log("first");
setTimeout(() => console.log("third (after 10 ms)"), 10);
setTimeout(() => console.log("second (0 ms, but later)"), 0);
console.log("end of script");
let n = 0;
const id = setInterval(() => { console.log("tick", ++n); if (n === 2) clearInterval(id); }, 20);

Output:

first
end of script
second (0 ms, but later)
third (after 10 ms)
tick 1
tick 2

Debounce

Wait until the calls stop for a while: used for search boxes and resize events (needs a browser to see in action).

function debounce(fn, ms) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  };
}
const search = debounce((q) => console.log("search for", q), 100);
search("p"); search("py"); search("pyt");

Output:

search for pyt

Try it yourself

  1. Write a function makeAdder(n) that returns a function adding n to its argument.
  2. Write sum(...numbers) with a rest parameter.
  3. Make a counter with increment, decrement and reset.

Next lesson: dates, JSON and storage.

Finished this lesson?Log in to save your progress and pick up where you left off.
Next lesson →

Comments

Loading comments…

MeroSir Team
A free MeroSir course. Run the examples yourself and try the exercises: you learn programming by doing. Spotted a mistake? Use the Report button above the lesson.
Lesson 9: Functions: call, apply, bind, Closures and Recursion | MeroSir