I needed to join a function’s arguments using array.join()
and
I remembered that I could not simply call join()
on the
arguments
object, as it is not a real array.
Internet wisdom has it that arguments
can be changed into an
Array
by this code snippet:
const args = Array.prototype.slice.call (arguments);
Slicing can cost much more than expected
It came as a surprise to me, when I found this warning in the
MDN documentation
for arguments
:
The JavaScript interpreter in modern browsers is doing lots of optimizations,
which are turned off if arguments
is leaked outside of a function.
Leaking arguments
happens in following cases:
- When the function returns
arguments
. - When the function passes
arguments
to another function (such ascall()
in the classicArray.prototype.slice.call(arguments)
). - When
arguments
get captured in a closure.
See Petka Antonov’s explanation.
Workaround
The solution is to create an array with the expected size, and then copy one item after the other.
Here is a working example:
const args = new Array (arguments.length);
for (var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
and obviously, you should not extract this piece of code when doing some refactoring, as you’d end up with leaking again, defeating the purpose of this code snippet.
EDIT: see this follow-up post for the idiomatic ES2015 way of writing equivalent code.