Zrozumienie $ .proxy () w jQuery

167

Z dokumentów rozumiem, .proxy()że zmieniłoby to zakres funkcji przekazanej jako argument. Czy ktoś mógłby mi to lepiej wyjaśnić? Dlaczego powinniśmy to robić?

Aditya Shukla
źródło
1
Zgodnie z dokumentacją „Ta metoda jest najbardziej przydatna do dołączania procedur obsługi zdarzeń do elementu, w którym kontekst wskazuje z powrotem na inny obiekt. Ponadto jQuery zapewnia, że ​​nawet jeśli powiążesz funkcję zwróconą z jQuery.proxy (), nadal rozpinaj poprawną funkcję, jeśli przekazano oryginał ". Czy jest coś konkretnego w tym wyrażeniu, którego ci brakuje?
bzlm
1
To jest jasne tutaj Dodatkowo, jQuery daje pewność, że nawet jeśli wiążą funkcja wrócił z jQuery.proxy () to będzie nadal unbind prawidłowa funkcja, jeśli przeszły oryginalny”.Co oznaczało przez oryginalne?
Aditya Shukla
Oryginał to ten, dla którego utworzono proxy. Ale skoro nie rozumiesz w pełni tych rzeczy, czy na pewno musisz ich użyć?
bzlm
1
Oto świetny samouczek wideo autorstwa nettuts pokazujący, jak działa $ .proxy. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein
1
@bzlm, czytałem dokumentację jquery, kiedy wpadłem na tę metodę.
Aditya Shukla,

Odpowiedzi:

381

Ostatecznie zapewnia, że ​​wartość this w funkcji będzie wartością, której pragniesz.

Typowym przykładem jest to, setTimeoutktóre ma miejsce wewnątrz clickprocedury obsługi.

Weź to:

$('#myElement').click(function() {
        // In this function, "this" is our DOM element.
    $(this).addClass('aNewClass');
});

Zamiar jest dość prosty. Po myElementkliknięciu powinien otrzymać klasę aNewClass. Wewnątrz procedury obsługi thisreprezentuje kliknięty element.

Ale co by było, gdybyśmy chcieli krótkiego opóźnienia przed dodaniem klasy? Możemy użyć a, setTimeoutaby to osiągnąć, ale problem polega na tym, że niezależnie od funkcji, jaką damy setTimeout, wartość thiswewnątrz tej funkcji będzie windowzamiast naszego elementu.

$('#myElement').click(function() {
    setTimeout(function() {
          // Problem! In this function "this" is not our element!
        $(this).addClass('aNewClass');
    }, 1000);
});

Zatem zamiast tego możemy wywołać $.proxy()i wysłać do niej funkcję i wartość, do której chcemy przypisać this, a zwróci to funkcję, która zachowa tę wartość.

$('#myElement').click(function() {
   // ------------------v--------give $.proxy our function,
    setTimeout($.proxy(function() {
        $(this).addClass('aNewClass');  // Now "this" is again our element
    }, this), 1000);
   // ---^--------------and tell it that we want our DOM element to be the
   //                      value of "this" in the function
});

Więc po tym, jak podaliśmy $.proxy()funkcję i żądaną wartość this, zwróciła ona funkcję, która zapewni, że thisjest poprawnie ustawiona.

Jak to się dzieje? Po prostu zwraca anonimową funkcję, która wywołuje naszą funkcję za pomocą .apply()metody, która pozwala jawnie ustawić wartość this.

Uproszczone spojrzenie na zwracaną funkcję może wyglądać następująco:

function() {
    // v--------func is the function we gave to $.proxy
    func.apply( ctx );
    // ----------^------ ctx is the value we wanted for "this" (our DOM element)
}

Więc ta anonimowa funkcja jest dana setTimeouti wszystko, co robi, to wykonanie naszej oryginalnej funkcji w odpowiednim thiskontekście.

