O que 'var that = this;' significa em JavaScript?

351

Em um arquivo JavaScript, vi:

function Somefunction(){
   var that = this; 
   ... 
}

Qual é o propósito de declarar thate atribuir thisisso a ele?

Chris
fonte
3
possível duplicado de var self = this?
Bergi
6
O hack "this" e "that" não é necessário para funções de seta. Com as funções de seta "isso" funciona como esperado. Veja aqui para mais detalhes ES6 em profundidade: funções Seta
satguru srivastava
11
aqui conceito deste é explicado scotch.io/@alZami/understanding-this-in-javascript
AL-Zami
Uma grande explicação sobre misteriosa este comportamento com base no contexto aqui
RBT
Últimas & explicação atualizado pode ser encontrado aqui
Sukrit Gupta

Respostas:

486

Vou começar esta resposta com uma ilustração:

var colours = ['red', 'green', 'blue'];
document.getElementById('element').addEventListener('click', function() {
    // this is a reference to the element clicked on

    var that = this;

    colours.forEach(function() {
        // this is undefined
        // that is a reference to the element clicked on
    });
});

Minha resposta originalmente demonstrou isso com o jQuery, que é apenas um pouco diferente:

$('#element').click(function(){
    // this is a reference to the element clicked on

    var that = this;

    $('.elements').each(function(){
        // this is a reference to the current element in the loop
        // that is still a reference to the element clicked on
    });
});

Como thisfrequentemente muda quando você altera o escopo chamando uma nova função, não é possível acessar o valor original usando-o. Aliasing para thatpermite que você ainda acesse o valor original de this.

Pessoalmente, não gosto do uso de thatcomo apelido. Raramente é óbvio a que se refere, especialmente se as funções tiverem mais de duas linhas. Eu sempre uso um alias mais descritivo. Nos meus exemplos acima, eu provavelmente usaria clickedEl.

lonesomeday
fonte
149
Eu costumo ir com var self = this;. A palavra thatparece implicar que a variável é qualquer coisa, MAS this.
David Murdoch
13
@ David Sim, eu pensei que é um pouco enganador. Mas se, como Crockford diz, é uma convenção, é aconselhável seguir esse caminho. Eu concordo totalmente com você, porém, faz muito mais sentido.
El Ronnoco 03/02
4
@El Ronnoco, mas "ele tem cabelos grisalhos, barba desgrenhada e seu jeito lembra um velho rabugento que grita com as crianças para sair do gramado". - blogging.compendiumblog.com/blog/software-for-humans/0/0/… ;-p
David Murdoch
7
@ElRonnoco: Mas é um apelo à autoridade. Se apenas fizermos o que "as pessoas famosas" dizem que devemos fazer, estaremos indo para o desastre.
Lightness Races em órbita
3
A forEachfunção recebe um segundo argumento opcional: qual é a ligação da função. colours.forEach(function(){/* 'this' is bound correctly --> */}, this);Portanto, uma nota deve ser adicionada que var that = thisnão é realmente necessária forEach.
Matt Clarkson
107

A partir de Crockford

Por convenção, tornamos privada essa variável. Isso é usado para disponibilizar o objeto para os métodos privados. Esta é uma solução alternativa para um erro na especificação de idioma ECMAScript que faz com que isso seja definido incorretamente para funções internas.

JS Fiddle

function usesThis(name) {
    this.myName = name;

    function returnMe() {
        return this;        //scope is lost because of the inner function
    }

    return {
        returnMe : returnMe
    }
}

function usesThat(name) {
    var that = this;
    this.myName = name;

    function returnMe() {
        return that;            //scope is baked in with 'that' to the "class"
    }

    return {
        returnMe : returnMe
    }
}

var usesthat = new usesThat('Dave');
var usesthis = new usesThis('John');
alert("UsesThat thinks it's called " + usesthat.returnMe().myName + '\r\n' +
      "UsesThis thinks it's called " + usesthis.returnMe().myName);

Isso alerta ...

Que pensa que se chama Dave

Isso acha que é chamado de indefinido

El Ronnoco
fonte
2
Obrigado, resume bem o suficiente para mim.
Chris
3
Eu li isso, não entendi porque não tinha detalhes, procurei no Google, encontrei esta página. Onde estou novamente apontado para a mesma frase. Daí o voto negativo.
Aditya MP
3
Esse é um argumento justo, eu diria que alguém não familiarizado com JavaScript teria dificuldade em entender o conceito apenas da minha resposta. Respondi muito brevemente (e fiz o link para a página que você pesquisou no Google). Diria que a resposta de lonesomeday é a mais clara, embora eu ainda a tenha preferido em JS comum, em vez de um exemplo de jQuery.
El Ronnoco 21/02
16
Eu não me ofendo. É bom ver alguém que comenta ao fazer uma votação!
El Ronnoco 21/02
4
O problema com a resposta de Crockford é que a thatvariável não é usada em seu exemplo. Faz parecer que apenas criar uma retenção de variável thisfaz algo com o restante do código.
R3m0t 03/07/2013
86

Este é um truque para fazer com que as funções internas (funções definidas dentro de outras funções) funcionem mais como deveriam. Em javascript, quando você define uma função dentro de outra, thisautomaticamente é definido para o escopo global. Isso pode ser confuso porque você espera thister o mesmo valor que na função externa.

