Comprensione di $ .proxy () in jQuery


167

Da documenti ho capito che .proxy()cambierebbe l'ambito della funzione passata come argomento. Qualcuno potrebbe spiegarmi meglio? Perché dovremmo farlo?


1
Secondo la documentazione, "Questo metodo è molto utile per associare i gestori di eventi a un elemento in cui il contesto punta a un altro oggetto. Inoltre, jQuery si assicura che anche se si associa la funzione restituita da jQuery.proxy () sblocca ancora la funzione corretta, se passava l'originale ". C'è qualcosa di particolare in quel frase che ti manca?
bzlm,

1
Questo non è chiaro qui Inoltre, jQuery si assicura che anche se si associa la funzione restituita da jQuery.proxy (), si annullerà comunque la funzione corretta, se viene passato l'originale ".Che cosa si intende per originale?
Aditya Shukla,

L'originale è quello per cui è stato creato un proxy. Ma dal momento che non afferri completamente queste cose, sei sicuro di doverle usare?
bzlm,

1
Ecco un ottimo tutorial video di nettuts che mostra come funziona $ .proxy. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein

1
@bzlm, stavo leggendo la documentazione di jquery quando ho scoperto questo metodo.
Aditya Shukla,

Risposte:


381

Ciò che alla fine fa è assicurarsi che il valore di thisin una funzione sia il valore desiderato.

Un esempio comune è in un setTimeoutche si svolge all'interno di un clickgestore.

Prendi questo:

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

L'intenzione è abbastanza semplice. Quando myElementviene cliccato, dovrebbe ottenere la classe aNewClass. All'interno del gestore thisrappresenta l'elemento su cui è stato fatto clic.

E se volessimo un breve ritardo prima di aggiungere la classe? Potremmo usare a setTimeoutper realizzarlo, ma il guaio è che qualunque sia la funzione che diamo setTimeout, il valore thisall'interno di quella funzione sarà windowinvece del nostro elemento.

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

Quindi quello che possiamo fare, invece, è chiamare $.proxy(), inviandogli la funzione e il valore che vogliamo assegnare thise restituirà una funzione che manterrà quel valore.

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

Quindi dopo che abbiamo dato $.proxy()la funzione e il valore che desideriamo this, ha restituito una funzione che garantirà che thissia impostata correttamente.

Come lo fa? Restituisce semplicemente una funzione anonima che chiama la nostra funzione utilizzando il .apply()metodo, che consente di impostare esplicitamente il valore di this.

Uno sguardo semplificato alla funzione che viene restituita può apparire come:

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

Quindi questa funzione anonima è data a setTimeout, e tutto ciò che fa è eseguire la nostra funzione originale nel giusto thiscontesto.


Qual è il valore dell'utilizzo $.proxy(function () {...}, this)piuttosto che (function() {...}).call(this)? C'è una differenza?
Justin Morgan,

11
@JustinMorgan: con .callte stai chiamando immediatamente la funzione. Con $.proxy, è come Function.prototype.binddove restituisce una nuova funzione. Quella nuova funzione ha il thisvalore associato in modo permanente, in modo che quando viene passato a setTimeout, e setTimeoutchiama la funzione in seguito, avrà ancora il thisvalore corretto .
lo stato grigio arriverà il

2
Qual è il vantaggio, se esiste, di questa tecnica rispetto a qualcosa del genere? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg,

1
Non è necessario utilizzare il metodo $ .proxy per questo esempio. Invece puoi semplicemente riscriverlo in questo modo $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / nuovo contesto attraverso una variabile dichiarata nell'ambito del metodo handler $ (that) .addClass ('aNewClass');}, 1000);});
paul

4
Un utente anonimo con una reputazione di 112k, una conoscenza JavaScript / jQuery spaventosa e non si vede da ottobre 2011 ... John Resig forse?
cantera,

49

Senza entrare nel dettaglio (ciò sarebbe necessario perché si tratta di Contesto in ECMAScript, questa variabile di contesto ecc.)

Esistono tre diversi tipi di "contesti" in ECMA- / Javascript:

  • Il contesto globale
  • Contesto funzionale
  • contesto eval

Ogni codice viene eseguito nel suo contesto di esecuzione . Esiste un contesto globale e possono esserci molti casi di contesti di funzione (e valutazione). Ora la parte interessante:

Ogni chiamata di una funzione entra nel contesto di esecuzione della funzione. Un contesto di esecuzione di una funzione è simile a:

L'ambito Activation Object
Scope
questo valore

Quindi questo valore è un oggetto speciale correlato al contesto di esecuzione. Esistono due funzioni in ECMA- / Javascript che possono modificare questo valore in un contesto di esecuzione della funzione:

.call()
.apply()

Se abbiamo una funzione foobar()possiamo cambiare questo valore chiamando:

foobar.call({test: 5});

Ora potremmo accedere foobarall'oggetto in cui siamo passati:

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

Questo è esattamente ciò che jQuery.proxy()fa. Prende un functione context(che non è altro che un oggetto) e collega la funzione invocando .call()o .apply()e restituisce quella nuova funzione.


1
Spiegazione eccellente, più semplice / migliore dei documenti ufficiali di jQuery per la funzione
higuaro,

4

Ho scritto questa funzione:

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

Lo stesso obiettivo può essere raggiunto utilizzando una funzione di auto-esecuzione "Espressione di funzione immediatamente richiamata, breve: 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>


2
Questa è di solito chiamata "IIFE (Immediately-Invoked Function Expression)" anziché "funzione di auto-esecuzione", vedere en.wikipedia.org/wiki/Immediately-invoked_function_expression .
Chris Seed,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.