Function.prototype.length
Reports how many parameters a function declares before its first default or rest parameter.
func.length- Added in JavaScript ECMAScript 1 (1997)
Return value
numberThe number of parameters expected by the function: formal parameters before the first one with a default value, excluding the rest parameter, with each destructuring pattern counting as one.
Changelog
- PHP ECMAScript 2015Default and rest parameters were introduced and defined the current counting rules: counting stops at the first parameter with a default, and the rest parameter is never counted.
- PHP ECMAScript 2015length became configurable: true (it was non-configurable in pre-ES2015 implementations), so Object.defineProperty() can now change it.
length is a read-only, non-enumerable, configurable data property giving the function's declared arity. Three rules decide the number:
- Counting stops at the first parameter that has a default value.
- A rest parameter is never counted.
- A destructuring pattern counts as one parameter, however many bindings it contains.
When to reach for it
Mostly for dispatching on arity in library code. Node-style APIs check it to decide whether a callback wants an error argument; test runners check it to decide whether a test function expects a done callback; currying helpers use it to know when enough arguments have accumulated.
What trips people up
It is a declaration count, not a call count. If you want how many arguments actually arrived, that is arguments.length or the length of a rest parameter — the two are unrelated and routinely differ.
The second surprise is silent zeros. Give the first parameter a default, or use only a rest parameter, and length is 0 even though the function clearly takes arguments. Any arity-sniffing library breaks quietly the day someone adds a default. bind() also lowers it by the number of pre-filled arguments.
Example
When to use it
- Detect whether a callback expects a done/next argument before invoking it
- Decide when a curried function has collected enough arguments to run
- Validate at registration time that a plugin hook declares the parameters it should
- Report the declared arity of a built-in, such as parseInt.length
Frequently asked questions
Why is my function's length 0 when it clearly takes parameters?
Because the first parameter has a default value or the function only uses a rest parameter — counting stops at the first default and rest parameters are never counted. Reorder the parameters so the required ones come first if you need an accurate count.
What is the difference between function length and arguments.length?
function.length is how many parameters the source declares; arguments.length is how many were passed in this particular call. The first is fixed at definition time, the second changes on every invocation.
Can I change a function's length?
Not by assignment — the property is not writable — but it is configurable, so Object.defineProperty(fn, "length", { value: 3 }) works. Wrapper and currying libraries use exactly this to make a wrapper report the arity of the function it wraps.
More examples
Declared arity versus arguments actually received
f.length is always 2 because that is what the source declares, while arguments.length changes with each call — 1 then 3. In g the rest parameter is excluded from length, so declared stays 1 no matter how many arguments arrive.
function f(a, b) {
return { declared: f.length, received: arguments.length };
}
console.log(f(1));
console.log(f(1, 2, 3));
function g(first, ...rest) {
return { declared: g.length, received: rest.length + 1 };
}
console.log(g("a", "b", "c"));Dispatching on arity
Extra arguments are always accepted, so every handler runs; length is what tells the caller how many the function actually intends to read. The isAsyncStyle check is the real-world pattern used by Mocha and Jest to spot a done callback.
const handlers = [
() => "no arguments wanted",
(a) => `got one: ${a}`,
(a, b) => `got two: ${a + b}`,
];
for (const h of handlers) {
console.log(h.length, "->", h(1, 2));
}
// A test runner deciding whether the test is async
const isAsyncStyle = (fn) => fn.length > 0;
console.log(isAsyncStyle((done) => {}), isAsyncStyle(() => {}));
Discussion