Aliasing funkcji JavaScript nie działa
Właśnie czytałem to pytanie i chciałem spróbować metody alias zamiast metody wrapper funkcji, ale nie mogłem go uruchomić ani w Firefoksie 3, ani 3. 5beta4, ani w 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ę go na stronie internetowej, wywołanie do myAlias daje mi ten 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 (z > > > ' S wstawione dla jasności):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
I na stronie testowej dostaję ta sama "nielegalna inwokacja".
Czy robię coś nie tak? Czy ktoś jeszcze może to odtworzyć? Poza tym, co dziwne, po prostu próbowałem i działa w IE8.6 answers
Musisz powiązać tę metodę z obiektem document. Wygląd:
>>> $ = 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();">
Gdy wykonujesz prosty alias, funkcja jest wywoływana na obiekcie globalnym, a nie na obiekcie dokumentu. Aby to naprawić, użyj techniki zwanej zamknięciami:
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 metoda bind
od ES5, która pozwala nam to zrobić w bardziej fantazyjny sposób:
>>> $ = document.getElementById.bind(document)
>>> $('bn_home')
<body id="bn_home" onload="init();">
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-28 09:28:19
Kopałem głęboko, aby zrozumieć to zachowanie i myślę, że znalazłem dobre wyjaśnienie.
Zanim przejdę do tego, dlaczego nie jesteś w stanie alias document.getElementById
, postaram się 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żmy następującą funkcję:
function sum(a, b)
{
return a + b;
}
sum(10, 20); // returns 30;
Ta funkcja jest zadeklarowana w obszarze okna i gdy ją wywołujesz wartość this
wewnątrz funkcji sum będzie globalnym obiektem Window
.
Dla funkcji' sum 'nie ma znaczenia, jaka jest wartość' this', ponieważ jej nie używa.
Rozważmy 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 zadzwonisz do dave ' a.funkcja getAge, interpreter JavaScript widzi, że wywołujesz funkcję getAge w obiekcie dave
, więc ustawia this
na dave
i wywołuje funkcję getAge
. getAge()
poprawnie powróci 100
.
Możesz wiedzieć, że w JavaScript możesz określić zakres za pomocą metody apply
. Spróbujmy.
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ższej linii, zamiast pozwolić JavaScript decydować o zakresie, przekazujesz go ręcznie jako obiekt bob
. getAge
powróci teraz 200
, nawet jeśli "myślałeś", że zadzwoniłeś getAge
na dave
obiekt.
Jaki jest sens tego wszystkiego? Funkcje są "luźno" dołączone do obiektów JavaScript. Np. można do
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 kolejny krok.
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge(); //returns 100;
ageMethod(); //returns ?????
ageMethod
egzekucja wyrzuca błąd! Co się stało?
Jeśli uważnie przeczytasz moje powyższe punkty, zauważysz, że metoda dave.getAge
została wywołana z dave
jako obiekt this
, podczas gdy JavaScript nie mógł określić "zakresu" dla ageMethod
wykonania. Więc przeszło globalne "okno" jako "to". Teraz, gdy window
nie ma właściwości birthDate
, ageMethod
egzekucja zakończy się niepowodzeniem.
ageMethod.apply(dave); //returns 100.
Czy to wszystko miało sens? Jeśli tak, to będziesz w stanie wyjaśnić, dlaczego nie jesteś w stanie alias
document.getElementById
:
var $ = document.getElementById;
$('someElement');
$
jest wywoływana z window
jako this
i jeśli getElementById
implementacja oczekuje {[19] } na document
, to się nie powiedzie.
Ponownie, aby to naprawić, możesz zrobić
$.apply(document, ['someElement']);
Więc dlaczego to działa w Internet Explorer?
Nie znam wewnętrznej implementacji getElementById
W IE, ale komentarz w jQuery source (inArray
implementacja metody) mówi, że w IE, window == document
. W takim przypadku aliasing document.getElementById
powinien działać w IE.
Aby zilustrować to dalej, stworzyłem rozbudowany przykład. Spójrz na funkcję Person
poniżej.
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();
};
}
Dla powyższej funkcji Person
, oto jak będą się zachowywać różne metody getAge
.
Stwórzmy dwa obiekty za pomocą funkcji Person
.
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.
Prosto do przodu, metoda getAge otrzymuje yogi
obiekt jako this
i wyjścia 100
.
var ageAlias = yogi.getAge;
console.log(ageAlias()); //Output: -1
JavaScript interepreter ustawia obiekt window
jako this
, a nasza metoda getAge
zwróci -1
.
console.log(ageAlias.apply(yogi)); //Output: 100
Jeśli ustawimy poprawny zakres, możesz użyć metody ageAlias
.
console.log(ageAlias.apply(anotherYogi)); //Output: 200
Jeśli przejdziemy do jakiegoś obiektu innej osoby, to nadal poprawnie obliczy wiek.
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias()); //Output: 100
Funkcja ageSmarter
przechwyciła oryginalny obiekt this
, więc teraz nie musisz się martwić o podanie poprawnego zakres.
console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!
Problem z 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
Funkcja ageSmartest
użyje oryginalnego zakresu, jeśli zostanie podany nieprawidłowy zakres.
console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
Nadal będziesz mógł przekazać inny Person
obiekt do getAgeSmartest
. :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-30 19:45:18
To krótka odpowiedź.
Poniżej znajduje się Kopia (odniesienie do) funkcji. Problem polega na tym, że teraz funkcja znajduje się na obiekcie window
, gdy została zaprojektowana do życia na obiekcie document
.
window.myAlias = document.getElementById
Alternatywami są
- W tym celu należy użyć wrappera (już wspomnianego przez Fabiena Ménagera)
-
Lub możesz użyć dwóch aliasów.
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-03 17:55:28
Kolejna krótka odpowiedź, tylko dla owijania / aliasingu console.log
i podobnych metod logowania. Wszyscy oczekują, że będą w kontekście console
.
Jest to przydatne podczas owijania console.log
z pewnymi zapasami, na wypadek, gdybyś ty lub Twoi użytkownicy wpadli w kłopoty, gdy używasz przeglądarki, która nie (zawsze) ją obsługuje. Nie jest to jednak pełne rozwiązanie tego problemu, ponieważ należy go rozszerzyć i cofnąć-przebieg może się różnić.
Przykład użycia warnings
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 twoje argumenty logowania były zawinięte w tablicę (robię to najczęściej), zastąp .apply(...)
z .call(...)
.
Powinien pracować z console.log()
, console.debug()
, console.info()
, console.warn()
, console.error()
. console
na MDN .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:09
Oprócz innych świetnych odpowiedzi, istnieje prosta metoda jQuery $.proxy .
Możesz nazwać tak:
myAlias = $.proxy(document, 'getElementById');
Lub
myAlias = $.proxy(document.getElementById, document);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-17 04:23:40
W rzeczywistości nie można "czystego aliasu" funkcji na predefiniowanym obiekcie. Dlatego najbliżej aliasingu można uzyskać bez zawijania, pozostając w tym samym obiekcie:
>>> document.s = document.getElementById;
>>> document.s('myid');
<div id="myid">
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-04 17:52:24