Over a million developers have joined DZone.

Spreading arrays into arguments in JavaScript

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Sometimes, one needs to spread the elements of an array, to use them as the arguments of a function call. JavaScript allows you to do that via Function.prototype.apply, but that does not work for constructor invocations. This post explains spreading and how to make it work in the presence of the new operator.

Spreading

Spreading means making a function call, method call or constructor invocation and supplying the arguments via an array. In Python, one calls this unpacking. ECMAScript.next will have the spread operator (prefix ...) for this purpose. In current JavaScript, you can perform this operation via Function.prototype.apply.

Example: function call. Math.max() returns the maximum among its 0 or more numeric parameters. With the spread operator, you can use it for arrays:

    Math.max(...[13, 7, 30])
This is the equivalent of
    Math.max(13, 7, 30)
In current JavaScript, you have to use apply().
    > Math.max.apply(null, [13, 7, 30])
    30
Explanation: An apply invocation looks as follows:
    func.apply(thisValue, [param1, param2, ...])
which is equivalent to
    thisValue.func(param1, param2, ...)
Note that func does not have to be a method of thisValue – apply temporarily turns it into one.

Example: constructor invocation. The Date constructor takes several numeric parameters and produces a date. With the spread operator, you can hand in an array.

    new Date(...[2011, 11, 24]) // Christmas 2011
However, apply does not work with constructors, because the new operator assumes that the trailing apply method call is the constructor. The following is a work-around:
    new (Function.prototype.bind.apply(
        Date, [null].concat([2011, 11, 24])))
What is happening here? Let’s look at the main components:
  • bind: We use this method to turn Date into a function with zero parameters, by pre-filling them in. A bind invocation looks as follows:
        func.bind(thisValue, [arg1], [arg2], ...)
    
    It turns func into a new function whose implicit this parameter is thisValue and whose initial arguments are always as given. When one invokes the new function, the arguments of such an invocation are appended to what has already been provided via bind. MDN has more details. What we want to do is the following.
        Date.bind(null, ...[2011, 11, 24])
    
    The first argument is null, because bind turns Date into a function that does not need a thisValue: It is only invoked as a constructor and new overrides the thisValue from bind. To simulate the above spread operator, we need the apply method.
  • apply: Again, we use apply to turn an array into arguments for a function call. We invoke apply on the function Function.prototype.bind, with two arguments:
    • 1st argument: this has the value Date
    • 2nd argument: The arguments for bind are created by prepending null to the array [2011, 11, 24]. We use the slightly more verbose concat instead of unshift, because it is non-destructive (does not change this).
    The performed method call is thus
        Date.bind(null, 2011, 11, 24)
    
    which is the same as the above mentioned Date.bind(null, ...[2011, 11, 24]).
  • new: Invoke the zero-argument function produced by bind as a constructor. By putting the operand in parentheses, we avoid apply becoming the constructor and instead use new on the result of apply.

Constructor spreading via a library method

Mozilla suggests turning the above work-around into a library method. The following is a slightly edited version of their suggestion:
    if (!Function.prototype.construct) {
        Function.prototype.construct = function(argArray) {
            if (! Array.isArray(argArray)) {
                throw new TypeError("Argument must be an array");
            }
            var nullaryFunc = Function.prototype.bind.apply(
                this, [null].concat(argArray));
            return new nullaryFunc();
        };
    }
Interaction:
    > Date.construct([2011, 11, 24])
    Sat Dec 24 2011 00:00:00 GMT+0100 (CET)

 

From http://www.2ality.com/2011/08/spreading.html

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}