Comprender $ .proxy () en jQuery

167

De los documentos , entiendo que .proxy()cambiaría el alcance de la función pasada como argumento. ¿Podría alguien explicarme esto mejor? ¿Por qué deberíamos hacer ésto?

Aditya Shukla
fuente
1
De acuerdo con la documentación, "Este método es más útil para adjuntar controladores de eventos a un elemento en el que el contexto apunta a un objeto diferente. Además, jQuery se asegura de que incluso si vincula la función devuelta por jQuery.proxy () aún desvincula la función correcta, si pasa el original ". ¿Hay algo en particular en esa frase que le falta?
bzlm
1
Esto no está claro aquí Además, jQuery se asegura de que, incluso si vincula la función devuelta por jQuery.proxy (), seguirá desvinculando la función correcta, si se pasa el original ". ¿Qué se entiende por original?
Aditya Shukla
El original es aquel para el que se creó un proxy. Pero como no comprende completamente estas cosas, ¿está seguro de que necesita usarlas?
bzlm
1
Aquí hay un gran video tutorial de nettuts que muestra cómo funciona $ .proxy. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein
1
@bzlm, estaba leyendo la documentación de jquery cuando descubrí este método.
Aditya Shukla

Respuestas:

381

Lo que finalmente hace es garantizar que el valor de thisuna función sea el valor que desee.

Un ejemplo común es el setTimeoutque tiene lugar dentro de un clickcontrolador.

Toma esto:

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

La intención es lo suficientemente simple. Cuando myElementse hace clic, debería obtener la clase aNewClass. Dentro del controlador thisrepresenta el elemento en el que se hizo clic.

Pero, ¿y si quisiéramos un pequeño retraso antes de agregar la clase? Podríamos usar a setTimeoutpara lograrlo, pero el problema es que cualquiera que sea la función que le demos setTimeout, el valor de thisdentro de esa función será en windowlugar de nuestro elemento.

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

Entonces, lo que podemos hacer es llamar $.proxy(), enviarle la función y el valor que queremos asignar this, y devolverá una función que retendrá ese valor.

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

Entonces, después de que le dimos $.proxy()la función y el valor que deseamos this, devolvió una función que garantizará que thisse establezca correctamente.

Como lo hace Simplemente devuelve una función anónima que llama a nuestra función utilizando el .apply()método, que le permite establecer explícitamente el valor de this.

Un aspecto simplificado de la función que se devuelve puede verse así:

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

Por lo tanto, se le asigna esta función anónima setTimeouty todo lo que hace es ejecutar nuestra función original con el thiscontexto adecuado .

usuario113716
fuente
¿Cuál es el valor de usar en $.proxy(function () {...}, this)lugar de (function() {...}).call(this)? ¿Hay una diferencia?
Justin Morgan
11
@JustinMorgan: con .callusted está llamando a la función de inmediato. Con $.proxy, es como Function.prototype.binddonde devuelve una nueva función. Esa nueva función tiene el thisvalor enlazado permanentemente, de modo que cuando se pasa setTimeouty setTimeoutllama a la función más tarde, seguirá teniendo el thisvalor correcto .
estado gris llegará el
2
¿Cuál es la ventaja, si la hay, de esta técnica sobre algo como esto? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg
1
No tiene que usar el método $ .proxy para este ejemplo. En su lugar, simplemente puede reescribirlo de esta manera $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / nuevo contexto a través de una variable declarada en el alcance del método del controlador $ (that) .addClass ('aNewClass');}, 1000);});
Paul
44
Un usuario anónimo con 112k rep, conocimiento de JavaScript / jQuery muy bueno, y no ha sido visto desde octubre de 2011 ... ¿John Resig quizás?
cantera
49

Sin entrar en mayor detalle (lo cual sería necesario porque se trata de Contexto en ECMAScript, la variable de contexto de este, etc.)

Hay tres tipos diferentes de "Contextos" en ECMA- / Javascript:

  • El contexto global
  • Contexto de la función
  • contexto de evaluación

Cada código se ejecuta en su contexto de ejecución . Hay un contexto global y puede haber muchas instancias de contextos de función (y evaluación). Ahora la parte interesante:

Cada llamada de una función ingresa al contexto de ejecución de la función. El contexto de ejecución de una función se ve así:

El objeto de activación
Alcance encadena
este valor

Entonces, este valor es un objeto especial que está relacionado con el contexto de ejecución. Hay dos funciones en ECMA- / Javascript que pueden cambiar este valor en un contexto de ejecución de función:

.call()
.apply()

Si tenemos una función foobar(), podemos cambiar este valor llamando a:

foobar.call({test: 5});

Ahora podríamos acceder en foobarel objeto que pasamos:

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

Esto es exactamente lo que jQuery.proxy()hace. Toma un functiony context(que no es más que un objeto) y vincula la función invocando .call()o .apply()y devuelve esa nueva función.

jAndy
fuente
1
Excelente explicación, más simple / mejor que los documentos oficiales de jQuery para la función
Higuaro
4

He escrito esta función:

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
fuente
1

Se puede lograr el mismo objetivo utilizando una función de ejecución automática "Expresión de función invocada inmediatamente, abreviatura: 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>

Leyendas
fuente
2
Eso generalmente se llama una "Expresión de función invocada inmediatamente" (IIFE) en lugar de una "función de ejecución automática", consulte en.wikipedia.org/wiki/Immediately-invoked_function_expression .
Chris Seed