Grundlegendes zu $ ​​.proxy () in jQuery


167

Aus Dokumenten verstehe ich, dass .proxy()dies den Umfang der als Argument übergebenen Funktion ändern würde. Könnte mir bitte jemand das besser erklären? Warum sollten wir das tun?


1
In der Dokumentation heißt es: "Diese Methode ist am nützlichsten, um Ereignishandler an ein Element anzuhängen, bei dem der Kontext auf ein anderes Objekt verweist. Außerdem stellt jQuery sicher, dass dies auch dann der Fall ist, wenn Sie die von jQuery.proxy () zurückgegebene Funktion binden." Entbinden Sie immer noch die richtige Funktion, wenn Sie das Original übergeben haben ". Gibt es etwas Besonderes an dieser Formulierung, das Ihnen fehlt?
Bzlm

1
Dies ist hier unklar. Außerdem stellt jQuery sicher, dass auch wenn Sie die von jQuery.proxy () zurückgegebene Funktion binden, die korrekte Funktion aufgehoben wird, wenn das Original übergeben wird. "Was ist mit Original gemeint?
Aditya Shukla

Das Original ist das, für das ein Proxy erstellt wurde. Aber da Sie dieses Zeug nicht vollständig verstehen, sind Sie sicher, dass Sie es verwenden müssen?
Bzlm

1
Hier ist ein großartiges Video-Tutorial von nettuts, das zeigt, wie $ .proxy funktioniert. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein

1
@bzlm, ich habe gerade die jquery-Dokumentation gelesen, als ich auf diese Methode gestoßen bin.
Aditya Shukla

Antworten:


381

Letztendlich wird sichergestellt, dass der Wert thiseiner Funktion der von Ihnen gewünschte Wert ist.

Ein häufiges Beispiel ist ein setTimeout, das in einem clickHandler stattfindet.

Nimm das:

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

Die Absicht ist einfach genug. Wenn darauf myElementgeklickt wird, sollte es die Klasse erhalten aNewClass. Im Handler befindet sich thisdas Element, auf das geklickt wurde.

Aber was ist, wenn wir eine kurze Verzögerung wünschen, bevor wir die Klasse hinzufügen? Wir könnten a verwenden setTimeout, um dies zu erreichen, aber das Problem ist, dass unabhängig von der Funktion, die wir geben setTimeout, der Wert thisinnerhalb dieser Funktion windowanstelle unseres Elements liegt.

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

Was wir stattdessen tun können, ist aufzurufen $.proxy()und ihm die Funktion und den Wert zu senden, denen wir zuweisen möchten this, und es wird eine Funktion zurückgegeben, die diesen Wert beibehält.

$('#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
});

Nachdem wir $.proxy()die Funktion und den gewünschten Wert angegeben haben this, wurde eine Funktion zurückgegeben, die sicherstellt, dass sie thisrichtig eingestellt ist.

Wie macht es das? Es wird nur eine anonyme Funktion zurückgegeben, die unsere Funktion mit der .apply()Methode aufruft , mit der der Wert von explizit festgelegt werden kann this.

Ein vereinfachter Blick auf die zurückgegebene Funktion könnte folgendermaßen aussehen:

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

Diese anonyme Funktion ist also gegeben setTimeout, und alles, was sie tut, ist, unsere ursprüngliche Funktion mit dem richtigen thisKontext auszuführen .


Was ist der Wert der Verwendung $.proxy(function () {...}, this)anstatt (function() {...}).call(this)? Ist da ein Unterschied?
Justin Morgan

11
@ JustinMorgan: Mit .calldir rufst du die Funktion sofort auf. Mit $.proxyist es so, Function.prototype.bindals würde eine neue Funktion zurückgegeben. Bei dieser neuen Funktion ist der thisWert permanent gebunden, sodass sie bei Übergabe an setTimeoutund setTimeoutspäterem Aufrufen der Funktion immer noch den richtigen thisWert hat.
grauer Zustand kommt

2
Was ist der Vorteil dieser Technik gegenüber so etwas? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg

1
Sie müssen für dieses Beispiel nicht die $ .proxy-Methode verwenden. Stattdessen können Sie sie einfach wie folgt umschreiben: $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / neuer Kontext durch eine Variable, die im Bereich der Handler-Methode $ (that) .addClass ('aNewClass') deklariert ist;}, 1000);});
Paul

4
Ein anonymer Benutzer mit 112.000 Repräsentanten, beängstigend guten JavaScript / jQuery-Kenntnissen und seit Oktober 2011 nicht mehr gesehen ... John Resig vielleicht?
Cantera

49

Ohne näher darauf einzugehen (was notwendig wäre, da es sich um den Kontext in ECMAScript, die Kontextvariable this usw. handelt)

Es gibt drei verschiedene Arten von "Kontexten" in ECMA- / Javascript:

  • Der globale Kontext
  • Funktionskontext
  • eval Kontext

Jeder Code wird in seinem Ausführungskontext ausgeführt . Es gibt einen globalen Kontext und es kann viele Instanzen von Funktionskontexten (und Bewertungskontexten) geben. Nun der interessante Teil:

Jeder Aufruf einer Funktion tritt in den Funktionsausführungskontext ein. Ein Ausführungskontext einer Funktion sieht folgendermaßen aus:

Die Aktivierung Object
Scope - Chain
diesen Wert

So ist der dieser Wert ist ein spezielles Objekt , das mit dem Ausführungskontext verwendet ist. In ECMA- / Javascript gibt es zwei Funktionen, die diesen Wert in einem Funktionsausführungskontext ändern können :

.call()
.apply()

Wenn wir eine Funktion foobar()haben, können wir diesen Wert ändern , indem wir Folgendes aufrufen:

foobar.call({test: 5});

Jetzt konnten wir auf foobardas Objekt zugreifen, das wir übergeben haben:

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

Genau das jQuery.proxy()macht es. Es nimmt ein functionund context(das nichts anderes als ein Objekt ist) und verknüpft die Funktion durch Aufrufen von .call()und .apply()und gibt diese neue Funktion zurück.


1
Hervorragende Erklärung, einfacher / besser als die offiziellen jQuery-Dokumente für die Funktion
Higuaro

4

Ich habe diese Funktion geschrieben:

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); }
}

1

Das gleiche Ziel kann mit einer selbstausführenden Funktion "Sofort aufgerufener Funktionsausdruck, kurz: IIFE" erreicht werden :

    $('#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>


2
Dies wird normalerweise als "Sofort aufgerufener Funktionsausdruck" (IIFE) und nicht als "selbstausführende Funktion" bezeichnet (siehe en.wikipedia.org/wiki/Immedially-invoked_function_expression) .
Chris Seed
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.