Właśnie czytałem to pytanie i chciałem wypróbować metodę aliasu zamiast metody opakowywania funkcji, ale nie mogłem sprawić, aby działała w Firefoksie 3 lub 3.5beta4 lub Google Chrome, zarówno w ich oknach debugowania, jak i na testowej stronie internetowej.
Firebug:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
Jeśli umieszczę to na stronie internetowej, wywołanie myAlias daje mi następujący błąd:
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 (dla jasności wstawiono >>>):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
Na stronie testowej otrzymuję to samo „Nielegalne wywołanie”.
czy robię coś źle? Czy ktoś inny może to odtworzyć?
Co dziwne, właśnie spróbowałem i działa w IE8.
Odpowiedzi:
Musisz powiązać tę metodę z obiektem dokumentu. Popatrz:
>>> $ = 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();">
Kiedy robisz prosty alias, funkcja jest wywoływana na obiekcie globalnym, a nie na obiekcie dokumentu. Użyj techniki zwanej domknięciami, aby to naprawić:
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();">
W ten sposób nie stracisz odniesienia do oryginalnego obiektu.
W 2012 roku pojawiła się nowa
bind
metoda z ES5, która pozwala nam to zrobić w bardziej wyszukany sposób:>>> $ = document.getElementById.bind(document) >>> $('bn_home') <body id="bn_home" onload="init();">
źródło
Kopałem głęboko, aby zrozumieć to szczególne zachowanie i myślę, że znalazłem dobre wytłumaczenie.
Zanim przejdę do tego, dlaczego nie możesz aliasować
document.getElementById
, spróbuję wyjaśnić, jak działają funkcje / obiekty JavaScript.Za każdym razem, gdy wywołujesz funkcję JavaScript, interpreter JavaScript określa zakres i przekazuje go do funkcji.
Rozważ następującą funkcję:
function sum(a, b) { return a + b; } sum(10, 20); // returns 30;
Ta funkcja jest zadeklarowana w zakresie okna i kiedy ją wywołasz, wartość
this
wewnątrz funkcji sum będzieWindow
obiektem globalnym .W przypadku funkcji „sum” nie ma znaczenia, jaka jest wartość „this”, ponieważ jej nie używa.
Rozważ następującą funkcję:
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.
Kiedy wywołujesz funkcję dave.getAge, interpreter JavaScript widzi, że wywołujesz funkcję getAge na
dave
obiekcie, więc ustawiathis
jądave
i wywołujegetAge
.getAge()
wróci poprawnie100
.Być może wiesz, że w JavaScript możesz określić zakres za pomocą
apply
metody. Spróbujmy tego.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.
W powyższym wierszu, zamiast pozwolić JavaScript decydować o zakresie, przekazujesz zakres ręcznie jako
bob
obiekt.getAge
będzie teraz powrócić200
, nawet jeśli „myśli” dzwoniłeśgetAge
nadave
obiekcie.Jaki jest sens tego wszystkiego? Funkcje są „luźno” dołączone do obiektów JavaScript. Np. Możesz to zrobić
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
Zróbmy następny krok.
var dave = new Person(new Date(1909, 1, 1)); var ageMethod = dave.getAge; dave.getAge(); //returns 100; ageMethod(); //returns ?????
ageMethod
wykonanie generuje błąd! Co się stało?Jeśli czytasz moje powyższe punkty ostrożnie, by pamiętać, że
dave.getAge
metoda została wywołanadave
jakothis
obiekt natomiast JavaScript nie może określić zakres „” doageMethod
wykonania. Więc przeszedł globalne „Okno” jako „to”. Teraz, ponieważwindow
nie mabirthDate
właściwości,ageMethod
wykonanie zakończy się niepowodzeniem.Jak to naprawić? Prosty,
ageMethod.apply(dave); //returns 100.
Czy wszystkie powyższe miały sens? Jeśli tak, będziesz w stanie wyjaśnić, dlaczego nie możesz użyć aliasu
document.getElementById
:var $ = document.getElementById; $('someElement');
$
nazywa sięwindow
takthis
a jeśligetElementById
realizacja spodziewathis
siędocument
, że zawiedzie.Ponownie, aby to naprawić, możesz to zrobić
$.apply(document, ['someElement']);
Dlaczego więc działa w Internet Explorerze?
Nie wiem wewnętrzną implementację
getElementById
w IE, ale komentarz w źródle jQuery (inArray
implementacji metody) mówi, że w IEwindow == document
. W takim przypadku aliasingdocument.getElementById
powinien działać w IE.Aby dalej to zilustrować, stworzyłem rozbudowany przykład. Spójrz na
Person
poniższą funkcję.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(); }; }
Person
Oto jakgetAge
zachowują się różne metody w przypadku powyższej funkcji .Utwórzmy dwa obiekty za pomocą
Person
funkcji.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.
Od razu metoda getAge pobiera
yogi
obiekt jakothis
i wyprowadza100
.var ageAlias = yogi.getAge; console.log(ageAlias()); //Output: -1
JavaScript interpreter ustawia
window
obiekt jako,this
a naszagetAge
metoda zwróci-1
.console.log(ageAlias.apply(yogi)); //Output: 100
Jeśli ustawimy prawidłowy zakres, możesz użyć
ageAlias
metody.console.log(ageAlias.apply(anotherYogi)); //Output: 200
Jeśli przekażemy obiekt innej osoby, nadal poprawnie obliczy wiek.
var ageSmarterAlias = yogi.getAgeSmarter; console.log(ageSmarterAlias()); //Output: 100
ageSmarter
Funkcja schwytany oryginalnegothis
obiektu, więc teraz nie trzeba się martwić o dostarczanie prawidłowego zakresu.console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!
Problem
ageSmarter
polega na tym, że nigdy nie można ustawić zakresu na inny obiekt.var ageSmartestAlias = yogi.getAgeSmartest; console.log(ageSmartestAlias()); //Output: 100 console.log(ageSmartestAlias.apply(document)); //Output: 100
ageSmartest
Funkcja użyje pierwotnego zakresu, jeśli nieprawidłowy zakres jest dostarczany.console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
Nadal będziesz mógł przekazać inny
Person
obiektgetAgeSmartest
. :)źródło
To jest krótka odpowiedź.
Poniższy tekst tworzy kopię (odniesienie do) funkcji. Problem polega na tym, że teraz funkcja znajduje się na
window
obiekcie, gdy została zaprojektowana do życia nadocument
obiekcie.window.myAlias = document.getElementById
Są alternatywy
lub możesz użyć dwóch aliasów.
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
źródło
Kolejna krótka odpowiedź, tylko do zawijania / aliasingu
console.log
i podobnych metod logowania. Wszyscy oczekują, że znajdą się wconsole
kontekście.Jest to przydatne podczas pakowania
console.log
z pewnymi awaryjnymi rozwiązaniami, na wypadek gdybyś Ty lub Twoi użytkownicy mieli problemy podczas korzystania z przeglądarki, która (nie zawsze) tego nie obsługuje . Nie jest to jednak pełne rozwiązanie tego problemu, ponieważ wymaga rozszerzonych kontroli i rezerwy - Twój przebieg może się różnić.Przykład użycia ostrzeżeń
var warn = function(){ console.warn.apply(console, arguments); }
Następnie użyj go jak zwykle
warn("I need to debug a number and an object", 9999, { "user" : "Joel" });
Jeśli wolisz, aby zobaczyć swoje argumenty rejestrowania zapakowane w tablicy (ja, przez większość czasu), substytut
.apply(...)
z.call(...)
.Powinny pracować z
console.log()
,console.debug()
,console.info()
,console.warn()
,console.error()
. Zobacz takżeconsole
na MDN .konsola firebug
źródło
Oprócz innych świetnych odpowiedzi, istnieje prosta metoda jQuery $ .proxy .
Możesz alias w ten sposób:
myAlias = $.proxy(document, 'getElementById');
Lub
myAlias = $.proxy(document.getElementById, document);
źródło
$.proxy
tak naprawdę jest też opakowanie.W rzeczywistości nie można „czystego aliasu” funkcji na predefiniowanym obiekcie. Dlatego też najbliższy aliasingu, jaki można uzyskać bez zawijania, jest pozostanie w tym samym obiekcie:
>>> document.s = document.getElementById; >>> document.s('myid'); <div id="myid">
źródło