Compreendendo $ .proxy () no jQuery

167

A partir dos documentos, eu entendo que .proxy()isso mudaria o escopo da função passada como argumento. Alguém poderia me explicar isso melhor? Por que devemos fazer isso?

Aditya Shukla
fonte
1
De acordo com a documentação, "Este método é mais útil para anexar manipuladores de eventos a um elemento em que o contexto está voltando para um objeto diferente. Além disso, o jQuery garante que, mesmo que você vincule a função retornada pelo jQuery.proxy (), ele será ainda assim, desvincule a função correta, se passou o original ". Existe algo em particular sobre esse fraseado que você acha que falta?
22411 bzlm
1
Isso está claro aqui Além disso, jQuery garante que mesmo se você vincular a função retornou de jQuery.proxy () ainda vai desvincular a função correta, se aprovada a original" .O do significado de original?
Aditya Shukla
O original é aquele para o qual um proxy foi criado. Mas como você não entende completamente essas coisas, tem certeza de que precisa usá-las?
22411 bzlm
1
Aqui está um ótimo tutorial em vídeo de nettuts mostrando como $ .proxy funciona. http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
Hussein
1
@ bzlm, eu estava lendo a documentação do jquery quando me deparei com esse método.
Aditya Shukla

Respostas:

381

O que ele faz é garantir que o valor de thisem uma função seja o valor que você deseja.

Um exemplo comum é setTimeoutaquele que ocorre dentro de um clickmanipulador.

Pegue isso:

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

A intenção é bastante simples. Quando myElementé clicado, ele deve receber a turma aNewClass. Dentro do manipulador thisrepresenta o elemento que foi clicado.

Mas e se quiséssemos um pequeno atraso antes de adicionar a classe? Podemos usar a setTimeoutpara realizá-lo, mas o problema é que, independentemente da função que atribuímos setTimeout, o valor de thisdentro dessa função será em windowvez de nosso elemento.

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

Portanto, o que podemos fazer é chamar $.proxy(), enviando a função e o valor que queremos atribuir this, e ele retornará uma função que reterá esse 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
});

Então, depois de atribuirmos $.proxy()a função e o valor que desejamos this, ele retornou uma função que garantirá que thisseja definida corretamente.

Como isso acontece? Ele apenas retorna uma função anônima que chama nossa função usando o .apply()método, que permite definir explicitamente o valor de this.

Uma visão simplificada da função retornada pode parecer com:

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

Portanto, essa função anônima é atribuída setTimeoute tudo o que faz é executar nossa função original com o thiscontexto apropriado .

user113716
fonte
Qual é o valor de usar em $.proxy(function () {...}, this)vez de (function() {...}).call(this)? Existe alguma diferença?
23812 Justin Morgan
11
@ JustinMorgan: com .callvocê está chamando a função imediatamente. Com $.proxy, é como Function.prototype.bindonde ele retorna uma nova função. Essa nova função tem o thisvalor permanentemente vinculado, de modo que, quando é passado setTimeoute setTimeoutchama a função posteriormente, ainda terá o thisvalor correto .
estado cinza está chegando
2
Qual é a vantagem, se houver, dessa técnica sobre algo assim? $ ('# myElement'). click (function () {var el = $ (this); setTimeout (function () {el.addClass ('aNewClass');}, 1000);});
Greg
1
Você não precisa usar o método $ .proxy para este exemplo. Em vez disso, você pode simplesmente reescrevê-lo dessa forma $ ('# myElement'). Click (function () {var that = this; setTimeout (function () {/ / novo contexto através de uma variável declarada no escopo do método manipulador $ (that) .addClass ('aNewClass');}, 1000);});
paul
4
Um usuário anônimo com 112k repetições, um ótimo conhecimento sobre JavaScript / jQuery e que não é visto desde outubro de 2011 ... John Resig, talvez?
Cantera #
49

Sem entrar em maiores detalhes (o que seria necessário porque se trata de Contexto no ECMAScript, a variável this context etc.)

Existem três tipos diferentes de "contextos" no ECMA- / Javascript:

  • O contexto global
  • Contexto da função
  • contexto geral

Todo código é executado em seu contexto de execução . Existe um contexto global e pode haver muitas instâncias de contextos de função (e avaliação). Agora a parte interessante:

Toda chamada de uma função entra no contexto de execução da função. Um contexto de execução de uma função se parece com:

O
escopo do objeto de ativação encadeia
esse valor

Portanto, este valor é um objeto especial relacionado ao contexto de execução. Existem duas funções no ECMA- / Javascript que podem alterar esse valor em um contexto de execução de função:

.call()
.apply()

Se tivermos uma função foobar(), podemos alterar esse valor chamando:

foobar.call({test: 5});

Agora podemos acessar foobaro objeto que passamos:

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

É exatamente jQuery.proxy()isso que faz. É preciso um functione context(que é nada mais do que um objeto) e liga a função invocando .call()ou .apply()e retorna essa nova função.

jAndy
fonte
1
Excelente explicação, mais simples / melhor do que os docs jQuery oficiais para a função
higuaro
4

Eu escrevi esta função:

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

O mesmo objetivo pode ser alcançado usando a função de execução automática "Expressão de função imediatamente chamada, curta: 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>

Legendas
fonte
2
Isso geralmente é chamado de "Expressão de Função Imediatamente Invocada" (IIFE) em vez de "função de execução automática", consulte en.wikipedia.org/wiki/Immedately-invoked_function_expression .
Chris Semente