JavaScript Functions: Scope and Closures

Learn to package JavaScript code as reusable functions. Write function declarations, function expressions and arrow functions; pass values in with parameters, default values and rest parameters; and send results back with return. Predict where a variable can be used with block, function and global scope, use closures to give functions their own memory, and pass functions to other functions as callbacks to write flexible higher-order code.

BeginnerLesson 3 of 9 in JavaScript Programming Course

What you will learn

  1. Package a reusable piece of code as a function, call it with arguments, and choose between a function declaration, a function expression and an arrow function.
    • Write and call a function declaration
      Define a named function with the function keyword, call it, and explain why functions make code shorter, clearer and easier to change.
    • Store a function in a variable with a function expression
      Treat functions as values: create a function expression, assign it to a variable, and explain how it differs from a declaration in hoisting and naming.
    • Write arrow functions
      Use the short arrow syntax, know when an arrow function returns a value automatically, and explain how arrow functions differ from functions written with function.
  2. Design what goes into a function and what comes out: match arguments to parameters, set defaults, accept any number of arguments, return results and follow how calls run one inside another.
    • Match arguments to parameters
      Explain how the values in a call reach the function's parameters, what happens with too few or too many arguments, and which of the caller's data a function can change.
    • Set default values and accept any number of arguments
      Give parameters default values, collect any number of arguments with a rest parameter, and spread an array into separate arguments.
    • Return results from a function
      Send a value back with return, use the result in further code, and tell returning a value apart from printing it or changing something outside the function.
    • Follow function calls through the call stack
      Trace the order in which nested calls run and return, and write a simple recursive function with a base case.
  3. Predict where a variable is visible by applying the rules for block, function and global scope and for functions written inside other functions.
    • Tell block, function and global scope apart
      Explain the scope of a variable declared with let, const or var, and use local variables to keep functions independent of each other.
    • Predict which variable a name refers to
      Follow the scope chain to find the variable behind a name, recognize shadowing, and explain hoisting and the temporal dead zone.
  4. Explain how a function keeps access to the variables around it after the outer function has finished, and use that to build function factories and private state.
    • Explain what a closure remembers
      Describe how a function keeps access to the variables of the scope in which it was created, even after the outer function has returned.
    • Use closures for function factories and private state
      Build function factories, keep data private behind functions, and avoid the classic closure-in-a-loop mistake.
  5. Use functions as values: hand them to other functions as callbacks, use and write higher-order functions, and build new functions from existing ones.
    • Pass a callback to a function
      Give a function to another function as an argument, write functions that call a callback, and pass the function itself rather than its result.
    • Use and write higher-order functions
      Recognize higher-order functions, use the array methods that take callbacks, and write your own function that takes a function as input.
    • Build new functions from existing ones
      Write higher-order functions that wrap, combine or pre-fill other functions, using closures to remember the function they were given.

JavaScript Functions study guide

5 chapters

1

Define and call functions in three forms

Functions turn repeated instructions into named tasks. You will read and write three common forms, then decide whether a line creates a function, stores one, or calls one. Small changes in syntax matter: a declaration is available early, while a stored expression must be initialized first, and an arrow body can change whether a result is returned. These distinctions prepare you to pass functions around as ordinary values.

1.1 Write and call a function declaration

A function gives a task a name and stores the instructions for doing it. Defining it does not run those instructions. A call runs them, and another call runs them again with new inputs.

Write the definition

A declaration has the keyword function, a name, parentheses for parameters, and curly braces for the body:

function greet(name) {
  console.log("Hello, " + name + "!");
}

Here name is a parameter: a local name for the input each call supplies. The body joins that input to the surrounding text and prints the result. The closing brace marks the end of the definition; execution continues with the next statement outside it.

A function with no inputs still needs parentheses. For example, function sayHi() { console.log("Hi"); } defines a task with an empty parameter list. The braces belong to the definition; the parentheses after a name belong in both definitions and calls, with different jobs.

Run the task
greet("Amira");
greet("Li");

The first call gives name the value "Amira" and prints "Hello, Amira!". The second starts a new call with name equal to "Li" and prints "Hello, Li!". The definition is shared; the input belongs to the individual call.

greet by itself refers to the function value. It does not run the body. This distinction matters when a line seems to do nothing: sayHi; reads a function value and discards it, while sayHi(); prints "Hi".

Replace copied instructions

Suppose several reports need a heading surrounded by lines:

function printHeading(title) {
  console.log("-----");
  console.log(title);
  console.log("-----");
}

printHeading("Report");
printHeading("Totals");

The first call prints a line, "Report", and another line. The second prints the same frame around "Totals". To change the line style, edit the two line-printing statements in this one definition. Without the function, every copied heading would need its own repair.

Choose a name that says what one call does. printHeading describes an action; isValidEmail names a yes-or-no check. Use camelCase, with a lowercase first word and uppercase starts for later words. If a proposed name joins two separate jobs with "and", consider giving each job its own function.

Read code that calls a declaration early

Declarations are set up before the surrounding code runs. This behavior is called hoisting:

sayHi();
function sayHi() {
  console.log("Hi");
}

The call works within the declaration's scope even though the written definition comes later. Hoisting does not make the body run early; it makes the function available for a call.

Check spelling and capitalization when a call fails. Greet() and greet() are different names. Calling an undeclared greeet() raises a ReferenceError; omitting the call parentheses can fail silently because merely reading an existing function is valid code.

Key Takeaways

  • Defining a function stores instructions; calling it runs them.
  • Call with parentheses, even when there are no arguments.
  • A declaration is available before its written position within the same scope.
  • Name one clear task so repeated code has one place to change.

1.2 Store a function in a variable with a function expression

Functions are values. You can keep one in a variable, copy its reference, pass it to another function, or return it. A function expression creates that value where an expression is expected.

Store a function
const square = function (n) {
  return n * n;
};

const answer = square(4);

The expression after = creates a function. The assignment stores it in square; the later call returns 16. This statement ends with a semicolon, following the formatting convention used here.

The position of function distinguishes the forms. A definition written as a standalone function square(n) { ... } is a declaration. A function written after = or directly as a call argument is an expression. Both ordinary function forms can accept parameters and return values.

Use const for a function variable when its value should stay the same. Calling square(5) is allowed; assigning square = 5; raises a TypeError. The constant protects the variable from replacement. It does not prevent repeated calls.

Keep a function or keep its result
const sq = square;
const result = square(3);

sq refers to the same function as square. Calling sq(3) returns 9. result holds the number 9 that one call returned. It cannot be called as a function. typeof square and typeof sq are "function"; typeof result is "number".

Reading an assignment from right to left helps: first decide whether its right side creates a function, refers to an existing function, or calls one. The variable on the left receives that value. Copying the reference does not create a second independent implementation.

Wait for the assignment to run

A declaration and a stored expression become callable at different times:

FormBefore the written definition or assignment
function square(n) { return n * n; }Callable within its scope
const square = function (n) { return n * n; };Reading square raises a ReferenceError
var square = function (n) { return n * n; };square is undefined; calling it raises a TypeError

With const or let, the name cannot be used before initialization. With var, the name exists early, but the function value is not assigned until execution reaches the expression. Hoisting the variable does not hoist the assignment.

Give an expression an internal name
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};

The outside name is factorial. The name fact belongs inside the function and lets it call itself; outside code cannot use that internal name directly. The expression's name can also make stack traces easier to read. For example, factorial(3) computes 3 times 2 times 1 and returns 6.

Declarations work well for the main named tasks in a script. Expressions fit places where code needs a function value, such as a variable assignment or an argument. Choosing an expression changes how the value is created and stored, not how parentheses call it.

Key Takeaways

  • A function expression creates a function value inside another expression.
  • Assigning square copies a function reference; assigning square(3) stores its result.
  • A function expression stored in const is callable only after initialization.
  • A var function variable holds undefined before its assignment runs.
  • A named expression can use its internal name for self-calls.

1.3 Write arrow functions

Arrow functions are function expressions with a shorter spelling. Their return behavior depends on what follows the arrow, so inspect the body before deciding what a call produces.

Choose the parameter form
const sayHi = () => "hi";
const double = n => n * 2;
const add = (a, b) => a + b;

No parameters require empty parentheses. One simple parameter may omit parentheses. Two or more require them. A default or rest parameter also needs parentheses: (n = 1) => n * 2 and (...nums) => nums.length are valid.

The arrow separates inputs from the body. const double = n => n * 2; creates the function and stores it; double(6) later calls it and returns 12. Like a const holding any function expression, double cannot be read before its initialization line runs.

Return an expression or write a block
BodyExampleResult of the call with 2 and 3
Concise expression(a, b) => a + b5
Block with return(a, b) => { return a + b; }5
Block without return(a, b) => { a + b; }undefined

A concise body returns its expression automatically. Curly braces begin a block of statements. In a block, evaluating an expression does not send it back to the caller; write return explicitly.

Use a block when the task needs intermediate work:

const describe = (n) => {
  const kind = n % 2 === 0 ? "even" : "odd";
  return n + " is " + kind;
};

For describe(7), the condition is false, so kind becomes "odd". The return then joins the number and words into "7 is odd". Removing that return would leave the local calculation in place but give the caller undefined.

Return an object literal

An object literal also uses curly braces. Wrap it in parentheses so the parser treats it as an expression:

