O que está por trás desse idioma do JavaScript: var self = this?

357

Eu vi o seguinte na fonte para WebKit HTML 5 SQL Storage Notes Demo :

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

O autor usa auto em alguns lugares (o corpo da função) e este em outros lugares (os corpos de funções definidas na lista de argumentos de métodos). O que está acontecendo? Agora que notei uma vez, começarei a vê-lo em todos os lugares?

Thomas L Holaday
fonte
4
Este é um recurso da linguagem JS chamado “fechamento lexical”.
subZero 07/12
2
Possível duplicado de: var self = this? .
DavidRR
O conceito deste é explicada explicitamente aqui scotch.io/@alZami/understanding-this-in-javascript
AL-Zami
Exemplo relevante nesta resposta stackoverflow.com/a/20279485/5610569 (para a pergunta "Como acessar o correto thisdentro de um retorno de chamada?"))
FluxLemur

Respostas:

431

Veja este artigo em alistapart.com . (Ed: o artigo foi atualizado desde que vinculado originalmente)

selfestá sendo usado para manter uma referência ao original, thismesmo quando o contexto está mudando. É uma técnica frequentemente usada em manipuladores de eventos (especialmente em fechamentos).

Editar: Observe que o uso selfagora é desencorajado como window.selfexiste e pode causar erros se você não for cuidadoso.

O que você chama de variável não importa particularmente. var that = this;está bem, mas não há nada mágico no nome.

Funções declaradas dentro de um contexto (por exemplo, retornos de chamada, fechamentos) terão acesso às variáveis ​​/ funções declaradas no mesmo escopo ou acima.

Por exemplo, um retorno de chamada de evento simples:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});

Jonathan Fingland
fonte
Parece que este artigo se transformou em usovar that = this;
Bob Stein
@BobStein Obrigado. Vou atualizar a resposta de acordo.
Jonathan Fingland 31/10/19
96

Eu acho que o nome da variável 'self' não deve mais ser usado dessa maneira, pois os navegadores modernos fornecem uma variável global queself aponta para o objeto global de uma janela normal ou de um WebWorker.

Para evitar confusão e possíveis conflitos, você pode escrever var thiz = thisou var that = thispreferir.

Duan Yao
fonte
43
Eu costumo usar_this
djheru
6
@djheru +1. muito melhor do que " that" (com o qual meu cérebro nunca se acostumará).
#
9
Comecei a usar "me" :)
Mopparthy Ravindranath
3
Até os navegadores modernos começarem a fornecer uma variável global - isso ou aquilo.
Beejor
10
Não há absolutamente nenhum problema em usar o nome self, desde que você o declare como varresponsável; isso sombreará o global. Obviamente, se você esqueceu o nome var, também não funcionaria com outro nome.
Bergi
34

Sim, você verá em todos os lugares. É frequentemente that = this;.

Veja como selfé usado dentro de funções chamadas por eventos? Esses teriam seu próprio contexto, então selfé usado para conter o thisque entrou Note().

O motivo selfainda está disponível para as funções, mesmo que elas possam ser executadas somente após a conclusão da Note()função, é que as funções internas obtêm o contexto da função externa devido ao fechamento .

Nosredna
fonte
12
Para mim, o ponto convincente é que selfnão tem significado especial. Pessoalmente, prefiro usar um var chamado algo diferente, selfuma vez que freqüentemente me confunde, pois espero que "eu" seja uma palavra reservada. Então, eu gosto da sua resposta. E no exemplo do OP, eu prefiro var thisNote = thisou similar.
Steve
@ Steve concordou, embora eu tente evitar usar essas / referências próprias em geral, pois elas são muito frágeis em termos de manutenção.
mattLummus
28

Também deve ser observado que existe um padrão de proxy alternativo para manter uma referência ao original thisem um retorno de chamada se você não gostar dovar self = this idioma.

