Working with Unnamed Parameters

So far, the examples in this chapter have only covered parameters that have been named in the function definition. However, JavaScript functions don’t limit the number of parameters that can be passed to the number of named parameters defined. You can always pass fewer or more parameters than formally specified. Default parameter values make it clear when a function can accept fewer parameters, and ECMAScript 6 sought to make the problem of passing more parameters than defined better as well.

Unnamed Parameters in ECMAScript 5

Early on, JavaScript provided the arguments object as a way to inspect all function parameters that are passed without necessarily defining each parameter individually. While inspecting arguments works fine in most cases, this object can be a little cumbersome to work with. For example, examine this code, which inspects the arguments object:

  1. function pick(object) {
  2. let result = Object.create(null);
  3. // start at the second parameter
  4. for (let i = 1, len = arguments.length; i < len; i++) {
  5. result[arguments[i]] = object[arguments[i]];
  6. }
  7. return result;
  8. }
  9. let book = {
  10. title: "Understanding ECMAScript 6",
  11. author: "Nicholas C. Zakas",
  12. year: 2015
  13. };
  14. let bookData = pick(book, "author", "year");
  15. console.log(bookData.author); // "Nicholas C. Zakas"
  16. console.log(bookData.year); // 2015

This function mimics the pick() method from the Underscore.js library, which returns a copy of a given object with some specified subset of the original object’s properties. This example defines only one argument and expects the first argument to be the object from which to copy properties. Every other argument passed is the name of a property that should be copied on the result.

There are a couple of things to notice about this pick() function. First, it’s not at all obvious that the function can handle more than one parameter. You could define several more parameters, but you would always fall short of indicating that this function can take any number of parameters. Second, because the first parameter is named and used directly, when you look for the properties to copy, you have to start in the arguments object at index 1 instead of index 0. Remembering to use the appropriate indices with arguments isn’t necessarily difficult, but it’s one more thing to keep track of.

ECMAScript 6 introduces rest parameters to help with these issues.

Rest Parameters

A rest parameter is indicated by three dots (...) preceding a named parameter. That named parameter becomes an Array containing the rest of the parameters passed to the function, which is where the name “rest” parameters originates. For example, pick() can be rewritten using rest parameters, like this:

  1. function pick(object, ...keys) {
  2. let result = Object.create(null);
  3. for (let i = 0, len = keys.length; i < len; i++) {
  4. result[keys[i]] = object[keys[i]];
  5. }
  6. return result;
  7. }

In this version of the function, keys is a rest parameter that contains all parameters passed after object (unlike arguments, which contains all parameters including the first one). That means you can iterate over keys from beginning to end without worry. As a bonus, you can tell by looking at the function that it is capable of handling any number of parameters.

I> Rest parameters do not affect a function’s length property, which indicates the number of named parameters for the function. The value of length for pick() in this example is 1 because only object counts towards this value.

Rest Parameter Restrictions

There are two restrictions on rest parameters. The first restriction is that there can be only one rest parameter, and the rest parameter must be last. For example, this code won’t work:

  1. // Syntax error: Can't have a named parameter after rest parameters
  2. function pick(object, ...keys, last) {
  3. let result = Object.create(null);
  4. for (let i = 0, len = keys.length; i < len; i++) {
  5. result[keys[i]] = object[keys[i]];
  6. }
  7. return result;
  8. }

Here, the parameter last follows the rest parameter keys, which would cause a syntax error.

The second restriction is that rest parameters cannot be used in an object literal setter. That means this code would also cause a syntax error:

  1. let object = {
  2. // Syntax error: Can't use rest param in setter
  3. set name(...value) {
  4. // do something
  5. }
  6. };

This restriction exists because object literal setters are restricted to a single argument. Rest parameters are, by definition, an infinite number of arguments, so they’re not allowed in this context.

How Rest Parameters Affect the arguments Object

Rest parameters were designed to replace arguments in ECMAScript. Originally, ECMAScript 4 did away with arguments and added rest parameters to allow an unlimited number of arguments to be passed to functions. ECMAScript 4 never came into being, but this idea was kept around and reintroduced in ECMAScript 6, despite arguments not being removed from the language.

The arguments object works together with rest parameters by reflecting the arguments that were passed to the function when called, as illustrated in this program:

  1. function checkArgs(...args) {
  2. console.log(args.length);
  3. console.log(arguments.length);
  4. console.log(args[0], arguments[0]);
  5. console.log(args[1], arguments[1]);
  6. }
  7. checkArgs("a", "b");

The call to checkArgs() outputs:

  1. 2
  2. 2
  3. a a
  4. b b

The arguments object always correctly reflects the parameters that were passed into a function regardless of rest parameter usage.

That’s all you really need to know about rest parameters to get started using them.