const makePoint = (x, y) => ({ x: x, y: y });

makePoint(2, 5) returns an object with x equal to 2 and y equal to 5. Without the parentheses, the first brace begins a block. The single-property form () => { value: 1 } returns undefined; the similar two-property form with a comma is a syntax error. The parentheses change how the body is read.

Know the limits of the shorter form

Arrow functions do not have their own this or arguments, and they cannot be called with new to construct objects. For a simple calculation that uses neither this nor arguments, an arrow and an ordinary function expression behave the same. Replacing every ordinary function with an arrow can change code that depends on those features.

Short arrows are especially readable as callbacks. [1, 2, 3].map(n => n * 10) passes a function that returns a transformed element, producing [10, 20, 30]. Changing it to n => { n * 10; } silently loses each returned value.

Key Takeaways

  • A concise arrow body returns its expression automatically.
  • A block arrow body needs an explicit return to return a computed value.
  • Use parentheses around an object literal returned by a concise arrow.
  • Empty, multiple, default, and rest parameter lists need parentheses.
  • Arrows have no own this or arguments and cannot be constructors.
2

Pass values into functions and get results back

A function call has an input side and an output side. Match arguments to parameters, handle optional or flexible inputs, and trace the value returned to the caller. You will also distinguish local changes from changes to a shared array, and follow nested calls as their work pauses and resumes. The same call-and-return model explains simple recursion, where several calls of one function wait for smaller calls to finish.

2.1 Match arguments to parameters

A function's parameters give names to the values supplied by a call. To trace a call, match inputs by position first, then follow the operations in the body.

Match positions and types
function area(width, height) {
  return width * height;
}

area(4, 2.5);

width and height are parameters; 4 and 2.5 are arguments. During this call, width holds 4 and height holds 2.5, so the result is 10. Parameter names do not match caller variable names. With divide(a, b), swapping divide(10, 2) to divide(2, 10) changes the result from 5 to 0.2.

JavaScript does not enforce the number of arguments. area(4) leaves height as undefined; multiplying by it produces NaN. area(4, 2, 99) returns 8 because this body has no use for the third argument. A function can choose to collect extra arguments, but unused extras do not cause an automatic error.

Types also matter even when the call is accepted. area("4", 2) returns 8 because multiplication converts the text. An addition function behaves differently: add("4", 2) returns "42" when its body is return a + b;. If text represents a number, add(Number("4"), 2) returns 6.

Reassign a local parameter

Each parameter is local to its call. Assigning a new number to it does not reassign the caller's variable:

let score = 5;
function reset(s) {
  s = 0;
}
reset(score);

The call copies the value 5 into s. The assignment changes s to 0, and score remains 5. Numbers, strings, and booleans follow this value-copy rule. Code outside cannot read s directly; a nested function can retain access to it through a closure.

Change an array through a copied reference

An array value is a reference to an array. Passing that value copies the reference, so the caller and parameter can reach the same array:

const list = [1, 2];
function addItem(arr) {
  arr.push(3);
}
addItem(list);

list is now [1, 2, 3]. The function did not reassign list; it changed the array that both names reach. A const declaration prevents replacing its stored reference, not changing the array's contents.

Compare a local reassignment:

function clearLocal(arr) {
  arr = [];
}
clearLocal(list);

This points only arr at a new empty array. list still reaches [1, 2, 3]. Ask whether an operation changes an existing array, as push does, or gives a name a different value, as = does.

Return a new array when the input should stay intact
function withItem(arr, item) {
  return arr.concat(item);
}

withItem([1, 2], 3) produces [1, 2, 3] as a new array. The input array remains [1, 2]. This makes the function useful when the caller needs both the old list and the extended list. Document a function's intended input changes through its name and behavior so callers can use it predictably.

Key Takeaways

  • Arguments match parameters by position.
  • A missing argument supplies undefined; unused extra arguments are allowed.
  • Reassigning a parameter does not reassign the caller's variable.
  • Mutating an array through a parameter changes the shared array.
  • Returning a new array can preserve the input.

2.2 Set default values and accept any number of arguments

Defaults make an input optional; rest parameters let a call supply many inputs. Spread syntax connects an existing array to a function that expects separate arguments.

Apply a default only for undefined
function greet(name = "friend") {
  return "Hello, " + name;
}

greet() and greet(undefined) both return "Hello, friend". greet(null) returns "Hello, null", and greet("") returns "Hello, ". A default tests for undefined, not for every value that looks empty.

Defaults are evaluated when a call needs them. They may use earlier parameters: function makeBox(width, height = width) { return width * height; } returns 9 for makeBox(3) and 12 for makeBox(3, 4). The explicit second argument replaces the default for that call.

Put required inputs first. With function total(amount, rate = 0.25) { return amount + amount * rate; }, total(80) returns 100 and total(80, 0.5) returns 120. Reversing the parameters would make the common default-rate call require total(undefined, 80).

Older code may use level = level || 50;. This also replaces 0, false, and an empty string because they are falsy. A default parameter preserves those values: function setVolume(level = 50) { return level; } returns 0 for setVolume(0) and 50 for setVolume(undefined).

Gather arguments with rest
function sum(...numbers) {
  let total = 0;
  for (const n of numbers) {
    total += n;
  }
  return total;
}

sum(1, 2, 3) puts [1, 2, 3] into numbers and returns 6. sum() gives it an empty array, so the loop adds nothing and returns 0. A rest parameter is a real array, ready for array methods or iteration.

A fixed input can come before rest: in function log(label, ...values) { console.log(label, values); }, log("scores", 7, 9) gives label the string "scores" and values the array [7, 9]. Only one rest parameter is allowed, and it must be last; otherwise JavaScript cannot assign the remaining arguments unambiguously using this syntax.

Expand arguments with spread
Location of ...JobExample
Definition's parameter listGather remaining argumentsfunction sum(...numbers) { ... }
Call's argument listExpand array elementssum(...values)

Math.max(...[3, 7, 2]) receives three numbers and returns 7. Math.max([3, 7, 2]) receives one array and returns NaN. The three dots are identical on the page, but the surrounding syntax tells you which job they perform.

Ordinary functions also have an arguments object containing the arguments passed to them. It is array-like, with indexed entries and a length, but it is not a real array and has no own map or filter methods. Arrows have no arguments of their own. Prefer a rest parameter for new code that needs a flexible list of inputs.

Key Takeaways

  • Defaults apply to missing arguments and explicit undefined, while preserving 0 and null.
  • A default expression is evaluated for each call that needs it.
  • One rest parameter gathers the remaining arguments into an array and comes last.
  • Spread expands an array into separate call arguments.
  • The older arguments object is array-like; rest produces a real array.

2.3 Return results from a function

A returned value is the output other code receives from a call. Printing can show the same value on screen, but it gives the caller a different result unless the function also returns it.

Use the call as a value
function double(n) {
  return n * 2;
}
const x = double(5) + 1;

double(5) returns 10, so the assignment evaluates as 10 plus 1 and stores 11. A call can appear anywhere an appropriate value is needed. double(double(3)) first gets 6 from the inner call, then returns 12 from the outer one. Math.round(double(2.3)) doubles to 4.6 and rounds to 5.

return also ends the current call immediately. A guard clause handles a special case before the main work:

function safeDivide(a, b) {
  if (b === 0) {
    return null;
  }
  return a / b;
}

safeDivide(6, 0) returns null and never reaches the division. safeDivide(6, 2) skips the guard and returns 3. Put the condition before the operation it protects.

Separate output from display
function addLog(a, b) {
  console.log(a + b);
}

addLog(2, 3) prints 5, but its return value is undefined. Consequently, addLog(2, 3) * 2 prints 5 and evaluates to NaN. A function that reaches its end without a return, or executes a bare return;, returns undefined. The console display is a side effect, not the call's usable numeric output.

If a caller needs a sum, write return a + b;. The caller can then choose to print it, store it, or use it in another calculation. That keeps display decisions out of a reusable calculation.

Keep the returned expression on the same line
function brokenAdd(a, b) {
  return
  a + b;
}

JavaScript ends the return statement at the line break. The function returns undefined; the later expression is never reached. For a long expression, start it or open a parenthesis on the return line, then continue below.

Package several results into one value

A call returns one value, but that value can be an array:

function minMax(nums) {
  return [Math.min(...nums), Math.max(...nums)];
}
const [low, high] = minMax([4, 9, 1]);

The returned array is [1, 9]. Destructuring assigns its first entry to low and its second to high. There is still one returned value, containing two useful results.

A pure function computes its output from its arguments and changes nothing outside itself. double is pure. Printing, updating an outer variable, and mutating an input array are side effects. Pure calculations are easy to check with input-output examples; side effects belong in clearly named functions where the program needs visible changes.

Key Takeaways

  • return supplies the call's value and ends the current call.
  • Printing alone leaves the function's return value as undefined.
  • Start the returned expression on the return line.
  • One returned array can carry several results.
  • Pure functions depend on their arguments and make no outside changes.

2.4 Follow function calls through the call stack

When one function calls another, the caller pauses until that call returns. The call stack records the unfinished work so execution can resume in the right place.

Add and remove call frames

Each active call has a frame holding its parameters, local variables, and return location. A new call goes on top of the stack; a return removes that top frame.

function square(n) {
  return n * n;
}
function sumOfSquares(a, b) {
  return square(a) + square(b);
}
sumOfSquares(3, 4);