var car = {};
car.starter = {};

car.start = function(){
    var that = this;

    // you can access car.starter inside this method with 'this'
    this.starter.active = false;

    var activateStarter = function(){
        // 'this' now points to the global scope
        // 'this.starter' is undefined, so we use 'that' instead.
        that.starter.active = true;

        // you could also use car.starter, but using 'that' gives
        // us more consistency and flexibility
    };

    activateStarter();

};

Isso é especificamente um problema quando você cria uma função como método de um objeto (como car.startno exemplo) e depois cria uma função dentro desse método (como activateStarter). No nível superior, o método thisaponta para o objeto e é um método de (neste caso car) , mas na função interna thisagora aponta para o escopo global. Isso é uma dor.

Criar uma variável a ser usada por convenção nos dois escopos é uma solução para esse problema muito geral com javascript (embora seja útil também nas funções jquery). É por isso que o nome sonoro muito geral thaté usado. É uma convenção facilmente reconhecível para superar uma falha no idioma.

Como El Ronnoco sugere em Douglas Crockford, essa é uma boa idéia.

Waylon Flinn
fonte
8
Suponho que seja uma resposta mais útil do que a aceita. Porque esclarece a razão pela qual Crockford inventou "isso", enquanto a resposta sobre o jQuery não.
Konstantin Smolyanin
4
Este é realmente um exemplo melhor do que a resposta aceita. Ele explica como é "um erro na especificação de linguagem ECMAScript que faz com que isso seja definido incorretamente para funções internas", disse Douglas.
Kbacii
11
Você pode querer corrigir a gramática. Eu sei que é mais como um erro de digitação, mas poderia confundir iniciantes em javascript, pois essa pergunta é mais parecida com iniciante. Quero dizer que deveria ser: var car = {}; car.starter = {}; car.start = function () {...}
kakacii
11
@kakacii Obrigado. Corrigido agora.
Waylon Flinn
9

O uso de thatnão é realmente necessário se você fizer uma solução alternativa com o uso de call()ou apply():

var car = {};
car.starter = {};

car.start = function(){
    this.starter.active = false;

    var activateStarter = function(){
        // 'this' now points to our main object
        this.starter.active = true;
    };

    activateStarter.apply(this);
};
Adela
fonte
3

Às vezes, thispode se referir a outro escopo e a outra coisa, por exemplo, suponha que você queira chamar um método construtor dentro de um evento DOM, nesse caso, thisse referirá ao elemento DOM e não ao objeto criado.

HTML

<button id="button">Alert Name</button>

JS

var Person = function(name) {
  this.name = name;
  var that = this;
  this.sayHi = function() {
    alert(that.name);
  };
};

var ahmad = new Person('Ahmad');
var element = document.getElementById('button');
element.addEventListener('click', ahmad.sayHi); // => Ahmad

Demo

A solução acima irá assing thispara that, em seguida, podemos e acesso a propriedade de nome dentro do sayHimétodo a partir that, de modo que este pode ser chamado sem problemas dentro da chamada DOM.

Outra solução é atribuir um thatobjeto vazio e adicionar propriedades e métodos a ele e depois devolvê-lo. Mas com esta solução, você perdeu prototypeo construtor.

var Person = function(name) {
  var that = {};
  that.name = name;
  that.sayHi = function() {
    alert(that.name);
  };
  return that;
};
Ahmad Ajmi
fonte
2

Aqui está um exemplo `

$(document).ready(function() {
        var lastItem = null;
        $(".our-work-group > p > a").click(function(e) {
            e.preventDefault();

            var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
            if (item == lastItem) {
                lastItem = null;
                $('.our-work-single-page').show();
            } else {
                lastItem = item;
                $('.our-work-single-page').each(function() {
                    var imgAlt = $(this).find('img').attr('alt'); //Here value of "this" is '.our-work-single-page'. 
                    if (imgAlt != item) {
                        $(this).hide();
                    } else {
                        $(this).show();
                    }
                });
            }

        });
    });`

Assim, você pode ver que esse valor é de dois valores diferentes, dependendo do elemento DOM que você segmentar, mas quando você adiciona "that" ao código acima, altera o valor de "this" que está direcionando.

`$(document).ready(function() {
        var lastItem = null;
        $(".our-work-group > p > a").click(function(e) {
            e.preventDefault();
            var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
            if (item == lastItem) {
                lastItem = null;
                var that = this;
                $('.our-work-single-page').show();
            } else {
                lastItem = item;
                $('.our-work-single-page').each(function() {
                   ***$(that).css("background-color", "#ffe700");*** //Here value of "that" is ".our-work-group > p > a"....
                    var imgAlt = $(this).find('img').attr('alt'); 
                    if (imgAlt != item) {
                        $(this).hide();
                    } else {
                        $(this).show();
                    }
                });
            }

        });
    });`

..... $ (that) .css ("cor de fundo", "# ffe700"); // Aqui o valor "that" é ".our-work-group> p> a" porque o valor de var that = this; portanto, mesmo estando em "this" = '.our-work-single-page', ainda podemos usar "that" para manipular o elemento DOM anterior.

stacksonstacksonstacks
fonte