Topics

Jump to a topic.

Search questions

Find a question by title, tag, or summary.

What is the arguments object, and why avoid it?

Questions in this chapter (7)
  • Draft
  • easy
  • Askedsometimes
  • Verified October 2026

Answer

arguments is created for every call of a non-arrow function (unless a parameter is named arguments, or, with no parameter expressions, a body-level function or let/const is). It holds the arguments as passed, regardless of how many parameters are declared.

  • Array-like, not an array. It has length, index keys, and Symbol.iterator (the same function as Array.prototype.values), so spread and Array.from work. It inherits from Object.prototype, so map, slice, and friends are missing, and Array.isArray is false.
  • Mapped vs unmapped. In a sloppy function whose parameter list is simple (no defaults, rest, or destructuring), arguments[i] is aliased to the i-th named parameter, in both directions, but only for indices below the number of arguments actually passed. In strict code or with a non-simple list it is a plain copy with no link.
  • callee. Sloppy mapped objects expose the running function as arguments.callee. In unmapped objects it is a poison-pill accessor that throws TypeError.
  • Arrows have none. Inside an arrow, arguments resolves to the enclosing function's.

Why avoid it: it isn't an array, the aliasing is surprising and only exists in legacy sloppy code, it hides a function's real signature, and rest parameters (...args) give a real array with explicit names. Reach for arguments only when you must read legacy code.

Example

// CommonJS (sloppy) so `arguments` is mapped; ES modules are strict.
function mapped(a, b) {
  arguments[0] = 'changed'; // writes through to `a`
  b = 'B';
  return [a, arguments[1]];
}
console.log(mapped('x', 'y')); // → [ 'changed', 'B' ]
console.log(mapped('x')); // → [ 'changed', undefined ]  (no argument 1, so no link)

function strictOne(a) {
  'use strict';
  arguments[0] = 'changed';
  return a;
}
console.log(strictOne('x')); // → x

function shape() {
  return [Array.isArray(arguments), arguments.length, [...arguments]];
}
console.log(shape(1, 2)); // → [ false, 2, [ 1, 2 ] ]

const sum = (...nums) => nums.reduce((total, n) => total + n, 0);
console.log(sum(1, 2, 3)); // → 6

Pitfalls

  • Relying on parameter aliasing. Add a default value, a rest parameter, or 'use strict' (or just use a module or class body) and the link vanishes silently.
  • Calling array methods on it directly (arguments.map(...) throws). Spread it first: [...arguments].
  • Using arguments in an arrow and expecting its own call's arguments. It reads the outer function's. At the top of a CommonJS module it is the module wrapper's arguments, not an error.
  • Leaving arguments.callee in code that may become strict. It throws there.

Follow-ups

How does the mapping get broken?
Deleting an index, or redefining it as non-writable with Object.defineProperty, removes the alias for that index. Redefining only its value keeps the alias. Parameters past the passed count were never mapped.
How do you write a variadic function today?
function f(first, ...rest). rest is a real array, is named in the signature, and works in arrows. To forward everything unchanged: (...args) => g(...args).

Exercises

Predict the output

Run as CommonJS (sloppy mode).

function f(a, b) {
  a = 'A';
  arguments[1] = 'B';
  return [arguments[0], b, arguments.length];
}
console.log(f('x'));

Predict the output

function g(a = 0) {
  arguments[0] = 'changed';
  return a;
}
console.log(g('x'));