First, sumOfSquares begins with a equal to 3 and b equal to 4. It calls square(3), which returns 9 and finishes. Then square(4) runs and returns 16. The paused addition can now finish as 9 plus 16, and sumOfSquares returns 25. The two square calls do not overlap in this trace.

Each call gets fresh parameters and local variables. Calling the same function twice does not reuse a previous call's local numeric state. Even when a function calls itself, the new call has its own frame.

Reduce a problem with recursion

A recursive function calls itself. A base case finishes without another self-call; a recursive case takes a step toward that base case.

function factorial(n) {
  if (n <= 1) {
    return 1;
  }
  return n * factorial(n - 1);
}

For nonnegative integer inputs, this multiplies the integers down to 1. Trace factorial(4) in two directions:

  1. factorial(4) waits for factorial(3).
  2. That call waits for factorial(2).
  3. That call waits for factorial(1).
  4. The base case returns 1.
  5. The waiting calls return 2, then 6, then 24.

The multiplication in the call with n equal to 4 happens after the smaller call returns. Its own n has stayed 4 while it waited.

A base case must be reachable. Using only n === 1 fails for a starting input of 0 if every recursive call subtracts 1: the values become negative and never reach 1. n <= 1 catches both 0 and 1. Without a stopping path, repeated calls exhaust the stack. Chrome and Node.js report "RangeError: Maximum call stack size exceeded"; Firefox uses "InternalError: too much recursion".

Recognize a smaller copy of the same task
function countItems(list) {
  let count = 0;
  for (const item of list) {
    if (Array.isArray(item)) {
      count += countItems(item);
    } else {
      count += 1;
    }
  }
  return count;
}

countItems([1, [2, 3], [4, [5]]]) returns 5. Each nested array asks the same counting question of a smaller list. A loop is also suitable for a simple factorial; recursion is especially useful when the data itself is nested.

If a call fails, read the stack trace from the most recent call at the top. The following entries show which unfinished callers led there. Repeated entries for one function can reveal recursion that failed to stop.

Key Takeaways

  • The call stack tracks unfinished calls; returns remove the most recent frame.
  • Each invocation has its own parameters and local variables.
  • Recursion needs a stopping case and progress toward it.
  • Recursive results return through the waiting calls in reverse order.
  • A stack trace shows the failing call and the callers that led to it.
  • Nested data can contain smaller instances of the same task.
3

Explain where a variable can be used

Variable names are meaningful within scopes. Block boundaries, function boundaries, and the place a function is defined determine which declarations it can reach. You will trace those boundaries to predict a value or an error, then distinguish shadowing from updating an outer variable. Hoisting and the temporal dead zone explain why a name can be registered before execution reaches its line yet still be unavailable to use.

3.1 Tell block, function and global scope apart

A variable's scope is the region where its name can be used. Scope lets functions keep their working data separate while still using inputs and values from surrounding code.

Draw boundaries around declarations

let and const are block-scoped. A block is a region inside curly braces, such as an if body, a loop body, or a function body:

if (true) {
  const msg = "hi";
  console.log(msg);
}

The print works inside the block. Adding console.log(msg); after it raises a ReferenceError because that outer code cannot reach this declaration. A different outer declaration with the same name would be a different variable.

Parameters and variables inside a function are local to that function. In function f() { let temp = 1; }, outside code cannot read temp. Two functions can each have a local total without sharing it. Each call starts with fresh local variables; a returned nested function can later preserve access through a closure.

Read older var declarations
DeclarationBoundary
let or const inside a blockThat block
var inside a functionThe enclosing function, even when written in an inner block
Top-level declaration in a regular scriptThe script's global scope
Top-level declaration in a moduleThat module
function test() {
  if (true) {
    var x = 1;
  }
  return x;
}

test() returns 1 because var ignores the if boundary. Replacing it with let x = 1; makes the return fail: the block's x is unavailable there. Similarly, for (let i = 0; i < 3; i++) {} keeps i within the loop, while a var i remains available in its enclosing function or script after the loop and holds 3.

Look outward from the function's written position

Scopes nest. An inner function can use names in its enclosing scopes, but outer code cannot search inward into an inner function's locals. This is lexical scope: where the code is written determines the available names.

const level = "global";
function show() {
  return level;
}
function run() {
  const level = "local";
  return show();
}

run() returns "global". show was defined outside run, so its enclosing scope contains the outer level. Calling it from inside run does not give it access to that caller's local level.

Keep shared state deliberate

A top-level variable in a regular script is global and can be reached by functions within that script. Shared globals can make independent tasks affect one another; scripts on the same page can also clash over names. Passing inputs and returning outputs keeps dependencies visible.

Declare every variable. In strict mode, count = 5; throws a ReferenceError if no accessible declaration exists. Without strict mode, such an assignment can create an accidental global. A script can enable strict mode with "use strict"; at its start; modules enable it automatically. Prefer const for values that are not reassigned and let when reassignment is needed.

Key Takeaways

  • let and const obey block boundaries; var obeys the enclosing function boundary.
  • Function parameters and local variables are unavailable to outside code.
  • Lexical scope follows where a function is defined, not where it is called.
  • Top-level module variables belong to the module.
  • Declare names explicitly to prevent accidental global assignments.

3.2 Predict which variable a name refers to

The same name can occur in several scopes. JavaScript finds the relevant declaration by looking outward from the current code, stopping at the first match.

Follow the scope chain
let x = 1;
function f() {
  let x = 2;
  return x;
}

f() returns 2. The local declaration shadows the outer x, so the search stops inside f. The outer value stays 1. A parameter can shadow a name in the same way. If no scope on the chain declares a name, reading it raises a ReferenceError.

An assignment does not declare a new variable. With let count = 0; function add() { count = count + 1; }, add finds the outer declaration and changes it. Inserting let changes the meaning: it creates a new local name, which may hide the very value you meant to read.

Separate registration from initialization

Hoisting describes how declarations are registered before their scope runs. Their early behavior differs:

DeclarationBefore its initialization line
Function declarationIts function is available
varIts value is undefined
let or constAccess raises a ReferenceError

For example, console.log(a); var a = 5; prints undefined. The assignment to 5 has not happened yet. console.log(b); let b = 5; fails instead. The interval when the lexical name exists but cannot be accessed is its temporal dead zone, often shortened to TDZ.

Trace shadowing before a declaration line
let n = 10;
function f() {
  console.log(n);
  let n = 20;
}
f();

The inner n shadows the outer one throughout the function body, including before the declaration's written line. At console.log(n), that inner name is still in its TDZ, so the call throws a ReferenceError. JavaScript does not skip an uninitialized local declaration and fall back to the outer value 10.

The same problem appears in let count = count + 1; inside add. The right side reads the new local count before it has been initialized. If the intention is to update an outer count, use an assignment without a new declaration. If the intention is to calculate a separate value, give it a different name and read the outer count explicitly.

Distinguish reassignment from mutation

let and const cannot redeclare the same name in one scope; doing so is a SyntaxError. An inner scope may declare its own name, which is shadowing. var allows repeated declarations in its scope, making accidental name reuse harder to notice.

const blocks reassignment of its variable. It does not freeze an array:

const list = [];
list.push(1);

The reference stored in list stays the same while the array changes to [1]. list = []; would try to replace the stored reference and would fail.

Keep a declaration in the smallest scope that needs it. Distinct names for distinct jobs reduce the need to mentally track shadowing, and initializing before use avoids TDZ failures.

Key Takeaways

  • Name lookup stops at the nearest enclosing declaration.
  • A local declaration shadows an outer name even before initialization.
  • var begins as undefined; let and const are inaccessible in their TDZ.
  • Assignment updates an existing binding; declaration creates one.
  • const prevents reassignment, while array contents can still change.
  • Redeclaration in one scope differs from shadowing in a nested scope.
4

Use closures to give functions their own memory

A returned function can keep access to the variables around its definition. That retained connection gives a counter memory and lets a factory build functions with their own settings. You will trace separate factory calls, shared state, and later changes to captured variables, then use the same rules to explain functions created in a loop. Private state follows from ordinary lexical scope, even after the outer call has returned.

4.1 Explain what a closure remembers

A function keeps a link to the scope where it was created. The function and that retained access form a closure. The link becomes especially useful when a nested function is returned and called after its outer call has finished.

Give a returned function memory
function makeCounter() {
  let count = 0;
  return function () {
    count = count + 1;
    return count;
  };
}
const counter = makeCounter();

The call to makeCounter creates count, creates an inner function, and returns that function. It does not run the inner body yet. counter now holds the function value. Its first call changes the retained count from 0 to 1 and returns 1. Its next call changes the same variable to 2 and returns 2.

The outer call's stack frame has finished, but the captured variable remains accessible. A local variable that no longer has any reachable use can be released; here the callable inner function still needs it. Closure access survives a return without making the variable global.

Create independent memory with separate calls
const a = makeCounter();
const b = makeCounter();
a();
a();
b();

The results are 1, 2, and 1. Each factory call created a different count. The two functions returned from separate calls have the same code but different retained environments.

Copying a returned function reference works differently. If const alias = a;, then alias() uses a's existing count; it does not call makeCounter and create another environment.

Keep variables, not snapshots
let greeting = "Hi";
const say = () => greeting;
greeting = "Hello";

