Comprendre $ .proxy () dans jQuery

167

D'après la documentation, je comprends que .proxy()cela changerait la portée de la fonction passée en argument. Quelqu'un pourrait-il mieux m'expliquer cela? Pourquoi devrions-nous faire cela?

Aditya Shukla
la source
1
Selon la documentation, "Cette méthode est la plus utile pour attacher des gestionnaires d'événements à un élément où le contexte pointe vers un objet différent. De plus, jQuery s'assure que même si vous liez la fonction retournée par jQuery.proxy (), il sera toujours dissocier la fonction correcte, si l'original est passé ". Y a-t-il quelque chose de particulier dans cette formulation qui vous manque?
bzlm
1
Ceci n'est pas clair ici De plus, jQuery s'assure que même si vous liez la fonction retournée par jQuery.proxy (), il dissociera toujours la fonction correcte, si vous passez l'original ".Qu'est-ce que signifie original?
Aditya Shukla
L'original est celui pour lequel un proxy a été créé. Mais comme vous ne comprenez pas complètement ce genre de choses, êtes-vous sûr de devoir l'utiliser?
bzlm
1
Voici un excellent didacticiel vidéo de nettuts montrant comment fonctionne $ .proxy. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein
1
@bzlm, je lisais la documentation de jquery quand je suis arrivé à cette méthode.
Aditya Shukla le

Réponses:

381

En fin de compte, il garantit que la valeur d' thisune fonction sera la valeur que vous désirez.

Un exemple courant est setTimeoutcelui qui a lieu à l'intérieur d'un clickgestionnaire.

Prends ça:

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

L'intention est assez simple. Quand myElementest cliqué, il devrait obtenir la classe aNewClass. À l'intérieur du gestionnaire thisreprésente l'élément sur lequel l'utilisateur a cliqué.

Mais que faire si nous voulions un court délai avant d'ajouter la classe? Nous pourrions utiliser a setTimeoutpour l'accomplir, mais le problème est que quelle que soit la fonction que nous attribuons setTimeout, la valeur de l' thisintérieur de cette fonction sera windowau lieu de notre élément.

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

Donc, ce que nous pouvons faire à la place, c'est d'appeler $.proxy(), de lui envoyer la fonction et la valeur que nous voulons attribuer this, et cela retournera une fonction qui conservera cette valeur.

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

Donc, après avoir donné $.proxy()la fonction et la valeur que nous voulons this, elle a renvoyé une fonction qui garantira qu'elle thisest correctement définie.

Comment ça marche? Il renvoie simplement une fonction anonyme qui appelle notre fonction à l'aide de la .apply()méthode, ce qui lui permet de définir explicitement la valeur de this.

Un aperçu simplifié de la fonction renvoyée peut ressembler à ceci:

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

Donc, cette fonction anonyme est donnée à setTimeout, et tout ce qu'elle fait est d'exécuter notre fonction d'origine avec le thiscontexte approprié .

utilisateur113716
la source
Quelle est la valeur d'utiliser $.proxy(function () {...}, this)plutôt que (function() {...}).call(this)? Y a-t-il une différence?
Justin Morgan
11
@JustinMorgan: avec .callvous appelez la fonction immédiatement. Avec $.proxy, c'est comme Function.prototype.bindlà où il renvoie une nouvelle fonction. Cette nouvelle fonction a la thisvaleur liée de façon permanente, de sorte que lorsqu'elle est transmise à setTimeoutet setTimeoutappelle la fonction plus tard, elle aura toujours la thisvaleur correcte .
l'état gris arrive
2
Quel est l'avantage, le cas échéant, de cette technique par rapport à quelque chose comme ça? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg
1
Vous n'êtes pas obligé d'utiliser la méthode $ .proxy pour cet exemple. À la place, vous pouvez simplement le réécrire comme ceci $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / nouveau contexte via une variable déclarée dans la portée de la méthode du gestionnaire $ (that) .addClass ('aNewClass');}, 1000);});
paul
4
Un utilisateur anonyme avec 112k représentants, une connaissance effrayante de JavaScript / jQuery, et qui n'a pas été vu depuis octobre 2011 ... John Resig peut-être?
cantera le
49

Sans entrer dans les détails (ce qui serait nécessaire car il s'agit de Contexte dans ECMAScript, la variable de contexte this etc.)

Il existe trois types différents de «contextes» dans ECMA- / Javascript:

  • Le contexte global
  • Contexte de la fonction
  • contexte d'évaluation

Chaque code est exécuté dans son contexte d'exécution . Il y en a un contexte global et il peut y avoir de nombreuses instances de contextes de fonction (et d'évaluation). Maintenant la partie intéressante:

Chaque appel d'une fonction entre dans le contexte d'exécution de la fonction. Un contexte d'exécution d'une fonction ressemble à ceci:

La
portée de l' objet d'activation Chaîne
cette valeur

Donc, cette valeur est un objet spécial qui est lié au contexte d'exécution. Il existe deux fonctions dans ECMA- / Javascript qui peuvent changer la valeur this dans un contexte d'exécution de fonction:

.call()
.apply()

Si nous avons une fonction, foobar()nous pouvons changer la valeur this en appelant:

foobar.call({test: 5});

Maintenant, nous pouvons accéder à foobarl'objet que nous avons passé:

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

C'est exactement ce que jQuery.proxy()fait. Il prend un functionand context(qui n'est rien d'autre qu'un objet) et lie la fonction en invoquant .call()ou .apply()et renvoie cette nouvelle fonction.

jAndy
la source
1
Excellente explication, plus simple / meilleure que la documentation officielle jQuery pour la fonction
higuaro
4

J'ai écrit cette fonction:

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
la source
1

Le même objectif peut être atteint en utilisant une fonction auto- exécutable "Expression de fonction immédiatement appelée, abrégée: 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>

Légendes
la source
2
C'est ce qu'on appelle généralement une «expression de fonction immédiatement invoquée» (IIFE) plutôt qu'une «fonction auto-exécutable», voir en.wikipedia.org/wiki/Immediately-invoked_function_expression .
Chris Seed