Using 'apply' to Emulate JavaScript's Upcoming Spread Operator

The next version of JavaScript, ECMAScript 6, is planned to introduce a handy spread operator which makes using arrays to supply function arguments much simpler. Here's an example of how it will work:

varsomeArgs=["a","b","c"];// Using the spread operator with someArgsconsole.log(...someArgs);// Is equivalent to thisconsole.log("a","b","c");

Now some of you might be thinking that apply gives us a nice easy way to emulate that, and you'd be right. This is also equivalent to the above:

console.log.apply(console,someArgs);

The apply method is part of the prototype of all functions, so any time we have a function, we can invoke it using apply. It takes two arguments, a this parameter which sets the value of this within the function, and a single array which is converted into a list of arguments to the function that apply is called on.

So if apply gives us the same thing as the spread operator, why is the ES6 committee adding spread at all? Because the spread operator is much more flexible. See for example:

varsomeArgs=["a","b","c"];varmoreArgs=[1,2,3];// Using the spread operator twice, with a non-spread argumentconsole.log(...someArgs,"between",...moreArgs);// Is equivalent to thisconsole.log("a","b","c","between",1,2,3);

In this case, there isn't a simple one to one mapping between using the spread operator and using apply. But we can still create a pretty decent emulation. Let's take a look at how.

What on earth is going on here? It's actually pretty simple. Our spreadify function takes two arguments, a function, and an optional parameter specifying what the function's this should be. In the case of console.log, that is console.

When the spreadify function is invoked, it immediately returns another function. That's why we have the doubled parentheses at the end of the example. But you could also do something like this:

// `spreadLog` is now the function that `spreadify` returnsvarspreadLog=spreadify(console.log,console);// Outputs: a b c 1 2 3spreadLog(someArgs,moreArgs);

So we have our new function. That new function does two things.

It does some preprocessing on whatever arguments we pass it. (Either immediately with that second set of parentheses in the first example, or later on as in the second example.)

It then invokes our original function (log in this case) with the processed arguments.

In our preprocessing we use the arguments object to examine all the arguments which were passed in. If an argument is an array, then we add the array's contents to spreadArgs. If the argument is not an array, then we directly push the argument onto spreadArgs.

Finally, we invoke our original function by using apply, passing in the this value, as well the spreadArgs array.

In the event that you didn't pass a this value into spreadify, fnThis will be undefined, and apply will use the default value of this (usually the global object).

You may have noticed one problem with our emulation of the spread operator here. The spreadify function assumes that all arrays should be expanded into individual arguments. That may not always be the case. Fortunately there's a fairly simple workaround. Just wrap any array you want to preserve as an individual argument inside another array, like so: