What you'll be able to do

By the end of this page you should be able to set a default when a caller omits an argument, know that null does not trigger the default the way undefined does, and collect leftover arguments into an array with rest (...args).

Previous post: scope and hoisting plainly.

Who this is for

  • People who write if (name === undefined) name = "Guest" at the top of every function
  • Beginners who pass too many values and wonder where the extras went
  • Anyone confusing rest (... in parameters) with spread (... when copying arrays — later)

You can skip this if you already use defaults and rest comfortably, and you know null is not “missing.” Come back when a default never applies or extras are ignored.

Default parameters: a fallback when the arg is missing

Put = value after a parameter. If the caller does not pass that argument (or passes undefined), the default is used.

function greet(name = "Guest") {
  return "Hello, " + name;
}

console.log(greet());
console.log(greet("Asha"));

Logs Hello, Guest and Hello, Asha.

Scenario A — optional label. A helper can work with one argument or none. Defaults keep the call site clean: greet() instead of greet("Guest") every time.

Scenario B — several parameters. Defaults can stack; put required params first when you can:

function clipLabel(title, seconds = 0) {
  return title + " (" + seconds + "s)";
}

console.log(clipLabel("Intro"));
console.log(clipLabel("Intro", 12));

If you need a default in the middle and still want to pass a later arg, you must pass undefined explicitly for the middle one — awkward. Prefer ordering so optionals sit at the end.

undefined triggers the default; null does not

function greet(name = "Guest") {
  return name;
}

console.log(greet(undefined)); // Guest
console.log(greet(null)); // null

Scenario A — omitted or undefined. Treated as “use the default.”

Scenario B — null on purpose. You said “there is no name,” not “missing.” The default does not run. If empty should become Guest, check null yourself or normalize before the call.

0 and "" also do not trigger defaults — they are real values. That is usually what you want for counts and strings.

Rest parameters: gather the leftovers

Rest uses ... before the last parameter name. It packs remaining arguments into a real array.

function sum(first, ...rest) {
  let total = first;
  for (const n of rest) {
    total += n;
  }
  return total;
}

console.log(sum(1, 2, 3, 4));

Logs 10. first is 1; rest is [2, 3, 4].

Scenario A — “any number of scores.” average(...nums) style APIs. Rest makes a real array you can loop with for...of.

Scenario B — one required + extras. function tag(label, ...parts) — label is special; parts are the rest.

Rest must be last. function bad(...rest, x) {} is a syntax error.

Do not confuse rest with the old arguments object. Prefer rest: it is a real Array, clearer, and works in arrows.

Defaults + rest together

function logAll(prefix = "LOG", ...items) {
  for (const item of items) {
    console.log(prefix, item);
  }
}

logAll(undefined, "a", "b");

Scenario A — default prefix, many items. Pass undefined for prefix if you want the default and still pass items — or call logAll("LOG", "a", "b") explicitly.

Scenario B — skip prefix cleanly. If you often want the default, put extras-only calls behind a helper so callers are not juggling undefined.

A tiny practice file

Save as defaults-rest.html.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Defaults and rest</title>
  </head>
  <body>
    <button id="go">Run</button>
    <pre id="out">Result will show here.</pre>
    <script>
      function greet(name = "Guest") {
        return "Hello, " + name;
      }

      function sumAll(...nums) {
        let total = 0;
        for (const n of nums) {
          total += n;
        }
        return total;
      }

      document.getElementById("go").addEventListener("click", function () {
        const lines = [];
        lines.push(greet());
        lines.push(greet("Asha"));
        lines.push("null stays null-ish: " + String(greet(null)));
        lines.push("sumAll(1,2,3): " + sumAll(1, 2, 3));
        document.getElementById("out").textContent = lines.join("\n");
      });
    </script>
  </body>
</html>

Watch greet(null): you get "Hello, null" as a string join — proof the default did not fire.

Mistakes I see a lot

1. Expecting null or "" to use the default. Only missing / undefined does.

2. Putting rest in the middle of the parameter list. It must be last.

3. Default before a required param and then skipping it. Call sites get messy; reorder params.

4. Using arguments in new code. Prefer ...rest.

5. Thinking rest “spreads” into another call by itself. Rest collects. Spreading into a call is a different use of ... (arrays post).

What to try before the next post

  1. Write multiply(a, b = 1) and call with one arg, then two.
  2. Log greet(null) vs greet().
  3. Write sumAll(...nums) and pass four numbers.
  4. Build the tiny HTML file.

Next in this series: callbacks as values — passing a function into another function (still sync), so “run this later” starts to feel natural.

Try this next outside the series

Functions matter more once they stop being textbook examples and start shaping reusable app code.

  • Node.js async patterns — see functions return promises, accept callbacks, and coordinate real work
  • Diff Checker — compare two function versions before you keep a refactor