user113716
źródło
Jaka jest wartość używania $.proxy(function () {...}, this)zamiast (function() {...}).call(this)? Czy jest jakaś różnica?
Justin Morgan
11
@JustinMorgan: z .calltobą natychmiast wywołujesz funkcję. W $.proxyprzypadku jest to tak, jakby Function.prototype.bindzwracała nową funkcję. Ta nowa funkcja ma thiswartość trwale związany, tak, że gdy jest on przekazywany do setTimeout, i setTimeoutwywołuje funkcję później, to nadal mają prawidłową thiswartość.
stan szary nadchodzi
2
Jaka jest przewaga tej techniki, jeśli w ogóle, nad czymś takim? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg
1
W tym przykładzie nie musisz używać metody $ .proxy. Zamiast tego możesz po prostu przepisać ją ponownie w ten sposób $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / nowy kontekst poprzez zmienną zadeklarowaną w zakresie metody obsługi $ (that) .addClass ('aNewClass');}, 1000);});
paul
4
Anonimowy użytkownik z 112 tys. Rep., Przerażająco dobrą znajomością JavaScript / jQuery, którego nie widziano od października 2011 ... Może John Resig?
cantera
49

Bez wchodzenia w szczegóły (co byłoby konieczne, ponieważ dotyczy to kontekstu w ECMAScript, tej zmiennej kontekstu itp.)

Istnieją trzy różne typy „kontekstów” w ECMA / JavaScript:

  • Kontekst globalny
  • Kontekst funkcji
  • kontekst ewaluacyjny

Każdy kod jest wykonywany w swoim kontekście wykonania . Jest jeden kontekst globalny i może istnieć wiele przykładów kontekstów funkcyjnych (i ewaluacyjnych). Teraz interesująca część:

Każde wywołanie funkcji wchodzi w kontekst wykonania funkcji. Kontekst wykonania funkcji wygląda następująco:


Łańcuch zakresu obiektu aktywacji
to wartość

Więc ta wartość jest specjalnym obiektem, który jest powiązany z kontekstem wykonania. W ECMA- / Javascript są dwie funkcje, które mogą zmienić wartość w kontekście wykonywania funkcji:

.call()
.apply()

Jeśli mamy funkcję foobar(), możemy zmienić wartość, wywołując:

foobar.call({test: 5});

Teraz mogliśmy uzyskać dostęp foobardo przekazanego obiektu:

function foobar() { 
    this.test // === 5
}

Dokładnie to jQuery.proxy()robi. Pobiera functionand context(który jest niczym innym jak obiektem) i łączy funkcję przez wywołanie .call()lub .apply()i zwraca tę nową funkcję.

jAndy
źródło
1
Doskonałe wyjaśnienie, prostsze / lepsze niż oficjalne dokumenty jQuery dla tej funkcji
higuaro
4

Napisałem tę funkcję:

function my_proxy (func,obj)
{
    if (typeof(func)!="function")
        return;

    // If obj is empty or another set another object 
    if (!obj) obj=this;

    return function () { return func.apply(obj,arguments); }
}
sgv_test
źródło
1

Ten sam cel można osiągnąć za pomocą samowykonującej się funkcji „Natychmiastowo wywoływane wyrażenie funkcyjne, w skrócie: IIFE” :

    $('#myElement').click(function() {  
      (function(el){
         setTimeout(function() {
              // Problem! In this function "this" is not our element!
            el.addClass('colorme');
        }, 1000);
      })($(this)); // self executing function   
    });
.colorme{
  color:red;
  font-size:20px;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

  <div id="myElement">Click me</div>
</body>
</html>

Legendy
źródło
2
Zwykle nazywa się to „Wyrażeniem funkcji natychmiastowo wywoływanej” (IIFE), a nie „funkcją samowykonawczą”, zobacz en.wikipedia.org/wiki/Immediately-invoked_function_expression .
Chris Seed,