Pesquisei profundamente para entender esse comportamento específico e acho que encontrei uma boa explicação.
Antes de entrar no porquê de você não conseguir document.getElementById
usar um alias , tentarei explicar como as funções / objetos JavaScript funcionam.
Sempre que você invoca uma função JavaScript, o interpretador JavaScript determina um escopo e o passa para a função.
Considere a seguinte função:
function sum(a, b)
{
return a + b;
}
sum(10, 20);
Esta função é declarada no escopo Window e quando você a invoca, o valor de this
dentro da função sum será o Window
objeto global .
Para a função 'sum', não importa qual é o valor de 'this', pois não o está usando.
Considere a seguinte função:
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();
Quando você chama dave.getAge função, o interpretador JavaScript vê que você está chamando a função getAge sobre o dave
objeto, de modo que define this
a dave
e chama a getAge
função. getAge()
retornará corretamente 100
.
Você deve saber que em JavaScript você pode especificar o escopo usando o apply
método. Vamos tentar isso.
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
dave.getAge.apply(bob);
Na linha acima, em vez de deixar o JavaScript decidir o escopo, você está passando o escopo manualmente como o bob
objeto. getAge
agora retornará 200
mesmo que você "tenha pensado" que chamou getAge
o dave
objeto.
Qual é o ponto de tudo isso? As funções são anexadas "vagamente" aos objetos JavaScript. Por exemplo, você pode fazer
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();
dave.getAge();
Vamos dar o próximo passo.
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge();
ageMethod();
ageMethod
a execução gera um erro! O que aconteceu?
Se você ler meus pontos acima com atenção, notará que o dave.getAge
método foi chamado com dave
como this
objeto, ao passo que o JavaScript não pôde determinar o 'escopo' de ageMethod
execução. Portanto, passou a 'Janela' global como 'isto'. Agora como window
não tem uma birthDate
propriedade, a ageMethod
execução irá falhar.
Como consertar isto? Simples,
ageMethod.apply(dave);
Todas as opções acima faziam sentido? Em caso afirmativo, você poderá explicar por que não consegue criar um alias document.getElementById
:
var $ = document.getElementById;
$('someElement');
$
é chamado com window
como this
e se getElementById
a execução está esperando this
para ser document
, ele irá falhar.
Novamente, para corrigir isso, você pode fazer
$.apply(document, ['someElement']);
Então, por que funciona no Internet Explorer?
Não conheço a implementação interna do getElementById
no IE, mas um comentário no código fonte jQuery ( inArray
implementação do método) diz que no IE window == document
,. Se for esse o caso, o aliasing document.getElementById
deve funcionar no IE.
Para ilustrar isso ainda mais, criei um exemplo elaborado. Dê uma olhada na Person
função abaixo.
function Person(birthDate)
{
var self = this;
this.birthDate = birthDate;
this.getAge = function()
{
if(this.constructor == Person)
return new Date().getFullYear() - this.birthDate.getFullYear();
else
return -1;
};
this.getAgeSmarter = function()
{
return self.getAge();
};
this.getAgeSmartest = function()
{
var scope = this.constructor == Person ? this : self;
return scope.getAge();
};
}
Para a Person
função acima, veja como os vários getAge
métodos se comportarão.
Vamos criar dois objetos usando Person
função.
var yogi = new Person(new Date(1909, 1,1));
var anotherYogi = new Person(new Date(1809, 1, 1));
console.log(yogi.getAge());
Em linha reta, o método getAge obtém o yogi
objeto como this
e as saídas 100
.
var ageAlias = yogi.getAge;
console.log(ageAlias());
O intérprete de JavaScript define o window
objeto como this
e nosso getAge
método retornará -1
.
console.log(ageAlias.apply(yogi));
Se definirmos o escopo correto, você pode usar o ageAlias
método.
console.log(ageAlias.apply(anotherYogi));
Se passarmos algum objeto de outra pessoa, ele ainda calculará a idade corretamente.
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias());
A ageSmarter
função capturou o this
objeto original, então agora você não precisa se preocupar em fornecer o escopo correto.
console.log(ageSmarterAlias.apply(anotherYogi));
O problema ageSmarter
é que você nunca pode definir o escopo para algum outro objeto.
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias());
console.log(ageSmartestAlias.apply(document));
A ageSmartest
função usará o escopo original se um escopo inválido for fornecido.
console.log(ageSmartestAlias.apply(anotherYogi));
Você ainda poderá passar outro Person
objeto para getAgeSmartest
. :)