I dug deep to understand this particular behavior and I think I have found a good explanation.
Before I get in to why you are not able to alias document.getElementById, I will try to explain how JS functions/objects work.
Whenever you invoke a JS function, JS Interpreter determines a scope and passes it to the function.
Consider following function:
function sum(a, b)
{
return a + b;
}
sum(10, 20); // returns 30;
This function is declared in the Window scope and when you invoke it the value of 'this' inside the sum function will be the global 'Window' object.
For the 'sum' function, it doesn't matter what the value of 'this' is as it is not using it.
Consider following function:
function Person(birthDate)
{
this.birthDate = birthDate;
this.getAge = function() { return new Date().getFullYear() - this.birthDate.getFullYear(); };
}
var dave = new Person(new Date(1909, 1, 1));
dave.getAge(); //returns 100.
When you call dave.getAge function, JS interpreter sees that you are calling getAge function on the 'dave' object, so it sets 'this' to 'dave' and calls getAge function. getAge() will correctly return 100.
You may know that in JS you can specify the scope using the 'apply' method. Let's try that.
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
dave.getAge.apply(bob); //returns 200.
In the above line, instead of letting JS decide the scope, you are passing the scope manually as the 'bob' object. getAge will now return 200 even though you 'thought' you called getAge on the 'dave' object.
What's the point of all of the above? functions are 'loosely' attached to your JS objects.
E.g. you can do
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
bob.getAge = function() { return -1; };
bob.getAge(); //returns -1
dave.getAge(); //returns 100
Let's take the next step.
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge(); //returns 100;
ageMethod(); //returns ?????
ageMethod execution will throw an error! What happened?
If you read my above points carefully, you would note that dave.getAge method called with 'dave' as 'this' object whereas JS could not determine the 'scope' for ageMethod execution. So it passed global 'Window' as 'this'. Now as 'Window' doesn't have birthDate property, ageMethod execution will fail.
How to fix this? Simple,
ageMethod.apply(dave); //returns 100.
Did all of the above make sense? If it does, then you will be able to explain why you are not able to alias document.getElementById
var $ = document.getElementById;
$('someElement');
$ is called with 'Window' as 'this' and if getElementById implementation was expecting 'this' to be 'document', it will fail.
Again to fix this, you can do
$.apply(document, ['someElement']);
So why does it work in Internet Explorer?
I don't know the internal implementation of getElementById in IE, but a comment in jQuery source (inArray method implementation) says that in IE, window == document. If that's the case, then aliasing document.getElementById should work in IE.
To illustrate this further, I have created an elaborate example. Have a look at the Person function below.
function Person(birthDate)
{
var self = this;
this.birthDate = birthDate;
this.getAge = function()
{
//Let's make sure that getAge method was invoked
//with an object which was constructed from our Person function.
if(this.constructor == Person)
return new Date().getFullYear() - this.birthDate.getFullYear();
else
return -1;
};
//Smarter version of getAge function, it will always refer to the object
//it was created with.
this.getAgeSmarter = function()
{
return self.getAge();
};
//Smartest version of getAge function.
//It will try to use the most appropriate scope.
this.getAgeSmartest = function()
{
var scope = this.constructor == Person ? this : self;
return scope.getAge();
};
}
For the 'Person' function above, here's how the various getAge methods will behave.
Let's create two objects using 'Person' function.
var yogi = new Person(new Date(1909, 1,1)); //Age is 100
var anotherYogi = new Person(new Date(1809, 1, 1)); //Age is 200
console.log(yogi.getAge()); //Output = 100.
Straight forward, getAge method gets 'yogi' object as 'this' and outputs 100.
var ageAlias = yogi.getAge;
console.log(ageAlias()); //Outputs = -1;
JS interepreter sets 'Window' object as 'this' and our getAge method will return -1.
console.log(ageAlias.apply(yogi)); //Output = 100;
If we set the correct scope, you can use ageAlias method.
console.log(ageAlias.apply(anotherYogi)); //Output = 200;
If we pass in some other person object, it will still calculate age correctly.
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias()); //Output = 100;
The ageSmarter function captured the original 'this' object so now you don't have to worry about supplying correct scope.
console.log(ageSmarterAlias.apply(anotherYogi)); //Output = 100 !!!;
The problem with ageSmarter is that you can never set the scope to some other object.
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias()); //Output = 100;
console.log(ageSmartestAlias.apply(document)); //Ouput = 100;
The ageSmartest function will use the original scope if an invalid scope is supplied.
console.log(ageSmartestAlias.apply(anotherYogi)); //Ouput = 200;
You will still be able to pass another 'Person' object to getAgeSmartest. :)