JavaScript arguments Weirdness!
As you know, arguments is a "magic undeclared variable" with a local scope present in each function body. This variable is an Object, with an Array like structure.
Nothing new so far, but I bet not everybody knew some arguments "feature" which is browser dependent or somehow re-usable.
Funny enough, even if we reassign indexed properties, these will not be exposes in the loop, but if we set an extra index or property, this one will probably be exposed.
And as soon as we change the length property, this one could be exposed (FF, IE) or not (Chrome). So what we have so far?
Since as I said arguments is always part of a function body, we could use this variable to know if some piece of code changed the Object prototype, rather than create a new object for each check.
Unfortunately, this is not worthy, because the empty inline Object instance creation performs better than a scope resolution for an undeclared variable as arguments is.
We could think to use arguments as a Function prototype in order to create objects ... wait a minute, NO! We could think to create a function that will directly make the magic argument public, rather than internal.
Unfortunately, the super innovative, secure, and efficient with advanced tab isolation and recovery browser Internet Explorer 8 crashes like a charm, so we need to rename the function into something less ambiguous for the magic JScript engine.
Nada, nien, nothing ... arguments is destined to be arguments, damn it!
Everything seems to be OK except Internet Explorer does not expose properties until you manually discover them:
Cool enough, we just discover a property and magic happens again:
Sometimes I forget we have to deal with a browser which logic is to bring internally even user comments:
As summary, to use arguments as prototype the function should be modified for IE:
In a valueOf operation, arguments[0] contains a sort of information, undefined, or "number", which could tell us if the object is going to be casted as a number:
I am sure every browser has some weird behavior with arguments variable so more than an inspiration for ArrayLike Objects, we cannot truly rely that much in this magic variable. What did you discover guys?
checkArgs(1, 2, 3);
function checkArgs(){
// even if we do not declare
// arguments, this one will
// be present with a length property
// equal to 3 and respective
// 1, 2, and 3 values at index
// 0, 1, and 2
arguments.length; // 3
arguments[0]; // 1
Array.prototype.join.call(arguments,"-");
// will produce 1-2-3
};
Nothing new so far, but I bet not everybody knew some arguments "feature" which is browser dependent or somehow re-usable.
The Common "For In" Behavior
There is a particular behavior about arguments variable, it does not expose properties in a "for in" loop.How can be possible? Simple enough, 0, 1, and 2 index properties are not user defined but JavaScript core assignments. Since JavaScript nature is extremely dynamic, it is simple to define what you want to expose in a loop, so far user defined properties or methods, and what should not be iterated in a "for in", something we will be able to manually introduce in next JavaScript release.
checkArgs(1, 2, 3);
function checkArgs(){
for(var key in arguments)
alert(key);
// nothing happened ...
};
Initial Weirdness
Funny enough, even if we reassign indexed properties, these will not be exposes in the loop, but if we set an extra index or property, this one will probably be exposed.
checkArgs(1, 2, 3);
function checkArgs(){
for(var i = 0; i < arguments.length; ++i)
arguments[i] = arguments[i];
// the extra index, manually assigned
arguments[3] = 4;
for(var key in arguments)
alert(key);
// will be 3 in FF and IE
// still nothing in Chrome
};
And as soon as we change the length property, this one could be exposed (FF, IE) or not (Chrome). So what we have so far?
Zero Stress for Extended Object.prototype Checks?
Since as I said arguments is always part of a function body, we could use this variable to know if some piece of code changed the Object prototype, rather than create a new object for each check.
function someGeniusExtendedObjectPrototype(){
for(var k in arguments)
return true;
return false;
};
Unfortunately, this is not worthy, because the empty inline Object instance creation performs better than a scope resolution for an undeclared variable as arguments is.
function someGeniusExtendedObjectPrototype(){
for(var k in {})return true;return false;
};
// that's it
An Instance With Not Exposed Indexes?
We could think to use arguments as a Function prototype in order to create objects ... wait a minute, NO! We could think to create a function that will directly make the magic argument public, rather than internal.
// global window scope
// where arguments has no
// meaning
// NOTE: do not use with Internet Explorer
function arguments(){
return arguments;
};
// let's test it
var a = arguments(1, 2, 3);
// loop ?
for(var k in a)
alert(k); // nothing!
// it "works"!
Unfortunately, the super innovative, secure, and efficient with advanced tab isolation and recovery browser Internet Explorer 8 crashes like a charm, so we need to rename the function into something less ambiguous for the magic JScript engine.
// use with Internet Explorer too
function args(){
return arguments;
};
A Magic Object Creation?
Using latest snippet, we could abuse about "for in" feature to create our favourite library ... maybe ...
function MyCoolCollection(){
// will be exposed in IE and FireFox
arguments.push = Array.prototype.push;
// will be exposed in FireFox
arguments.toString = Array.prototype.join;
return arguments;
};
var a = MyCoolCollection(1, 2, 3);
// nothing only in Chrome
for(var k in a)
alert(k);
// with this, IE will expose added index "3" and length
a.push(4);
alert(a); // 1,2,3,4
Nada, nien, nothing ... arguments is destined to be arguments, damn it!
arguments as prototype
Trying to discover more arguments freaking stuff, I used this magic variable as prototype:
(function(){"use strict";
// reusable in-scope constructor
function $(){};
// the magic ArgList function
window.ArgList = function ArgList(){
// assign arguments as prototype
$.prototype = arguments;
// return a new magic instance
return new $;
};
})();
var a = ArgList(1,2,3);
Everything seems to be OK except Internet Explorer does not expose properties until you manually discover them:
// after above snippet ...
Array.prototype.slice.call(a);
// ",," in Internet Explorer
// 1,2,3 in every other browser
Cool enough, we just discover a property and magic happens again:
a[1];
Array.prototype.slice.call(a);
// ",2," in Internet Explorer
Sometimes I forget we have to deal with a browser which logic is to bring internally even user comments:
var f = ( /* hi scope! */ function(){ /* hi bracket! */ } );
alert(f);
// will be exactly:
// ( /* hi scope! */ function(){ /* hi bracket! */ } )
// the magic of an efficient code parser
As summary, to use arguments as prototype the function should be modified for IE:
(function(){"use strict";
function $(){};
window.ArgList = function ArgList(){
$.prototype = arguments;
var o = new $, i = o.length;
while(i--)o[i]; // just access ...
return o;
};
})();
Firefox Specific Weirdness
arguments has some secret negative index in every Firefox (Spidermonkey) engine. For example, the index [-2] contains the length of the arguments object, but it does not perform faster than arguments.length, useless. The index [-3] contains the function itself:
function args(){
alert(arguments[-3] === arguments.callee);
alert(arguments[-2] === arguments.length);
};
In a valueOf operation, arguments[0] contains a sort of information, undefined, or "number", which could tell us if the object is going to be casted as a number:
var o = {
push:[].push,
length:0,
toString:[].join,
valueOf:function(){
return arguments[0] == "number" ? this.length : this.toString();
}
};
o.push(1, 2, 3);
alert(o); // 1,2,3
alert(o*1); // 3
Is That It?
I am sure every browser has some weird behavior with arguments variable so more than an inspiration for ArrayLike Objects, we cannot truly rely that much in this magic variable. What did you discover guys?
Comments
Post a Comment