say() returns "Hello". The closure reads the variable when it runs, so a later update is visible. It does not store a frozen copy of "Hi" at creation time.

Outside code cannot access the local count directly, and counter.count is undefined. Only code with access to that lexical environment can read or update it. Calling the counter from a different function does not change which count it uses.

Share state within one outer call
function makeWallet() {
  let balance = 0;
  return [
    function deposit(n) { balance += n; return balance; },
    function getBalance() { return balance; }
  ];
}
const [deposit, getBalance] = makeWallet();
deposit(50);
deposit(20);

getBalance() returns 70 because both returned functions were created in the same call and refer to the same balance. Another call to makeWallet() creates a separate balance.

Key Takeaways

  • A closure retains access to the scope where its function was created.
  • A captured variable can remain usable after its outer call returns.
  • Separate factory calls create separate environments.
  • Closures read current variable values, not creation-time snapshots.
  • Functions from the same outer call can share private state.

4.2 Use closures for function factories and private state

A function factory uses closure access to create functions with settings or state of their own. The caller supplies the configuration once, then uses the returned function many times.

Configure a function with a parameter
function makeMultiplier(factor) {
  return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);

double(5) returns 10 and triple(5) returns 15. Each returned arrow retains the factor from its own factory call. The outer function configures the behavior; the inner one performs the repeated work.

The compact form is const makeMultiplier = (factor) => (n) => n * factor;. Two arrows mean two function creations, not two consecutive calculations on one number. makeMultiplier(4)(5) first returns a function configured with 4, then calls it with 5 to get 20.

Enforce a rule around private state
function makeLimiter(max) {
  let used = 0;
  return function () {
    if (used >= max) {
      return false;
    }
    used = used + 1;
    return true;
  };
}
const tryLogin = makeLimiter(3);

The first three calls return true and increment used. The fourth returns false before incrementing. used is local to the factory call, so outside code cannot reset it through that name. The returned function controls which changes are available. Another limiter would have its own used variable and its own maximum.

Diagnose functions created in loops
const fns = [];
for (var i = 0; i < 3; i++) {
  fns.push(() => i);
}

The loop creates three functions but only one var i. After the loop, that variable holds 3. Calling any stored function now returns 3 because every closure reads the same current binding. Creating a function does not evaluate its body or snapshot the current i.

Changing the header to let i = 0 gives the loop a separate binding for each iteration. The stored functions then return 0, 1, and 2. The same distinction explains delayed printing: a setTimeout callback that reads var i after the loop sees the final value, while the let version retains each iteration's value.

Recognize an immediately invoked expression

Older code often creates a private function scope and runs it immediately:

(function () {
  var hidden = 1;
  console.log(hidden);
})();

This is an immediately invoked function expression, or IIFE. The outer parentheses make a function expression; the final parentheses call it. hidden belongs to that function, keeping it out of the surrounding global scope. It is a common pattern in code written before block-scoped declarations and modules.

Retained access also affects memory. A callable closure that needs a large array keeps that data reachable. When the function and its captured data are no longer reachable, they can be released. Store long-lived closures deliberately when their memory is part of the task.

Key Takeaways

  • Factories return functions configured by their arguments.
  • Private variables can be changed through the returned operations.
  • var loop closures share one binding; let loop iterations get separate bindings.
  • An IIFE creates a function scope and runs it immediately.
  • Reachable closures keep the captured data they need reachable.
5

Pass functions to other functions

Passing a function lets a caller choose part of another function's behavior. You will inspect how receivers invoke callbacks, which arguments they supply, and how they use the result. Familiar array methods then become examples of reusable higher-order patterns. Finally, returned functions will wrap calls, connect outputs to inputs, or hold selected arguments ready for later use, combining function values with closure access.

5.1 Pass a callback to a function

A callback is a function supplied as an argument for another function to call. It lets the receiving function handle the overall task while the caller supplies a particular action or test.

Pass an action to a loop
function repeat(times, action) {
  for (let i = 0; i < times; i++) {
    action(i);
  }
}
repeat(3, (i) => console.log(i));

repeat owns the loop and chooses the values 0, 1, and 2. The arrow owns what to do with each value: print it. Each action(i) calls the supplied function with that iteration's number.

You can pass an existing function or write one inline. repeat(2, sayHi) passes the named function. repeat(2, (i) => console.log("Round " + (i + 1))) prints "Round 1" and "Round 2". A callback may ignore arguments it does not need.

Pass the value, not an early call

repeat(3, sayHi()) evaluates sayHi() while preparing the arguments. If sayHi only prints, its return value is undefined, so repeat receives that value as action. The later action(i) fails with a TypeError because undefined is not callable.

Use repeat(3, sayHi) to hand over the function. If the eventual call needs chosen arguments, pass a wrapper such as () => sayHi() so the call happens inside the callback body. Function creation and function invocation are separate steps.

Check what arguments the receiver supplies

Array callbacks often receive more than one argument: element, index, and the whole array. Passing an existing function is safe only when those inputs fit its parameter meanings.

["1", "2", "3"].map(parseInt);

This returns [1, NaN, NaN]. parseInt accepts a second argument for the number base, so the array index changes its behavior. Use ["1", "2", "3"].map((s) => parseInt(s, 10)) to choose base 10 explicitly, or .map(Number) to convert these strings.

Return an answer to the receiver
function countIf(arr, test) {
  let n = 0;
  for (const x of arr) {
    if (test(x)) {
      n++;
    }
  }
  return n;
}

countIf([3, 8, 12, 5], (x) => x > 4) returns 3. The callback answers a question for each item; countIf uses each answer to decide whether to increment. Compare repeat, whose loop ignores the callback's return value. The receiving function's code or documentation tells you both the arguments and the role of the result.

Callbacks can run immediately or later. repeat and array methods call them synchronously before returning. setTimeout(sayHi, 2000) schedules one for later while following code continues immediately. Being a callback describes how a function is supplied, not when it must run.

Key Takeaways

  • A callback is a function passed for another function to call.
  • The receiver controls callback arguments and whether its result is used.
  • Adding call parentheses too early can pass a result instead of a function.
  • Array callback arguments can change the behavior of an existing function.
  • Callbacks can be synchronous or delayed.

5.2 Use and write higher-order functions

A higher-order function accepts a function, returns a function, or does both. The function value supplies behavior, while the higher-order function manages a reusable pattern.

Write the pattern once
function myMap(arr, transform) {
  const result = [];
  for (const item of arr) {
    result.push(transform(item));
  }
  return result;
}

myMap([1, 2, 3], (n) => n * n) returns [1, 4, 9]. The loop and output-array construction stay the same; the callback decides how each entry changes. A matching filter pushes an item only when test(item) is truthy. With [4, 7, 10, 3] and (n) => n % 2 === 0, that pattern keeps [4, 10].

Choose the callback contract you need
MethodCallback's jobMethod's result
mapReturn each transformed valueA new array of the same length
filterReturn a truthy value for items to keepA new array of selected items
findTest for a matching itemFirst match, or undefined
forEachPerform an action for each itemundefined
reduceReturn the next accumulatorThe final accumulator

map, filter, and find do not themselves modify the input array, but callbacks can still mutate it. Returning a new array from a method does not make every supplied callback pure.

Carry a result through a reduction
[5, 10, 15].reduce((total, n) => total + n, 0);

The starting accumulator is 0. The callback returns 5 for the first item, 15 for the second, and 30 for the third. Each return becomes the next call's total. The method returns 30 after the last item. An empty array without a starting accumulator makes reduce throw a TypeError; an explicit starting value also defines the useful empty-input result.

Returning arrays makes chaining possible. [3, 8, 12, 5].filter((n) => n > 4).map((n) => n * 2) first keeps [8, 12, 5], then transforms it into [16, 24, 10]. Read the chain in execution order from left to right.

Supply a numeric sort rule

Without a comparator, sort compares entries as strings. [10, 9, 1].sort() therefore produces [1, 10, 9]. For ascending numeric order, use .sort((a, b) => a - b). A negative return puts a first, a positive return puts b first, and zero leaves the pair equivalent for ordering. Unlike map and filter, sort rearranges its input array.

Check the returned values

[1, 2, 3].map((n) => { n * 2; }) yields [undefined, undefined, undefined] because the block has no return. Using forEach to build a new result by assignment also fails if you expect the method itself to return an array. Choose map for transformation and forEach for actions whose collected outputs are not needed.

A higher-order method states a familiar pattern clearly. A plain loop remains useful when work needs break or does not fit the method's contract.

Key Takeaways

  • Higher-order functions take functions, return functions, or both.
  • map, filter, and find use callback results in different ways.
  • forEach returns undefined; reduce returns the final accumulator.
  • A reduction callback must return the next accumulator.
  • Numeric sorting needs a comparator and mutates the original array.
  • A method that returns a new array can still run callbacks with side effects.

5.3 Build new functions from existing ones

A function can return a new function that remembers an existing one. Wrappers, composition, and partial application use this pattern to adapt behavior without copying the original function's body.

Wrap a call and preserve its result
function logged(fn) {
  return (...args) => {
    console.log("Calling with", args);
    const result = fn(...args);
    console.log("Result", result);
    return result;
  };
}
const add = (a, b) => a + b;

