Eu estava lendo esta pergunta e queria tentar o método de alias em vez do método de wrapper de função, mas não consegui fazê-lo funcionar no Firefox 3 ou 3.5beta4 ou no Google Chrome, ambos em suas janelas de depuração e em uma página da web de teste.
Firebug:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
Se eu colocar em uma página da web, a chamada para myAlias me dará este erro:
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 (com >>> inserido para maior clareza):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
E na página de teste, recebo o mesmo "Invocação ilegal".
Estou fazendo algo errado? Alguém mais pode reproduzir isso?
Além disso, por incrível que pareça, eu apenas tentei e funciona no IE8.
Respostas:
Você deve vincular esse método ao objeto de documento. Veja:
>>> $ = 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();">
Quando você está criando um alias simples, a função é chamada no objeto global, não no objeto do documento. Use uma técnica chamada closures para corrigir isso:
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();">
Desta forma, você não perde a referência ao objeto original.
Em 2012, existe o novo
bind
método da ES5 que nos permite fazer isso de uma forma mais sofisticada:>>> $ = document.getElementById.bind(document) >>> $('bn_home') <body id="bn_home" onload="init();">
fonte
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); // returns 30;
Esta função é declarada no escopo Window e quando você a invoca, o valor de
this
dentro da função sum será oWindow
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(); //returns 100.
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 definethis
adave
e chama agetAge
função.getAge()
retornará corretamente100
.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)); //Age 100 in 2009 var bob = new Person(new Date(1809, 1, 1)); //Age 200 in 2009 dave.getAge.apply(bob); //returns 200.
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 chamougetAge
odave
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(); //returns -1 dave.getAge(); //returns 100
Vamos dar o próximo passo.
var dave = new Person(new Date(1909, 1, 1)); var ageMethod = dave.getAge; dave.getAge(); //returns 100; ageMethod(); //returns ?????
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 comdave
comothis
objeto, ao passo que o JavaScript não pôde determinar o 'escopo' deageMethod
execução. Portanto, passou a 'Janela' global como 'isto'. Agora comowindow
não tem umabirthDate
propriedade, aageMethod
execução irá falhar.Como consertar isto? Simples,
ageMethod.apply(dave); //returns 100.
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 comwindow
comothis
e segetElementById
a execução está esperandothis
para serdocument
, 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 IEwindow == document
,. Se for esse o caso, o aliasingdocument.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() { //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 a
Person
função acima, veja como os váriosgetAge
métodos se comportarão.Vamos criar dois objetos usando
Person
função.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.
Em linha reta, o método getAge obtém o
yogi
objeto comothis
e as saídas100
.var ageAlias = yogi.getAge; console.log(ageAlias()); //Output: -1
O intérprete de JavaScript define o
window
objeto comothis
e nossogetAge
método retornará-1
.console.log(ageAlias.apply(yogi)); //Output: 100
Se definirmos o escopo correto, você pode usar o
ageAlias
método.console.log(ageAlias.apply(anotherYogi)); //Output: 200
Se passarmos algum objeto de outra pessoa, ele ainda calculará a idade corretamente.
var ageSmarterAlias = yogi.getAgeSmarter; console.log(ageSmarterAlias()); //Output: 100
A
ageSmarter
função capturou othis
objeto original, então agora você não precisa se preocupar em fornecer o escopo correto.console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!
O problema
ageSmarter
é que você nunca pode definir o escopo para algum outro objeto.var ageSmartestAlias = yogi.getAgeSmartest; console.log(ageSmartestAlias()); //Output: 100 console.log(ageSmartestAlias.apply(document)); //Output: 100
A
ageSmartest
função usará o escopo original se um escopo inválido for fornecido.console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
Você ainda poderá passar outro
Person
objeto paragetAgeSmartest
. :)fonte
Esta é uma resposta curta.
O seguinte faz uma cópia de (uma referência à) função. O problema é que agora a função está no
window
objeto quando foi projetada para viver neledocument
.window.myAlias = document.getElementById
As alternativas são
ou você pode usar dois aliases.
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
fonte
Outra resposta curta, apenas para agrupamento / aliasing
console.log
e métodos de registro semelhantes. Todos eles esperam estar noconsole
contexto.Isso pode ser usado durante o empacotamento
console.log
com alguns substitutos, caso você ou seus usuários tenham problemas ao usar um navegador que (sempre) não oferece suporte . No entanto, esta não é uma solução completa para esse problema, pois precisa de verificações expandidas e um fallback - sua milhagem pode variar.Exemplo de uso de avisos
var warn = function(){ console.warn.apply(console, arguments); }
Em seguida, use-o normalmente
warn("I need to debug a number and an object", 9999, { "user" : "Joel" });
Se você preferir ver seus argumentos de log empacotados em um array (eu faço, na maioria das vezes), substitua
.apply(...)
por.call(...)
.Deve trabalhar com
console.log()
,console.debug()
,console.info()
,console.warn()
,console.error()
. Veja tambémconsole
no MDN .console firebug
fonte
Além de outras ótimas respostas, há o método jQuery simples $ .proxy .
Você pode usar um alias como este:
myAlias = $.proxy(document, 'getElementById');
Ou
myAlias = $.proxy(document.getElementById, document);
fonte
$.proxy
também é um invólucro.Na verdade, você não pode criar um "alias puro" para uma função em um objeto predefinido. Portanto, o mais próximo do aliasing que você pode obter sem agrupar é permanecer no mesmo objeto:
>>> document.s = document.getElementById; >>> document.s('myid'); <div id="myid">
fonte