Recursive Function.prototype.apply conceptual understanding

Himmel Source

I came across this stackoverflow question about recursively flattening a JS array. Here is the accepted answer:

function flatten() {
    var flat = [];
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat.push.apply(flat, flatten.apply(this, arguments[i]));
        } else {
            flat.push(arguments[i]);
        }
    }
    return flat;
}

flatten([[1], 2, [3, 4]]); // returns [1, 2, 3, 4]

I'm having trouble understanding how flat.push.apply(...) and flatten.apply(...) work.

I understand that the function will only exit if none of the items in the array are arrays themselves. I also understand that Function.prototype.apply() allows you to call a function using an array of arguments.

What I don't understand is why you are using flat.push... if flat will be set to [] at each function iteration. Also, what significance does setting flat as the this context have?

Can someone help explain how the execution of flat.push.apply(flat, flatten.apply(this, arguments[i])); works?

javascriptarraysrecursion

Answers

answered 3 years ago Dan Macák #1

He uses flat.push safely, because push is actually called on different array everytime. Each time flatten executes, it creates new array with different reference stored in flat variable, so each recursion is associated with different flat array.

Setting flat as this context provides the array to be called push upon. Try setting it to null, and you will probably get TypeError because of illegal operation - trying to call push on null.

Now for the more complicated part. push accepts a list of arguments to be added to array, right? But it is sometimes not convenient to supply these arguments one by one, especially, if you don't know, how many of them push should receive, as in your case, where the sizes of arrays to be flatten vary.

That's why the author of that code invokes push via apply with array reference as second argument - which is the returned value of flatten.apply(this, arguments[i]) - reference to array containing only numbers by that time. Each number of that array is pushed to flat, because apply called push and passed whole list of numbers of that array via arguments to push.

The flatten.apply(this, arguments[i]) part is invoked via apply because of similar reasons - it utilizes arguments (array-like object each function has) to process function args easily. Handing over this in the first arg of apply doesn't really matter here, because there is no use for this in flatten (this points to global object in your code sample).

comments powered by Disqus