Search code examples
javascriptecma262

What exactly `Function.prototype.length` means?


There was a Function.prototype.arity property purposed for getting number of arguments function expects. Now it is obsolete (since JS 1.4), and the same goal has Function.prototype.length.

But recently I've found an article in the documentation about Array.prototype.reduce method. And it clearly says that the method has property length equal to 1:

The length property of the reduce method is 1.

This exact article has a header with number of arguments, and there are two of them:

Array.prototype.reduce ( callbackfn [ , initialValue ] )

callbackfn and initialValue (optional).

So it is not clear to me, what exactly the purpose of length property.
If it is used to give useful information to developer, then it actually does not.
If it is just a technical automatically-generated property that just indicates number of arguments in function definition, then why don't maintain its consistency?


Solution

  • Array.prototype.reduce's length is 1 because the second parameter is optional.*

    So it is not clear to me, what exactly the purpose of length property.

    To tell the developer how many declared parameters it has prior to the first parameter with a default value (if any) or the rest parameter (if any), whichever is earliest in the parameter list. Or as the spec puts it:

    The value of the length property is an integer that indicates the typical number of arguments expected by the function.

    The exact algorithm is in the spec's Static Semantics: ExpectedArgumentCount section.

    If it is used to give useful information to developer, then it actually does not.

    Well, that's a matter of opinion. :-)

    When you have a language like JavaScript where functions can only express an expectation but may be called with fewer or more arguments, and particularly when you add the concepts of default parameter values and rest parameters, it's not surprising that the arity of function is a bit of a soft concept.

    Some fun examples:

    function ex1(a) { }             // length is 1, of course
    function ex2(a, b = 42) { }     // length is 1, `b` has a default
    function ex3(a, b = 42, c) { }  // length is 1, `b` has a default and
                                    // `c` is after `b`
    function ex4(a, ...rest) { }    // length is 1 yet again, rest parameter doesn't count
    

    * In ES5, its declaration in JavaScript would be:

    function reduce(callback) {
        // ...
    }
    

    ...and then it would use arguments.length to determine whether you'd supposed an initialValue.

    In ES2015+ (aka "ES6"+), it would either still be like that, or be like this:

    function reduce(callback, ...args) {
        // ...
    }
    

    ...and use args.length to see if there was an initial value.

    Or possibly like this:

    const omitted = {};
    function reduce(callback, initialValue = omitted) {
        // ...
    }
    

    ...and then use initialValue === omitted to know whether you'd supplied an initial value. (The default value of initialValue can't be undefined or null or similar because the function has to branch based on whether the argument was provided [not what its value is]. But we can do that with object identity.)