logged(add) creates a function that retains add as fn. Calling logged(add)(2, 3) gathers the arguments, forwards them to add, prints the returned value, and returns 5. Rest gathers the wrapper's inputs; spread forwards them. The final return keeps the output available to later calculations.

logged(add(2, 3)) passes the number 5 instead. Calling that wrapper later tries to call 5 and throws a TypeError. There are two stages: configure the wrapper with a function, then call the returned function with inputs.

Retain state around an operation
function once(fn) {
  let done = false;
  let result;
  return (...args) => {
    if (!done) {
      done = true;
      result = fn(...args);
    }
    return result;
  };
}

For an operation that returns normally, the first call runs fn and stores its result. Later calls return that stored value without rerunning fn, even if their arguments differ. The boolean tracks whether the operation ran; checking the result's truthiness would be wrong when a valid result is 0 or false. Each call of once creates its own flag and stored result.

Feed one result into another function
const compose = (f, g) => (x) => f(g(x));
const addOne = (n) => n + 1;
const square = (n) => n * n;

compose(square, addOne)(3) runs addOne(3) first, then squares 4 to return 16. compose(addOne, square)(3) squares 3 first and then adds 1, returning 10. The function nearest the input runs first, so the order of the arguments to compose matters.

Fix some arguments in advance
function partial(fn, ...fixed) {
  return (...rest) => fn(...fixed, ...rest);
}
const greetWith = (greeting, name) => greeting + ", " + name;
const hello = partial(greetWith, "Hello");

hello("Ana") calls greetWith("Hello", "Ana") and returns "Hello, Ana". This partial helper puts fixed arguments first and later arguments afterward; changing that order changes the underlying call.

Key Takeaways

  • A wrapper retains a function and adds behavior around its call.
  • Rest and spread let a wrapper forward separate arguments.
  • Returning the wrapped result keeps it usable by the caller.
  • A run-once wrapper retains both a flag and the first result.
  • compose(f, g) applies g before f.
  • Partial application fixes selected arguments before later calls.

JavaScript Functions examples

5 Walkthroughs

1

Create, store, and call a function

11 steps
Follow a reusable greeting through declarations, function values, and arrow bodies.
  1. 1
    IntroductionSeparate the recipe from the call

    A function stores instructions so several calls can reuse them. Its definition describes a task; a call performs that task with particular inputs.

    Follow a greeting through three forms: a declaration, an expression stored in a variable, and an arrow. Watch two details as the syntax changes: when the function becomes available and whether its body sends a value back.

  2. 2
    ExampleDefine a greeting
    function greet(name) {
      console.log("Hello, " + name + "!");
    }
    

    The keyword, name, parameter list, and body form a declaration. Nothing has printed yet: the definition stores the instructions.

    name is a parameter whose value will come from a call. The body can be reused because it describes how to build the greeting without choosing one person's name in advance.

  3. 3
    ExampleRun the stored instructions
    greet("Amira");
    greet("Li");
    

    The calls print "Hello, Amira!" and "Hello, Li!" on separate lines. Each starts the same body again with its own input.

    The expression greet alone is the function value. The parentheses in greet("Li") request execution. Even a zero-input function needs call parentheses, as in sayHi().

  4. 4
    ChallengePredict the early call

    What prints when this script runs?

    show();
    function show() { console.log("Ready"); }
    

    ---reveal---

    It prints "Ready" once. The declaration is hoisted within its scope, so the function is available at the first line. Hoisting does not run the body by itself: the explicit show() call does. A line containing only show; would read the function without printing.

  5. 5
    ExplanationKeep the function as a value
    const square = function (n) {
      return n * n;
    };
    const alias = square;
    

    The expression creates a function, and square stores it. The second assignment copies its reference into alias; it does not calculate a square. Both square(3) and alias(3) return 9.

    By comparison, const answer = square(3); calls the function and stores the returned number.

  6. 6
    ChallengeLocate the first failure

    Does this script print 16?

    console.log(square(4));
    const square = function (n) { return n * n; };
    

    ---reveal---

    No. Reading square before its const initialization raises a ReferenceError. The assignment has not run, so the expression's function value is unavailable. Move the call below the assignment, or use a function declaration if the function needs to be callable earlier in this scope.

  7. 7
    ExampleUse a concise arrow
    const square = (n) => n * n;
    const add = (a, b) => a + b;
    

    A concise arrow body returns its expression, so square(4) returns 16 and add(2, 3) returns 5.

    One simple parameter may omit parentheses: n => n * n. Empty lists, multiple parameters, defaults, and rest parameters need them. Arrows are expressions, so these const variables still need initialization before use.

  8. 8
    ChallengeRepair a missing result

    Why does add(2, 3) return undefined, and what change makes it return 5?

    const add = (a, b) => { a + b; };
    

    ---reveal---

    The braces begin a block, so the expression is evaluated but not returned. Write (a, b) => { return a + b; }, or remove the braces to use (a, b) => a + b. Both repairs return the sum. Merely adding a semicolon after the assignment does not change the body's return behavior.

  9. 9
    TipReturn an object expression
    const makePoint = (x, y) => ({ x: x, y: y });
    

    Parentheses make the object literal an expression returned by the concise body. An unparenthesized opening brace would begin a block instead.

    Arrows have no own this or arguments, and they cannot be constructors. The short form works well for simple calculations, but those differences matter when converting other kinds of functions.

  10. 10
    ChallengeFollow a function and its alias

    What does copy(5) return?

    const adjust = (n) => { return n + 2; };
    const copy = adjust;
    

    ---reveal---

    It returns 7. The arrow's block contains an explicit return, and copy holds the same callable function as adjust. The assignment did not call the function or store a numeric result. Replacing const copy = adjust; with const copy = adjust(5); would store 7, which is not callable.

  11. 11
    RecapRead the operation on each line

    A declaration creates a named task that is available early in its scope. An expression creates a function value when execution reaches it. An arrow's concise body returns automatically; its block body needs return.

    When tracing code, distinguish a function definition, a reference assignment, and a call. Those are different operations even when they use the same name.

2

Trace inputs, outputs, and waiting calls

16 steps
Match arguments, follow returned values, and unwind a short recursive calculation.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionTrack what enters and leaves a call
  2. 2
    ExampleMatch arguments by position
  3. 3
    ExampleDistinguish mutation from reassignment
  4. 4
    ChallengeKeep the original input
  5. 5
    ExplanationDefault means undefined
  6. 6
    ExampleGather, then expand
  7. 7
    ChallengeUse a default with rest
  8. 8
    ExampleReturn the number the caller needs
  9. 9
    ExampleStop a path with an early return
  10. 10
    ChallengeEvaluate from the inside out
  11. 11
    ExampleRemember the unfinished caller
  12. 12
    ExplanationGive recursion a stopping path
  13. 13
    ExampleUnwind the waiting products
  14. 14
    ChallengeRepair a recursive boundary
  15. 15
    ConnectionRead failure through the stack
  16. 16
    RecapFollow a value through each boundary
3

Find the declaration behind a name

10 steps
Trace block boundaries, lexical lookup, and initialization timing.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionAsk where the name belongs
  2. 2
    ExampleCompare block and function boundaries
  3. 3
    ExampleUse the definition location
  4. 4
    ChallengeTrace a local and an outer value
  5. 5
    ExplanationStop at the nearest declaration
  6. 6
    Key InsightRegistered is not initialized
  7. 7
    ChallengePredict a shadowed read
  8. 8
    TipKeep reassignment separate from mutation
  9. 9
    ChallengeCombine a callback with lookup timing
  10. 10
    RecapTrace location before value
4

Give returned functions their own memory

11 steps
Trace shared and independent closure state, then inspect functions created inside a loop.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionKeep access after a return
  2. 2
    ExampleCreate a counter without calling it
  3. 3
    ExampleUpdate the retained variable
  4. 4
    ChallengeCompare independent counters
  5. 5
    Key InsightRetain the binding, not a snapshot
  6. 6
    ExampleCreate a configured calculation
  7. 7
    ExampleKeep a private use count
  8. 8
    ChallengeExplain three equal results
  9. 9
    ConnectionRecognize private scope in older code
  10. 10
    ChallengeShare one configured operation
  11. 11
    RecapTrack how many environments exist
5

Pass behavior and build adapted functions

15 steps
Trace callback arguments, array method results, and functions that return configured functions.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionLet a function supply the changing behavior
  2. 2
    ExampleCall the supplied action
  3. 3
    ChallengeUse a callback result
  4. 4
    TipInspect all callback arguments
  5. 5
    ExampleWrite a transformation pattern
  6. 6
    ExplanationChoose the result shape
  7. 7
    ChallengeTrace a selection and a transformation
  8. 8
    Key InsightKeep numeric order deliberate
  9. 9
    ExampleWrap and forward a call
  10. 10
    ExplanationRemember whether a call has run
  11. 11
    ExampleCompose from the inside out
  12. 12
    ExampleFix inputs before the later call
  13. 13
    ChallengePass a configured callback
  14. 14
    ChallengeRepair a lost wrapper result
  15. 15
    RecapCheck both stages of higher-order code

JavaScript Functions practice questions

50 Practice Questions in 5 sets

Set 1: Choose inputs and trace retained state10 Practice Questions

Question 1 of 10

What happens when this script runs?

announce();
function announce() { console.log("Open"); }
  1. It throws a ReferenceError.
  2. It prints undefined once.
  3. It prints "Open" once.
  4. It throws a TypeError.
