Estaba leyendo esta pregunta y quería probar el método de alias en lugar del método de envoltura de función, pero parece que no pude hacerlo funcionar en Firefox 3 o 3.5beta4, o Google Chrome, tanto en sus ventanas de depuración como en una página web de prueba.
Firebug:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
Si lo pongo en una página web, la llamada a myAlias me da este error:
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 (con >>> insertado para mayor claridad):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
Y en la página de prueba, obtengo la misma "Invocación ilegal".
¿Estoy haciendo algo mal? ¿Alguien más puede reproducir esto?
Además, por extraño que parezca, lo intenté y funciona en IE8.
Respuestas:
Tienes que vincular ese método al objeto del documento. Mira:
>>> $ = 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();">
Cuando está haciendo un alias simple, la función se llama en el objeto global, no en el objeto del documento. Utilice una técnica llamada cierres para solucionar este problema:
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 esta forma no perderá la referencia al objeto original.
En 2012, existe el nuevo
bind
método de ES5 que nos permite hacer esto de una manera más elegante:>>> $ = document.getElementById.bind(document) >>> $('bn_home') <body id="bn_home" onload="init();">
fuente
Investigué profundamente para comprender este comportamiento en particular y creo que he encontrado una buena explicación.
Antes de explicar por qué no puede crear un alias
document.getElementById
, intentaré explicar cómo funcionan las funciones / objetos de JavaScript.Siempre que invoca una función de JavaScript, el intérprete de JavaScript determina un alcance y lo pasa a la función.
Considere la siguiente función:
function sum(a, b) { return a + b; } sum(10, 20); // returns 30;
Esta función se declara en el ámbito de la ventana y cuando la invoca, el valor de
this
dentro de la función de suma será elWindow
objeto global .Para la función 'suma', no importa cuál sea el valor de 'esto' ya que no lo está usando.
Considere la siguiente función:
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.
Cuando se llama a la función dave.getAge, el intérprete de JavaScript ve que está llamando la función getAge en el
dave
objeto, por lo que ponethis
adave
y llama a lagetAge
función.getAge()
regresará correctamente100
.Es posible que sepa que en JavaScript puede especificar el alcance utilizando el
apply
método. Probemos eso.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.
En la línea anterior, en lugar de dejar que JavaScript decida el alcance, está pasando el alcance manualmente como
bob
objeto.getAge
ahora regresará200
a pesar de que "pensó" que llamógetAge
aldave
objeto.¿Cuál es el punto de todo lo anterior? Las funciones se adjuntan "libremente" a sus objetos JavaScript. Por ejemplo, puedes hacer
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
Demos el siguiente paso.
var dave = new Person(new Date(1909, 1, 1)); var ageMethod = dave.getAge; dave.getAge(); //returns 100; ageMethod(); //returns ?????
ageMethod
¡la ejecución arroja un error! ¿Que pasó?Si usted lee mis puntos por encima con cuidado, que le cuenta que
dave.getAge
el método fue llamado condave
comothis
objeto mientras que JavaScript no pudo determinar el 'alcance' deageMethod
ejecución. Así que pasó la 'Ventana' global como 'esto'. Ahorawindow
que no tiene unabirthDate
propiedad, laageMethod
ejecución fallará.¿Cómo arreglar esto? Sencillo,
ageMethod.apply(dave); //returns 100.
¿Todo lo anterior tenía sentido? Si es así, entonces podrá explicar por qué no puede usar un alias
document.getElementById
:var $ = document.getElementById; $('someElement');
$
se llama conwindow
tanthis
y sigetElementById
la aplicación está a la esperathis
de serdocument
, se producirá un error.Nuevamente para solucionar esto, puede hacer
$.apply(document, ['someElement']);
Entonces, ¿por qué funciona en Internet Explorer?
No sé la implementación interna de
getElementById
en IE, pero un comentario en la fuente de jQuery (inArray
implementación del método) dice que en el IE,window == document
. Si ese es el caso, entonces el aliasingdocument.getElementById
debería funcionar en IE.Para ilustrar esto aún más, he creado un ejemplo elaborado. Eche un vistazo a la
Person
función a continuación.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(); }; }
Para la
Person
función anterior, así es comogetAge
se comportarán los distintos métodos.Creemos dos objetos usando la
Person
función.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.
Directo, el método getAge obtiene el
yogi
objeto comothis
y la salida100
.var ageAlias = yogi.getAge; console.log(ageAlias()); //Output: -1
El intérprete de JavaScript establece el
window
objeto comothis
y nuestrogetAge
método regresará-1
.console.log(ageAlias.apply(yogi)); //Output: 100
Si configuramos el alcance correcto, puede usar el
ageAlias
método.console.log(ageAlias.apply(anotherYogi)); //Output: 200
Si pasamos algún objeto de otra persona, aún calculará la edad correctamente.
var ageSmarterAlias = yogi.getAgeSmarter; console.log(ageSmarterAlias()); //Output: 100
La
ageSmarter
función capturó elthis
objeto original, por lo que ahora no tiene que preocuparse por suministrar el alcance correcto.console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!
El problema
ageSmarter
es que nunca se puede establecer el alcance en otro objeto.var ageSmartestAlias = yogi.getAgeSmartest; console.log(ageSmartestAlias()); //Output: 100 console.log(ageSmartestAlias.apply(document)); //Output: 100
La
ageSmartest
función utilizará el alcance original si se proporciona un alcance no válido.console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
Aún podrá pasar otro
Person
objeto agetAgeSmartest
. :)fuente
Esta es una respuesta corta.
Lo siguiente hace una copia de (una referencia a) la función. El problema es que ahora la función está en el
window
objeto cuando fue diseñado para vivir en eldocument
objeto.window.myAlias = document.getElementById
Las alternativas son
o puede utilizar dos alias.
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
fuente
Otra respuesta corta, solo para envoltura / aliasing
console.log
y métodos de registro similares. Todos esperan estar en elconsole
contexto.Esto se puede utilizar cuando se envuelve
console.log
con algunas alternativas, en caso de que usted o sus usuarios tengan problemas al usar un navegador que (siempre) no lo admite . Sin embargo, esta no es una solución completa para ese problema, ya que necesita una revisión ampliada y una alternativa; su kilometraje puede variar.Ejemplo usando advertencias
var warn = function(){ console.warn.apply(console, arguments); }
Entonces úsalo como de costumbre
warn("I need to debug a number and an object", 9999, { "user" : "Joel" });
Si prefiere ver sus argumentos de registro envueltos en una matriz (yo lo hago, la mayoría de las veces), sustitúyalos
.apply(...)
por.call(...)
.En caso de trabajar con
console.log()
,console.debug()
,console.info()
,console.warn()
,console.error()
. Consulte tambiénconsole
MDN .consola chinche
fuente
Además de otras excelentes respuestas, existe un método simple de jQuery $ .proxy .
Puedes usar un alias como este:
myAlias = $.proxy(document, 'getElementById');
O
myAlias = $.proxy(document.getElementById, document);
fuente
$.proxy
también es realmente un envoltorio.En realidad , no puede "crear un alias puro" de una función en un objeto predefinido. Por lo tanto, lo más cercano al alias que puede obtener sin ajustar es permanecer dentro del mismo objeto:
>>> document.s = document.getElementById; >>> document.s('myid'); <div id="myid">
fuente