J'ai creusé profondément pour comprendre ce comportement particulier et je pense avoir trouvé une bonne explication.
Avant d'expliquer pourquoi vous ne pouvez pas créer d'alias document.getElementById
, je vais essayer d'expliquer le fonctionnement des fonctions / objets JavaScript.
Chaque fois que vous appelez une fonction JavaScript, l'interpréteur JavaScript détermine une portée et la transmet à la fonction.
Considérez la fonction suivante:
function sum(a, b)
{
return a + b;
}
sum(10, 20);
Cette fonction est déclarée dans la portée Window et lorsque vous l'appelez, la valeur de l' this
intérieur de la fonction somme sera l' Window
objet global .
Pour la fonction «somme», la valeur de «ceci» n'a pas d'importance car elle ne l'utilise pas.
Considérez la fonction suivante:
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();
Lorsque vous appelez dave.getAge fonction, l'interpréteur JavaScript voit que vous appelez la fonction getAge sur l' dave
objet, il met this
à dave
et appelle la getAge
fonction. getAge()
reviendra correctement 100
.
Vous savez peut-être qu'en JavaScript, vous pouvez spécifier la portée à l'aide de la apply
méthode. Essayons ça.
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
dave.getAge.apply(bob);
Dans la ligne ci-dessus, au lieu de laisser JavaScript décider de la portée, vous passez la portée manuellement en tant bob
qu'objet. getAge
va maintenant revenir 200
même si vous «pensiez» avoir appelé getAge
l' dave
objet.
Quel est l'intérêt de tout ce qui précède? Les fonctions sont «vaguement» attachées à vos objets JavaScript. Par exemple, vous pouvez faire
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();
Passons à l'étape suivante.
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge();
ageMethod();
ageMethod
l'exécution lève une erreur! Qu'est-il arrivé?
Si vous lisez attentivement mes points ci-dessus, vous remarquerez que la dave.getAge
méthode a été appelée avec dave
comme this
objet alors que JavaScript ne pouvait pas déterminer la «portée» de l' ageMethod
exécution. Donc, il a passé la «fenêtre» globale comme «ceci». Maintenant qu'il window
n'a pas de birthDate
propriété, l' ageMethod
exécution échouera.
Comment régler ceci? Facile,
ageMethod.apply(dave);
Est-ce que tout ce qui précède avait du sens? Si tel est le cas, vous serez en mesure d'expliquer pourquoi vous ne pouvez pas créer d'alias document.getElementById
:
var $ = document.getElementById;
$('someElement');
$
est appelé avec window
comme this
et si l' getElementById
implémentation s'attend this
à l'être document
, elle échouera.
Encore une fois pour résoudre ce problème, vous pouvez le faire
$.apply(document, ['someElement']);
Alors, pourquoi cela fonctionne-t-il dans Internet Explorer?
Je ne sais pas la mise en œuvre interne getElementById
dans IE, mais un commentaire dans la source jQuery ( inArray
de mise en œuvre de la méthode) dit que dans IE, window == document
. Si tel est le cas, l'aliasing document.getElementById
devrait fonctionner dans IE.
Pour illustrer cela davantage, j'ai créé un exemple élaboré. Jetez un œil à la Person
fonction ci-dessous.
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();
};
}
Pour la Person
fonction ci-dessus, voici comment les différentes getAge
méthodes se comporteront.
Créons deux objets en utilisant Person
function.
var yogi = new Person(new Date(1909, 1,1));
var anotherYogi = new Person(new Date(1809, 1, 1));
console.log(yogi.getAge());
Simple, la méthode getAge obtient l' yogi
objet en tant this
que sortie 100
.
var ageAlias = yogi.getAge;
console.log(ageAlias());
L'interprète JavaScript définit l' window
objet comme this
et notre getAge
méthode retournera -1
.
console.log(ageAlias.apply(yogi));
Si nous définissons la bonne portée, vous pouvez utiliser ageAlias
method.
console.log(ageAlias.apply(anotherYogi));
Si nous passons dans un autre objet personne, il calculera toujours l'âge correctement.
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias());
La ageSmarter
fonction a capturé l' this
objet d' origine , vous n'avez donc plus à vous soucier de fournir une portée correcte.
console.log(ageSmarterAlias.apply(anotherYogi));
Le problème avec ageSmarter
est que vous ne pouvez jamais définir la portée sur un autre objet.
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias());
console.log(ageSmartestAlias.apply(document));
La ageSmartest
fonction utilisera la portée d'origine si une portée non valide est fournie.
console.log(ageSmartestAlias.apply(anotherYogi));
Vous pourrez toujours passer un autre Person
objet à getAgeSmartest
. :)