Show answerHide answer
Answer: C
The declaration is hoisted within the script's scope, so announce is already a function when the first line calls it. Its body prints "Open" once. The initialization timing of a function expression stored in const would be different, but this code uses a declaration.

Question 2 of 10

How can an arrow return an object with a value property equal to its input n?
  1. (n) => ({ value: n })
  2. (n) => { return; value: n; }
  3. (n) => (value = n)
  4. (n) => { value: n }
Show answerHide answer
Answer: A
The parentheses make { value: n } an object expression, which the concise arrow body returns. Without those parentheses, the braces start a block and value: n is read as a labeled statement, leaving no returned object. A bare return; also returns undefined.

Question 3 of 10

What does the call return?

function combine(first, ...rest) {
  return first + rest.length;
}
const values = [8, 2, 6];
combine(...values);
  1. 16
  2. 8
  3. 11
  4. 10
Show answerHide answer
Answer: D
Spread passes three separate arguments. The fixed parameter gets 8, and rest gathers [2, 6], whose length is 2, so the function returns 10. Counting all three values in rest would give 11, but the first argument has already been assigned to first.

Question 4 of 10

What does guarded(0) return?

function guarded(n) {
  if (n === 0) { return "empty"; }
  return "filled";
}
  1. "filled"
  2. "empty"
  3. ["empty", "filled"]
  4. undefined
Show answerHide answer
Answer: B
The condition is true for 0, so the first return immediately ends the call with "empty". Execution does not continue to the later return. A function returns one value for a particular call; two return statements describe different possible paths, not a collected list of outputs.

Question 5 of 10

What does totalDown(3) return?

function totalDown(n) {
  if (n === 0) { return 0; }
  return n + totalDown(n - 1);
}
  1. 0
  2. 6
  3. 9
  4. 3
Show answerHide answer
Answer: B
The calls reach totalDown(0), which returns 0. The waiting calls then return 1, 3, and 6. The base-case return does not replace every call's result with 0; each waiting frame still has its own n and must finish its pending addition.

Question 6 of 10

What happens when read() is called?

let label = "outer";
function read() {
  return label;
  let label = "inner";
}
  1. It returns the string "inner".
  2. It returns the string "outer".
  3. It throws a ReferenceError.
  4. It returns the value undefined.
Show answerHide answer
Answer: C
The local declaration shadows the outer label throughout read, including at the earlier return. That local is still uninitialized, so reading it throws a ReferenceError. The unreachable assignment does not make JavaScript skip the local declaration and use the outer string.

Question 7 of 10

What does the final call return?

let word = "first";
const read = () => word;
word = "second";
function run() {
  const word = "third";
  return read();
}
run();
  1. "second"
  2. undefined
  3. "first"
  4. "third"
Show answerHide answer
Answer: A
read closes over the top-level word, whose current value is "second". It does not keep a frozen copy of "first", and calling it from run does not redirect its lookup to that caller's local "third". Both the definition location and the later assignment matter.

Question 8 of 10

What array does the final expression produce?

const readers = [];
for (let i = 1; i <= 3; i++) {
  let value = i;
  readers.push(() => value);
  value += 10;
}
readers.map((read) => read());
  1. [13, 13, 13]
  2. [1, 2, 3]
  3. [14, 14, 14]
  4. [11, 12, 13]
Show answerHide answer
Answer: D
Each iteration creates its own value, and its function retains that binding. The later addition in that same iteration changes the value the function will read to 11, 12, or 13. [1, 2, 3] would treat closure creation as a snapshot and ignore those later updates.

Question 9 of 10

What happens when the last line runs?

function speak() { console.log("Hi"); }
function twice(action) { action(); action(); }
twice(speak());
  1. It prints "Hi" once, then throws a TypeError.
  2. It throws a TypeError before printing anything.
  3. It prints "Hi" twice, then returns normally.
  4. It prints "Hi" three times, then returns normally.
Show answerHide answer
Answer: A
Preparing the argument calls speak immediately, so "Hi" prints once. Its return value is undefined, which becomes action; the first attempted action() then throws a TypeError. Passing speak without call parentheses would let twice invoke it two times.

Question 10 of 10

What is the final result?

const compose = (f, g) => (x) => f(g(x));
const subtract = (n) => n - 2;
const triple = (n) => n * 3;
const first = compose(triple, subtract);
const second = compose(subtract, triple);
first(second(4));
  1. 18
  2. 28
  3. 24
  4. 30
Show answerHide answer
Answer: C
second(4) triples 4 and subtracts 2, returning 10. That becomes the input to first, which subtracts 2 and triples 8, returning 24. A result of 18 applies first and second in the opposite outer order; each composition must also preserve its own inner-first call order.
Set 2: Read functions and follow their values10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

Given function showLabel() { console.log("Ready"); }, what statement calls the function?
  1. "showLabel";
  2. showLabel();
  3. showLabel;
  4. const label = showLabel;
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does copy hold after this code?

const triple = function (n) { return n * 3; };
const copy = triple;
  1. The value undefined until copy is called.
  2. A separate function with copied private state.
  3. The number returned by calling triple(3).
  4. A reference to the same function as triple.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What does adjust(8) return?

const adjust = (n) => { n - 3; };
  1. undefined
  2. 5
  3. 8
  4. NaN
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What does difference(12, 5) return?

function difference(first, second) {
  return first - second;
}
  1. 17
  2. undefined
  3. 7
  4. -7
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What does readLevel(0) return?

function readLevel(level = 40) { return level; }
  1. 40
  2. undefined
  3. null
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What are the printed number and the value of result?

function displayTotal(a, b) { console.log(a + b); }
const result = displayTotal(6, 9);
  1. Prints 15; result is 15.
  2. Prints 15; result is undefined.
  3. Prints nothing; result is 15.
  4. Prints undefined; result is 15.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What happens when getFlag() is called?

function getFlag() {
  if (true) { let flag = "on"; }
  return flag;
}
  1. It throws a ReferenceError.
  2. It throws a TypeError.
  3. It returns the value undefined.
  4. It returns the string "on".
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What values do the two calls return, in order?

function makeStep() {
  let value = 4;
  return () => { value += 2; return value; };
}
const step = makeStep();
step();
step();
  1. 4, 6
  2. 8, 10
  3. 6, 8
  4. 6, 6
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What is the complete sequence of printed values?

function deliver(action) { action(7); }
console.log("start");
deliver((n) => console.log(n));
console.log("end");
  1. "start", 7, "end"
  2. "start", undefined, "end"
  3. "start", "end", 7
  4. 7, "start", "end"
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What is the final value of result?

const values = [2, 7, 4, 9];
const result = values
  .filter((n) => n > 3)
  .map((n) => n - 1)
  .reduce((total, n) => total + n, 5);
  1. 27
  2. 22
  3. 17
  4. 21
Show answerHide answer

Save this lesson to see this answer.

Set 3: Connect calls, scope, and callbacks10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What happens when this script runs?

const saved = build;
const build = function () { return 9; };
console.log(saved());
  1. It throws a ReferenceError on the first line.
  2. It throws a TypeError on the final line.
  3. It prints undefined on the final line.
  4. It prints 9 after the assignment completes.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What value is assigned to out?

const describe = (n) => {
  const doubled = n * 2;
  return doubled + 1;
};
const out = describe(6);
  1. undefined
  2. 7
  3. 13
  4. 12
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What are items and answer after this code?

const items = [5];
function change(list) {
  list.push(8);
  list = [1];
  return list;
}
const answer = change(items);
  1. items is [5, 8]; answer is [5, 8].
  2. items is [5]; answer is [1].
  3. items is [1]; answer is [1].
  4. items is [5, 8]; answer is [1].
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What does countLeaves([0, [[], 2], [[3], 4]]) return?

function countLeaves(list) {
  let count = 0;
  for (const item of list) {
    if (Array.isArray(item)) {
      count += countLeaves(item);
    } else {
      count++;
    }
  }
  return count;
}
  1. 6
  2. 4
  3. 3
  4. 5
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What does readValue() return?

function readValue() {
  if (true) { var value = 14; }
  return value;
}
  1. 14
  2. undefined
  3. It throws a ReferenceError.
  4. It throws a TypeError.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What are the two values printed?

let count = 2;
function update() {
  let count = 7;
  count += 1;
  return count;
}
console.log(update());
console.log(count);
  1. 3, 3
  2. 7, 2
  3. 8, 2
  4. 8, 8
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What values do the last three calls return?

function makeCounter(start) {
  let n = start;
  return () => { n++; return n; };
}
const a = makeCounter(5);
const alias = a;
const b = makeCounter(5);
a();
alias();
b();
  1. 5, 6, 5
  2. 6, 7, 6
  3. 6, 7, 8
  4. 6, 6, 6
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What does scale(7) return?

const makeScale = (factor) => (n) => n * factor;
const scale = makeScale(4);
  1. 7
  2. 11
  3. A new function.
  4. 28
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What are values and first afterward?

const values = [12, 3, 20];
const first = values.sort((a, b) => a - b)[0];
  1. values is [12, 3, 20]; first is 3.
  2. values is [3, 12, 20]; first is 3.
  3. values is [20, 12, 3]; first is 20.
  4. values is [12, 20, 3]; first is 12.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What do the calls read(0) and read(9) return, in order?

