Je lisais juste cette question et je voulais essayer la méthode d'alias plutôt que la méthode de wrapper de fonction, mais je n'arrivais pas à la faire fonctionner dans Firefox 3 ou 3.5beta4, ou Google Chrome, à la fois dans leurs fenêtres de débogage et dans une page Web de test.
Pyromane:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
Si je le mets dans une page Web, l'appel à myAlias me donne cette erreur:
uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame :: file:///[...snip...]/test.html :: <TOP_LEVEL> :: line 7" data: no]
Chrome (avec >>> inséré pour plus de clarté):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
Et dans la page de test, j'obtiens la même "invocation illégale".
Est-ce que je fais quelque chose de mal? Quelqu'un d'autre peut-il reproduire cela?
Aussi, assez curieusement, j'ai juste essayé et cela fonctionne dans IE8.
Réponses:
Vous devez lier cette méthode à l'objet document. Regardez:
>>> $ = document.getElementById getElementById() >>> $('bn_home') [Exception... "Cannot modify properties of a WrappedNative" ... anonymous :: line 72 data: no] >>> $.call(document, 'bn_home') <body id="bn_home" onload="init();">
Lorsque vous faites un simple alias, la fonction est appelée sur l'objet global, pas sur l'objet document. Utilisez une technique appelée fermetures pour résoudre ce problème:
function makeAlias(object, name) { var fn = object ? object[name] : null; if (typeof fn == 'undefined') return function () {} return function () { return fn.apply(object, arguments) } } $ = makeAlias(document, 'getElementById'); >>> $('bn_home') <body id="bn_home" onload="init();">
De cette façon, vous ne perdez pas la référence à l'objet d'origine.
En 2012, il y a la nouvelle
bind
méthode d'ES5 qui nous permet de le faire de manière plus sophistiquée:>>> $ = document.getElementById.bind(document) >>> $('bn_home') <body id="bn_home" onload="init();">
la source
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); // returns 30;
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(); //returns 100.
Lorsque vous appelez dave.getAge fonction, l'interpréteur JavaScript voit que vous appelez la fonction getAge sur l'
dave
objet, il metthis
àdave
et appelle lagetAge
fonction.getAge()
reviendra correctement100
.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)); //Age 100 in 2009 var bob = new Person(new Date(1809, 1, 1)); //Age 200 in 2009 dave.getAge.apply(bob); //returns 200.
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 revenir200
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(); //returns -1 dave.getAge(); //returns 100
Passons à l'étape suivante.
var dave = new Person(new Date(1909, 1, 1)); var ageMethod = dave.getAge; dave.getAge(); //returns 100; ageMethod(); //returns ?????
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 avecdave
commethis
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'ilwindow
n'a pas debirthDate
propriété, l'ageMethod
exécution échouera.Comment régler ceci? Facile,
ageMethod.apply(dave); //returns 100.
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é avecwindow
commethis
et si l'getElementById
implémentation s'attendthis
à l'êtredocument
, 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'aliasingdocument.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() { //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(); }; }
Pour la
Person
fonction ci-dessus, voici comment les différentesgetAge
méthodes se comporteront.Créons deux objets en utilisant
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.
Simple, la méthode getAge obtient l'
yogi
objet en tantthis
que sortie100
.var ageAlias = yogi.getAge; console.log(ageAlias()); //Output: -1
L'interprète JavaScript définit l'
window
objet commethis
et notregetAge
méthode retournera-1
.console.log(ageAlias.apply(yogi)); //Output: 100
Si nous définissons la bonne portée, vous pouvez utiliser
ageAlias
method.console.log(ageAlias.apply(anotherYogi)); //Output: 200
Si nous passons dans un autre objet personne, il calculera toujours l'âge correctement.
var ageSmarterAlias = yogi.getAgeSmarter; console.log(ageSmarterAlias()); //Output: 100
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)); //Output: 100 !!!
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()); //Output: 100 console.log(ageSmartestAlias.apply(document)); //Output: 100
La
ageSmartest
fonction utilisera la portée d'origine si une portée non valide est fournie.console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
Vous pourrez toujours passer un autre
Person
objet àgetAgeSmartest
. :)la source
Ceci est une réponse courte.
Ce qui suit fait une copie de (une référence à) la fonction. Le problème est que maintenant la fonction est sur l'
window
objet lorsqu'elle a été conçue pour vivre sur l'document
objet.window.myAlias = document.getElementById
Les alternatives sont
ou vous pouvez utiliser deux alias.
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
la source
Une autre réponse courte, juste pour le wrapping / aliasing
console.log
et les méthodes de journalisation similaires. Ils s'attendent tous à être dans leconsole
contexte.Ceci est utilisable lors de l'encapsulation
console.log
avec certaines solutions de secours, au cas où vous ou vos utilisateurs rencontriez des problèmes lors de l' utilisation d'un navigateur qui ne le prend pas (toujours) en charge . Ce n'est cependant pas une solution complète à ce problème, car il doit s'agir de vérifications étendues et d'une solution de secours - votre kilométrage peut varier.Exemple d'utilisation d'avertissements
var warn = function(){ console.warn.apply(console, arguments); }
Ensuite, utilisez-le comme d'habitude
warn("I need to debug a number and an object", 9999, { "user" : "Joel" });
Si vous préférez voir vos arguments de journalisation enveloppés dans un tableau (je le fais, la plupart du temps), remplacez-les
.apply(...)
par.call(...)
.Si le travail avec
console.log()
,console.debug()
,console.info()
,console.warn()
,console.error()
. Voir aussiconsole
sur MDN .console pyromane
la source
En plus d'autres bonnes réponses, il existe une méthode jQuery simple $ .proxy .
Vous pouvez alias comme ceci:
myAlias = $.proxy(document, 'getElementById');
Ou
myAlias = $.proxy(document.getElementById, document);
la source
$.proxy
sont aussi vraiment un emballage.En fait, vous ne pouvez pas «alias pur» une fonction sur un objet prédéfini. Par conséquent, le plus proche de l'aliasing que vous pouvez obtenir sans wrapping est de rester dans le même objet:
>>> document.s = document.getElementById; >>> document.s('myid'); <div id="myid">
la source