Como uma função pode ser chamada com um determinado contexto usando function.applyou function.call, você pode escrever um wrapper que retorna uma função que chama sua função com applyou callusando o contexto especificado. Veja a proxyfunção do jQuery para uma implementação desse padrão. Aqui está um exemplo de como usá-lo:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFuncpode ser chamado e terá sua versão thiscomo o contexto.

Máx.
fonte
10

Como outros explicaram, var self = this;permite que o código em um fechamento se refira ao escopo pai.

No entanto, agora é 2018 e o ES6 é amplamente suportado por todos os principais navegadores da web. ovar self = this; idioma não é tão essencial quanto era antes.

Agora é possível evitar var self = this;o uso de funções de seta .

Nos casos em que teríamos usado var self = this:

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

Agora podemos usar uma função de seta sem var self = this:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

As funções de seta não têm suas próprias thise simplesmente assumem o escopo anexo.

Elliot B.
fonte
Ou - choque, horror! - por que não passar a coisa relevante como argumento para sua função (fechamento)? Por que diabos você está fazendo referência fora do escopo, por que diabos alguém está programando assim? Nunca existe uma verdadeira razão para fazer isso. Em vez disso, .addEventListender("click", (x) => { console.log(x); });você explicou o como e o porquê com muita clareza, e eu concordo que o uso das funções de seta faz mais sentido, mas ainda assim ... isso é uma programação terrível, preguiçosa, bagunçada.
Benjamin R
2
Em JavaScript, consultar novamente o escopo pai é extremamente comum e necessário. É uma parte fundamental da linguagem. Sua sugestão para passar no escopo pai como argumento no manipulador de eventos não é realmente possível. Além disso, no ES6, as funções de seta usam o escopo lexical - 'this' refere-se ao seu escopo atual e não mais - não é "referenciar estado fora do escopo" ou algo assim.
Elliot B.
9

A variável é capturada pelas funções embutidas definidas no método thisna função se referirá a outro objeto. Dessa forma, você pode fazer com que a função mantenha uma referência ao thisno escopo externo.

Mehrdad Afshari
fonte
9

É uma peculiaridade do JavaScript. Quando uma função é uma propriedade de um objeto, mais apropriadamente chamada de método, isso se refere ao objeto. No exemplo de um manipulador de eventos, o objeto que contém é o elemento que acionou o evento. Quando uma função padrão é chamada, isso se refere ao objeto global. Quando você aninha funções como no seu exemplo, isso não se relaciona com o contexto da função externa. Funções internas fazer âmbito share com a função que contém, para que os desenvolvedores usarão variações var that = this, a fim de preservar a esse que eles precisam na função interna.

kombat
fonte
5

Na verdade self é uma referência a window ( window.self), portanto, quando você diz var self = 'something'que substitui uma referência de janela por si mesma - porque self existe no objeto window.

É por isso que a maioria dos desenvolvedores preferem var that = thismaisvar self = this;

De qualquer forma; var that = this;não está de acordo com as boas práticas ... presumindo que seu código será revisado / modificado posteriormente por outros desenvolvedores, você deve usar os padrões de programação mais comuns em relação à comunidade de desenvolvedores

Portanto, você deve usar algo como var oldThis/ var oThis/ etc - para ficar claro no seu escopo // ..é não muito, mas economizará alguns segundos e poucos ciclos cerebrais

SorinN
fonte
2
@prior Acho que faz sentido até o último parágrafo.
Miphe
0

Como mencionado várias vezes acima, 'self' está sendo simplesmente usado para manter uma referência a 'this' antes de entrar na função. Uma vez na função 'this' refere-se a outra coisa.

Cyprien
fonte
@JohnPaul ... isso faz dar uma resposta. Pode não ser a resposta certa, mas como você pode dizer que "está sendo usado para ..." não é uma resposta para "por que ele fez isso"?
GreenAsJade
-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call (thatt);

ht zhao
fonte
11
Você deve adicionar alguma explicação com o código de que o que você fez de especial.
Farhana