function once(fn) {
  let done = false;
  let result;
  return (...args) => {
    if (!done) {
      done = true;
      result = fn(...args);
    }
    return result;
  };
}
const read = once((n) => n * 2);
  1. undefined, 18
  2. 0, 18
  3. 0, 0
  4. 0, undefined
Show answerHide answer

Save this lesson to see this answer.

Set 4: Diagnose function behavior in small programs10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A script defines function printLine() { console.log("---"); } and then runs printline();. What happens?
  1. The call returns undefined without printing.
  2. One line is printed and execution continues.
  3. A TypeError occurs and no line is printed.
  4. A ReferenceError occurs and no line is printed.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does the final call return?

const climb = function step(n) {
  return n === 0 ? 0 : 1 + step(n - 1);
};
const saved = climb;
saved(2);
  1. 0
  2. 2
  3. It throws a ReferenceError.
  4. 1
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What are data and result at the end?

const data = [2, 5];
function extend(list, item) {
  const next = list.concat(item);
  list.push(9);
  return next;
}
const result = extend(data, 7);
result.push(1);
  1. data is [2, 5, 9]; result is [2, 5, 7, 1].
  2. data is [2, 5, 9, 1]; result is [2, 5, 7, 1].
  3. data is [2, 5, 7, 9]; result is [2, 5, 7, 9, 1].
  4. data is [2, 5]; result is [2, 5, 7, 9, 1].
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What does the final call return?

function total(base = 4, ...extras) {
  return extras.reduce((sum, n) => sum + n, base);
}
const values = [undefined, 3, 0, 8];
total(...values);
  1. NaN
  2. 11
  3. 15
  4. 19
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What value does this call produce?

function read() {
  return;
}
read();
  1. undefined
  2. null
  3. false
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

The input n is a nonnegative integer. Which replacement for the recursive return makes the function count down to 0 correctly for both odd and even inputs?

function countDown(n) {
  if (n === 0) { return 0; }
  // recursive return goes here
}
  1. return 1 + countDown(n - 2);
  2. return 1 + countDown(n - 1);
  3. return 1 + countDown(n);
  4. return 1 + countDown(n + 1);
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What does the final call return?

const mode = "shared";
function readMode() { return mode; }
function run() {
  const mode = "local";
  return readMode();
}
run();
  1. It throws a ReferenceError.
  2. undefined
  3. "local"
  4. "shared"
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What values do jobs[0]() and jobs[1]() return after the loop?

const jobs = [];
for (var i = 0; i < 2; i++) {
  const offset = i * 10;
  jobs.push(() => offset + i);
}
  1. 12, 12
  2. 0, 10
  3. 2, 12
  4. 0, 11
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What result does this expression produce?

["6", "8", "10"].map((text) => parseInt(text, 10));
  1. [6, NaN, NaN]
  2. [6, 8, 10]
  3. [6, NaN, 2]
  4. ["6", "8", "10"]
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What are the printed values of out and seen?

let seen = 0;
const out = [3, 5].forEach((n) => {
  seen += n;
  return n * 2;
});
console.log(out, seen);
  1. undefined, 8
  2. [6, 10], 8
  3. undefined, 16
  4. [6, 10], 0
Show answerHide answer

Save this lesson to see this answer.

Set 5: Combine closures and higher-order functions10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

Which arrow returns the sum of its two inputs?
  1. (a, b) => { console.log(a + b); }
  2. (a, b) => { return a + b; }
  3. (a, b) => { return; a + b; }
  4. (a, b) => { a + b; }
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does add("6", 4) * 2 evaluate to?

function add(a, b) { return a + b; }
  1. 20
  2. NaN
  3. 128
  4. "6464"
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What does the final expression evaluate to?

function choose(n) {
  if (n < 0) { return 0; }
  return n + 2;
}
choose(choose(-4));
  1. 2
  2. -2
  3. 0
  4. 4
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What happens when build() is called?

let amount = 6;
function build() {
  const read = () => amount;
  const result = read();
  let amount = 9;
  return result;
}
  1. It returns the number 6.
  2. It returns the number 9.
  3. It returns the value undefined.
  4. It throws a ReferenceError inside read.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What values do change(3), read(), and otherRead() return?

function makePair() {
  let n = 1;
  return [
    (step) => { n += step; return n; },
    () => n
  ];
}
const [change, read] = makePair();
const [, otherRead] = makePair();
  1. 4, 1, 1
  2. 4, 4, 4
  3. 3, 3, 1
  4. 4, 4, 1
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What does the final expression return?

function makeLimit(max) {
  let used = 0;
  return () => {
    if (used >= max) { return false; }
    used++;
    return true;
  };
}
const allow = makeLimit(2);
[allow(), allow(), allow()];
  1. [true, true, true]
  2. [true, true, false]
  3. [true, false, false]
  4. [false, false, false]
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What are result and calls after this code?

let calls = 0;
function choose(values, test) {
  for (const value of values) {
    calls++;
    if (test(value)) { return value; }
  }
  return null;
}
const result = choose([3, 8, 10, 5], (n) => n > 6 && n % 2 === 0);
  1. result is 8; calls is 2.
  2. result is [8, 10]; calls is 4.
  3. result is 8; calls is 4.
  4. result is 10; calls is 3.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What are items and out afterward?

const items = [2, 4, 6];
const out = items.map((n, index, array) => {
  array[index] = n + 1;
  return n * 2;
});
  1. items is [4, 8, 12]; out is [3, 5, 7].
  2. items is [3, 5, 7]; out is [6, 10, 14].
  3. items is [3, 5, 7]; out is [4, 8, 12].
  4. items is [2, 4, 6]; out is [4, 8, 12].
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What does doubleSaved(9) return after the earlier call shown?

function once(fn) {
  let done = false;
  let saved;
  return (...args) => {
    if (!done) { done = true; saved = fn(...args); }
    return saved;
  };
}
const compose = (f, g) => (x) => f(g(x));
const saved = once((n) => n + 3);
const doubleSaved = compose((n) => n * 2, saved);
saved(4);
doubleSaved(9);
  1. 18
  2. 14
  3. 7
  4. 24
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What is the returned array?

const partial = (fn, ...fixed) => (...later) => fn(...fixed, ...later);
const offset = (base, n) => base + n;
const addFive = partial(offset, 5);
const compose = (f, g) => (x) => f(g(x));
const transform = compose((n) => n * n, addFive);
[1, 3].map(transform);
  1. [12, 16]
  2. [6, 14]
  3. [36, 64]
  4. [26, 28]
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

66 Key Terms, each with its definition

A

Accumulator
An accumulator is the running result in a reduction, passed to the callback and replaced by that callback's returned value at each step.
A sum reduction starting at 0 updates its accumulator to 5, 15, and 30 while processing [5, 10, 15].
Anonymous function
An anonymous function is a function expression without an explicit function name, often stored in a variable or passed as an argument.
The expression function (n) { return n * n; } has no internal function name in its written syntax.
Argument
An argument is a value supplied to a function call, matched by position to a parameter or collected among the remaining inputs.
In area(4, 2.5), the arguments are 4 and 2.5, regardless of how the parameters were named.
arguments object
The arguments object is an array-like collection of the inputs passed to an ordinary function, distinct from a real rest-parameter array.
Older code can read arguments[0], but it cannot call arguments.map as an ordinary array method.
Array destructuring
Array destructuring assigns entries from an array to individual names, allowing one returned array to supply several named results.
The declaration const [low, high] = minMax(values); unpacks the two entries of the returned array.
Arrow function
An arrow function is a function expression written with an arrow between its parameters and body, without its own this or arguments binding.
The arrow (n) => n * 2 supplies a short transformation callback to an array method such as map.

B

Base case
A base case is a recursive function's stopping path, returning without another self-call when the problem has reached a simple form.
For a factorial on nonnegative integers, the test n <= 1 stops calls at either 0 or 1.
Binding
A binding associates a name with a stored value in a particular scope, allowing same-named variables in different scopes to remain distinct.
An assignment updates the nearest accessible binding; an inner declaration creates a separate local one.
Block body
A block body contains statements inside curly braces and requires an explicit return to send a computed value back from an arrow.
The arrow (n) => { n * 2; } computes a product but returns undefined because it has no return statement.
Block scope
Block scope confines a let or const declaration to its enclosing block and nested code, keeping that declaration unavailable to outer code.
A let message declared in an if body cannot be read by a statement after that block ends.

C

Call stack
The call stack records unfinished function calls, placing each new invocation on top and removing its frame when that call returns.
While sumOfSquares waits for square(3), the square call has a frame above its waiting caller.
Callback
A callback is a function passed as an argument for a receiver to invoke, with that receiver controlling its inputs and use of its return.
A countIf helper calls its test callback for each item and counts those whose test results are truthy.
camelCase
CamelCase is a naming style with a lowercase first word and capitalized later words, commonly used for JavaScript function names.
The name calculateTotal describes an action and separates its two words without spaces or underscores.
Captured variable
A captured variable is a binding reached through a closure, so the function can read its current value instead of a frozen creation-time copy.
Changing an outer greeting from "Hi" to "Hello" changes the value that a later call to a function reading greeting returns.
Closure
A closure combines a function with retained access to its creation scope, allowing captured variables to remain usable after an outer return.
A counter function can keep incrementing its private count after the factory that created it has finished.
Comparator
A comparator is a sorting callback whose negative, positive, or zero return value determines the relative order of two compared inputs.
The comparator (a, b) => a - b sorts numbers in ascending order while sort rearranges the input array.
Composition
Composition combines functions by passing one function's output into another's input, with the inner call performed before the outer call.
With f(g(x)), changing the order to g(f(x)) can change the result even when both functions accept numbers.
Concise body
A concise body is an arrow function body made from an expression whose value becomes the call result without an explicit return statement.
The expression body in (a, b) => a + b directly returns the sum of the two supplied arguments.

