var self = isso?


182

Usando métodos de instância como retornos de chamada para manipuladores de eventos altera o escopo de thispartir "Meu instância" para "Seja qual for chamado apenas o retorno de chamada" . Então, meu código fica assim

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

Funciona, mas é a melhor maneira de fazê-lo? Parece estranho para mim.


15
Você deve evitar, selfpois existe um window.selfobjeto, e pode acabar usando isso acidentalmente se esquecer de declarar seu próprio selfvar (por exemplo, ao mover algum código). Isso pode ser irritante para detectar / depurar. Melhor usar algo parecido _this.
Alastair Maw


3
No Tutorial do JavaScript : "O valor de thisé dinâmico em JavaScript. É determinado quando a função é chamada , não quando é declarada."
DavidRR


A questão é, no escopo global self === this. Portanto, selfem contextos locais, faz sentido e segue o padrão.
programmer5000

Respostas:


210

Esta questão não é específica para jQuery, mas específica para JavaScript em geral. O principal problema é como "canalizar" uma variável nas funções incorporadas. Este é o exemplo:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

Essa técnica depende do uso de um fechamento. Mas não funciona thisporque thisé uma pseudo-variável que pode mudar de escopo para escopo dinamicamente:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

O que podemos fazer? Atribua-o a alguma variável e use-o através do alias:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thisnão é exclusivo a esse respeito: argumentsé a outra pseudo-variável que deve ser tratada da mesma maneira - por meio de alias.


7
Você realmente deve ligar "este" para a chamada de função e não passá-lo ao redor ou variável travessia escopos com ele
michael

10
@michael Por que isso?
Chris Anderson

@michael você pode estar trabalhando dentro de pipe, mapeado, se inscreve ... em uma função e não chama funções, neste caso "this" está mudando, então o escopo variável é a solução
Skander Jenhani

@SkanderJenhani esse comentário tem 8 anos. Minha resposta seria muito diferente hoje
michael

18

Sim, isso parece ser um padrão comum. Alguns programadores usam auto, outros me usam. É usado como uma referência ao objeto "real" em oposição ao evento.

É algo que demorei um pouco para realmente entender, parece estranho no começo.

Eu costumo fazer isso na parte superior do meu objeto (desculpe meu código de demonstração - é mais conceitual do que qualquer outra coisa e não é uma lição sobre a excelente técnica de codificação):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}

12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

edit: apesar de, funções aninhadas dentro de um objeto assumem o objeto da janela global em vez do objeto circundante.


Você parece estar descrevendo o que var self = thisfaz, mas não é isso que a pergunta está perguntando (a pergunta está perguntando se essa é a melhor solução para o problema).
Quentin

3
"O principal problema é como" canalizar "uma variável nas funções incorporadas." Eu concordo com esta afirmação na resposta aceita. Além disso, não há nada errado com "essa" prática. Isso é bastante comum. Então, como resposta à parte "código parece estranho para mim", escolhi explicar como funciona. Eu acredito que a razão pela qual o "código parece estranho para mim" vem da confusão de como ele funciona.
serkan

12

Se você estiver executando o ES2015 ou o script de tipo e o ES5, poderá usar as funções de seta no seu código e não terá esse erro. Isso se refere ao escopo desejado em sua instância.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});

5
Como explicação: no ES2015, as funções de seta capturam thisde seu escopo definido. Definições de funções normais não fazem isso.
defnull 27/09/16

@ defnull Eu não acho que seja algo especial. Funções criam um escopo de bloco. portanto, usar a notação de seta em vez de uma função impede que você crie um escopo; portanto, isso ainda estará se referindo ao exterior dessa variável.
Nimeshka Srimal

4

Uma solução para isso é vincular todo o seu retorno de chamada ao seu objeto com o bindmétodo javascript .

Você pode fazer isso com um método nomeado,

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

Ou com um retorno de chamada anônimo

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Fazendo isso em vez de recorrer a var self = thismostra, você entende como a ligação de thisse comporta em javascript e não depende de uma referência de fechamento.

Além disso, o operador de seta gorda no ES6 é basicamente o mesmo que chamar .bind(this)uma função anônima:

doCallback( () => {
  // You can reference "this" here now
});

Pessoalmente, acho que .bind (this) é apenas mais digitado, alterando a referência this para mim (ou o que seja) e, em seguida, apenas usá-lo cria um código mais limpo. A flecha gorda é o futuro.
davidbuttar 23/08

3

Eu não usei o jQuery, mas em uma biblioteca como Prototype você pode vincular funções a um escopo específico. Portanto, com isso em mente, seu código ficaria assim:

 $('#foobar').ready('click', this.doSomething.bind(this));

O método bind retorna uma nova função que chama o método original com o escopo que você especificou.


Análogo a: $('#foobar').ready('click', this.doSomething.call(this));certo?
Muers 12/08/19

O bindmétodo não funciona em navegadores antigos; portanto, use o $.proxymétodo.
Guffa

2

Apenas adicionando a isso que no ES6, devido às funções de seta, você não precisa fazer isso porque captura o thisvalor.


1

Eu acho que realmente depende do que você fará dentro de sua doSomethingfunção. Se você quiser acessar MyObjectpropriedades usando essa palavra-chave, precisará usá-la. Mas acho que o seguinte fragmento de código também funcionará se você não estiver fazendo nada de especial usando object(MyObject)propriedades.

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

});
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.