The magical world of the JavaScript arguments object
So, I've known for a long time that the arguments
object that is available in a function is a bit odd
, but until recently I didn't realize how odd.
Last weekend, I was up in the northeast visiting friends and taking a little break from the south. As I waited at the airport for my flight home, I was browsing Hacker News for anything interesting, waiting for my zone to be called for boarding. I ended up at this guys blog where he was talking about adding contract-like tests to javascript using some extension macros thanks to Sweet.js.
Soon, I was on the plane and with nothing really to do and my chromebook in my carry-on I decided to do some experimentation with some different vanilla-js syntaxes for doing the same thing. Nothing serious, just wanted to play around.
However, I quickly stumbled onto something that I kind of blew my mind.
Here is a extremely simplified version what I did:
var clean = function (expected) {
if (typeof expected !== 'function') {
var capturedArguments = arguments;
expected = function () {
return capturedArguments[0];
};
}
return expected;
};
If expected
is not a function then I wanted to wrap it into a function, and returning the original value that was given in it's place.
Now, it's not the greatest example of idomatic javascript that I've ever written, but in my defense I was hurltleing through the air at 500mph in a box with wings.
So, I tried passing it a function:
var fn = clean(function () {
return 1;
});
fn(); // => 1
Cool, but then I tried it with something that wasn't a function:
var fn = clean(1);
fn();
I expected the return of fn()
to be 1
. However, much to my surprise, I got a reference back to fn
itself... Huh?
my former self though. I opened up the developer console, and added a breakpoint inside clean
. Shockingly – at least to me at the time – was that capturedArugments[0]
did not point at the value I originally had passed in... Instead, it pointed at the function that I had assigned to expected
.
*mindblown*
I had a feeling that I had stumbled onto something that I should have been aware of a long time ago. I wrote another function, something even simplier, in an attempt to understand what was going on.
var strangeAdd = function (x, y) {
arguments[0] = 10;
arguments[1] = 5;
return x + y;
};
And no matter what numbers that I fed into the function, the result would always come back 15
. In fact, no matter what data-type or object I fed in, I would always get 15
. Even more specifically, the only way that I would not get back 15
, was when I invoked the function with less than two arguments, eg. strangeAdd();
.
Then I tried something else.
var strangeAdd = function (x, y) {
x = 10;
y = 5;
return arguments[0] + arguments[1];
};
And lo-and-behold, the same result. It appeared like the arguments
object and the variables of the function are linked. Change one and it effects the other.
By this time, I was off the plane and was able to reconnect the internets – my source of information, and was able to find that this is a fairly well documented feature
of the arguments
object. I just had never heard of it.
*mindblown, again*
Note that this behavior is no longer supported when in strict mode:
var noMoreMonkeyBusiness = function (x, y) {
'use strict';
arguments[0] = 'foo';
arguments[1] = 'bar';
return x + y;
};
noMoreMonkeyBusiness(1, 2); // => 3
Phew... Sanity.
I'm really not sure when one would ever want to do this, but I'm sure somewhere in some code base this trick is used. But I'm more surprised that I just had never heard of it until now!