D

Default parameter
A default parameter uses a specified expression when its argument is undefined, including when the caller leaves that argument out.
The parameter level = 50 keeps an explicit 0 but supplies 50 when the argument is missing.
Delayed callback
A delayed callback is scheduled to run later, while code after the scheduling call can continue without waiting for that callback to run.
Passing sayHi to setTimeout lets following statements run before the scheduled greeting appears.

E

Early return
An early return ends a call before its later statements run, often handling a special input before the function performs its main work.
A division helper can return null for a zero divisor before execution reaches the division statement.

F

Falsy value
A falsy value behaves as false in a truth test, which is why an older logical-OR default can replace valid inputs such as zero.
The expression level || 50 replaces 0, while a parameter default preserves that supplied value.
Function
A function is a reusable set of instructions that performs a task when called, optionally accepting inputs and returning a result.
A script calls printHeading("Report") and printHeading("Totals") to reuse one heading routine.
Function body
A function body holds the instructions that execute during a call; an ordinary function encloses those statements in curly braces.
If a printHeading body holds three printing statements, all three run again for every call to that function.
Function call
A function call invokes a function using parentheses after a function value, with any arguments placed inside those parentheses.
Writing sayHi() runs its body, while a line containing only sayHi reads the function value.
Function declaration
A function declaration defines a named function with the function keyword and makes it available from the start of its scope.
The call greet("Li") can appear above the written declaration of greet in the same scope.
Function expression
A function expression creates a function as a value inside an expression, such as the right side of a variable assignment.
In const square = function (n) { return n * n; };, the expression supplies the value stored in square.
Function factory
A function factory creates and returns configured functions, using closure access to retain the arguments or local state of its invocation.
Calling makeMultiplier(3) returns a function that remembers 3 while accepting a later number to multiply.
Function reference
A function reference is a value that identifies a function, allowing another variable or receiver to call that same function later.
After const alias = square;, calling alias(4) invokes the function also reached through square.
Function scope
Function scope makes a parameter or var declaration local to its enclosing function, even when the var is written inside an inner block.
A var result assigned in an if block can be returned later from the enclosing function body.

G

Global scope
Global scope holds top-level declarations of a regular script, making those names available to functions whose scope chains reach them.
A script's top-level count can become shared state when several functions read or update it.
Guard clause
A guard clause checks a special condition and exits early, keeping that exceptional path separate from the main work of the function.
The test if (b === 0) { return null; } protects the division placed after it in safeDivide.

H

Higher-order function
A higher-order function accepts a function as an argument, returns a function as a result, or combines both forms of function-value handling.
Array methods accept callbacks, while a multiplier factory returns a configured calculation function.
Hoisting
Hoisting describes declarations being registered before their scope executes, with different early access rules for functions, var, let, and const.
Reading a hoisted var before its assignment gives undefined; an early function-declaration call can run.

I

IIFE
An immediately invoked function expression, or IIFE, creates a function and calls it at once, often to give older code a private scope.
The final parentheses in (function () { var hidden = 1; })(); invoke the newly created function.
Inline function
An inline function is a function expression written directly where its value is needed, such as an arrow inside a call's argument list.
The arrow in repeat(3, (i) => console.log(i)) supplies the action without a separate named declaration.
Iteration binding
An iteration binding is the separate loop-variable binding created for an iteration by let in a for-loop header, which closures can retain.
Stored arrows from different let i iterations can return different values after the loop has ended.

L

Lexical scope
Lexical scope determines accessible outer declarations from the written nesting of code, rather than from the location of a later caller.
A top-level reader still reads its outer variable when called inside a function with a same-named local.
Local variable
A local variable belongs to a limited function or block scope, allowing its working data to stay separate from similarly named outer data.
Two independent functions can each use a local total without having their additions affect one another.

M

Method chain
A method chain uses one method's returned value as the receiver of the next method, allowing several array-processing steps in sequence.
A filter followed by a map first selects inputs, then transforms only the entries that were kept.
Mutation
Mutation changes the contents of an existing value such as an array, making the change visible through other references to that array.
A function that calls arr.push(3) changes the same array that the caller supplied as its argument.

N

Named function expression
A named function expression creates a function with an internal name available inside its own body, including for recursive calls.
In const factorial = function fact(n) { /* body */ };, the outside variable is factorial, and the internal name is fact.

O

Object literal
An object literal creates an object using curly braces and named properties; a concise arrow return encloses it in parentheses.
The arrow (n) => ({ value: n }) returns an object instead of treating its braces as a statement block.

P

Parameter
A parameter is a local name in a function definition that receives a value from an argument when the function is called.
The names width and height are parameters in function area(width, height) { return width * height; }.
Partial application
Partial application fixes selected arguments ahead of time and returns a function that supplies the remaining inputs when called later.
A greeting function partially applied to "Hello" can later accept "Ana" and return "Hello, Ana".
Predicate
A predicate is a test function whose result decides whether an input meets a condition, used by methods such as filter or find.
The predicate (n) => n > 4 selects which numbers a filter keeps without changing their values itself.
Private state
Private state is data inside a closure's local scope that outside code can access or change only through the operations that expose it.
A limiter exposes an allow-or-reject operation while keeping its internal used count inaccessible directly.
Pure function
A pure function calculates its output from its arguments alone and makes no changes outside the call, including no console output.
A function that only returns n * 2 is pure; printing the product would introduce a side effect.

R

Reassignment
Reassignment changes the value stored in an existing variable or parameter, without changing another variable that held the old value.
Inside a function, arr = [] redirects the parameter without replacing the caller's array reference.
Recursion
Recursion occurs when a function calls itself to solve smaller instances of a task, with a stopping case that needs no further self-call.
A factorial function multiplies its input by the result of the same function on an input one smaller.
Recursive case
A recursive case handles the problem by calling the same function on a reduced input and using the returned result to finish the current call.
The expression n * factorial(n - 1) waits for the smaller factorial before multiplying by this call's n.
Rest parameter
A rest parameter collects remaining call arguments into a real array and appears as the last parameter, prefixed with three dots.
In function log(label, ...values) { /* body */ }, the first argument is separate from the rest array.
Return value
A return value is the single value produced by a function call and supplied to its caller for storage or use in another expression.
If double(5) returns 10, the expression double(5) + 1 can immediately use that value to produce 11.
Run-once wrapper
A run-once wrapper retains a completion flag and a first result so later calls can reuse that result without repeating a completed operation.
A separate flag lets a run-once wrapper remember that its first call completed even when the result was 0.

S

Scope chain
The scope chain is the sequence of current and enclosing scopes searched outward to resolve a name, stopping at the first declaration.
An inner calculation reads its local amount before considering a same-named variable in an outer scope.
Shadowing
Shadowing occurs when an inner declaration hides a same-named outer declaration within the inner scope, giving the name a different binding.
A parameter named count hides an outer count inside that function without changing the outer binding.
Shared state
Shared state is retained data that more than one function can access and update, such as one binding captured by functions from the same call.
A deposit operation and a balance reader returned together can both access the same private balance.
Side effect
A side effect is a change outside a function's local calculation, such as printing, changing an outer variable, or mutating an input array.
A callback that pushes to a shared list has a side effect even if it also returns a useful value.
Spread syntax
Spread syntax expands array entries into separate call arguments, using three dots before the array expression in the argument list.
Calling Math.max(...[3, 7, 2]) passes three numbers instead of passing one array to Math.max.
Stack frame
A stack frame holds the execution state of one active function call, including its parameters, local variables, and continuation point.
Two active recursive calls can each keep a different value of n because they have separate frames.
Stack trace
A stack trace lists the calls active when an error occurred, starting with the most recent call and continuing through its callers.
Repeated entries for one recursive function can help locate a stopping condition that the input never reaches.
Strict mode
Strict mode is a JavaScript execution mode that rejects assigning to undeclared names, helping prevent accidental global variables.
Starting a regular script with "use strict"; makes an undeclared count = 5; assignment raise a ReferenceError.
Synchronous callback
A synchronous callback runs during the receiving function's execution, finishing its work before that receiver returns to its own caller.
A map callback processes array entries before the completed map call supplies its result array.

T

Temporal dead zone
The temporal dead zone is the interval before a let or const binding is initialized, when accessing that binding raises a ReferenceError.
An early read finds a shadowing local even when it is still uninitialized, instead of using an outer value.
Transformation callback
A transformation callback returns a replacement value for an input item, letting a mapping function collect the outputs into a new array.
The callback (n) => n * n turns the entries of [1, 2, 3] into squares through a mapping operation.

W

Wrapper
A wrapper is a returned function that adds behavior around an existing function call, often forwarding its inputs and preserving its result.
A logging wrapper prints the supplied arguments, calls the original function, then returns its result.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started