¿Qué significa 'var that = this;' significa en JavaScript?

351

En un archivo de JavaScript vi:

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

¿Cuál es el propósito de declarar thaty asignarle thisesto?

Chris
fuente
3
posible duplicado de var self = this?
Bergi
66
El "esto" y "ese" pirateo no es necesario para las funciones de flecha. Con las funciones de flecha "esto" funciona como se esperaba. Ver aquí para más detalles ES6 en profundidad: funciones de flecha
satguru srivastava
1
Aquí el concepto de esto se explica scotch.io/@alZami/understanding-this-in-javascript
AL-zami
Una gran explicación sobre este misterioso comportamiento basado en el contexto aquí
RBT
La explicación más reciente y actualizada se puede encontrar aquí
Sukrit Gupta

Respuestas:

486

Voy a comenzar esta respuesta con una ilustración:

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
    });
});

Mi respuesta originalmente demostró esto con jQuery, que es muy ligeramente 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
    });
});

Debido a que con thisfrecuencia cambia cuando cambia el alcance llamando a una nueva función, no puede acceder al valor original al usarlo. Aliasing to le thatpermite todavía acceder al valor original de this.

Personalmente, no me gusta el uso de thatalias. Raramente es obvio a qué se refiere, especialmente si las funciones son más largas que un par de líneas. Yo siempre uso un alias más descriptivo. En mis ejemplos anteriores, probablemente usaría clickedEl.

solitario
fuente
149
Yo suelo ir con var self = this;. La palabra thatparece implicar que la variable es cualquier cosa PERO this.
David Murdoch
13
@David Sí, he pensado que es algo engañoso. Pero si, como dice Crockford, es una convención, ¿es prudente seguir ese camino? Sin embargo, estoy totalmente de acuerdo contigo, tiene mucho más sentido.
El Ronnoco
44
@El Ronnoco, pero "Tiene el cabello canoso y una barba desaliñada y su actitud recuerda a un viejo gruñón que les grita a los niños que se levanten del césped". - blogging.compendiumblog.com/blog/software-for-humans/0/0/… ;-p
David Murdoch
77
@ElRonnoco: Sin embargo, eso es un llamamiento a la autoridad. Si alguna vez hacemos lo que "la gente famosa" dice que debemos hacer, nos dirigimos al desastre.
ligereza corre en órbita el
3
La forEachfunción toma un segundo argumento opcional que es el enlace de la función. colours.forEach(function(){/* 'this' is bound correctly --> */}, this);Por lo tanto, se debe agregar una nota que realmentevar that = this no se necesita con . forEach
Matt Clarkson el
107

De Crockford

Por convención, hacemos una privada que variable. Esto se utiliza para hacer que el objeto esté disponible para los métodos privados. Esta es una solución para un error en la especificación del lenguaje ECMAScript que hace que esto se configure incorrectamente para las funciones 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);

Esto alerta ...

Usos que piensa que se llama Dave

Usos Esto cree que se llama indefinido

El ronnoco
fuente
2
Gracias, lo resume bastante bien para mí.
Chris
3
Lo leí, no lo entendí porque no tenía detalles, busqué en Google, encontré esta página. Donde estoy nuevamente señaló la misma oración. De ahí el voto negativo.
Aditya MP
3
Ese es un punto justo, diría que alguien que no esté familiarizado con JavaScript tendría dificultades para comprender el concepto solo con mi respuesta. Respondí muy brevemente (y hice un enlace a la página que buscaste en Google ...) Diría que la respuesta de lonesomeday es la más clara, aunque todavía la hubiera preferido en JS simple en lugar de un ejemplo de jQuery.
El Ronnoco
16
No me ofende. ¡Es agradable ver a alguien que hace comentarios al votar abajo!
El Ronnoco
44
El problema con la respuesta de Crockford es que la thatvariable no se usa en absoluto en su ejemplo. Hace que parezca que solo crear una retención variable thishace algo al resto del código.
r3m0t
86

Este es un truco para hacer que las funciones internas (funciones definidas dentro de otras funciones) funcionen más como deberían. En javascript, cuando define una función dentro de otra this, se establece automáticamente en el ámbito global. Esto puede ser confuso porque espera thistener el mismo valor que en la función 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();

};

Esto es específicamente un problema cuando crea una función como método de un objeto (como car.starten el ejemplo) y luego crea una función dentro de ese método (como activateStarter). En el método de nivel superior thisapunta al objeto, es un método de (en este caso car) pero en la función internathis ahora apunta al alcance global. Esto es un dolor

Crear una variable para usar por convención en ambos ámbitos es una solución para este problema muy general con JavaScript (aunque también es útil en las funciones de jquery). Es por eso que el nombre que suena muy generalthat se usa el . Es una convención fácilmente reconocible para superar una deficiencia en el idioma.

Al igual que El Ronnoco insinúa que Douglas Crockford cree que es una buena idea.

Waylon Flinn
fuente
8
Supongo que esta es una respuesta más útil que la aceptada. Porque aclara la razón por la cual Crockford ha inventado "eso" mientras que la respuesta sobre jQuery no lo hace.
Konstantin Smolyanin
44
Este es en realidad un mejor ejemplo que la respuesta aceptada. Explica cómo es "un error en la especificación del lenguaje ECMAScript que hace que esto se configure incorrectamente para las funciones internas", dijo Douglas.
kakacii
1
Sin embargo, es posible que desee hacer que la gramática sea correcta. Sé que es más como un error tipográfico, pero podría confundir a los principiantes de JavaScript, ya que esta pregunta es más como para principiantes. Quiero decir que debería ser: var car = {}; car.starter = {}; car.start = function () {...}
kakacii
1
@kakacii Gracias. Corregido ahora.
Waylon Flinn
9

El uso de thatno es realmente necesario si realiza una solución alternativa con el uso de call()o 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
fuente
3

A veces thispuede referirse a otro ámbito y a otra cosa, por ejemplo, suponga que desea llamar a un método constructor dentro de un evento DOM, en este casothis se referirá al elemento DOM, no al objeto creado.

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

Manifestación

La solución anterior voluntad assing thisa thatcontinuación, podemos y acceso a la propiedad de nombre dentro de lasayHi método dethat , por lo que este puede ser llamado sin problemas dentro de la llamada DOM.

Otra solución es asignar un thatobjeto vacío y agregarle propiedades y métodos y luego devolverlo. Pero con esta solución perdiste la prototypedel constructor.

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

Aquí hay un ejemplo `

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

        });
    });`

Por lo tanto, puede ver que el valor de esto es dos valores diferentes dependiendo del elemento DOM al que apunta, pero cuando agrega "eso" al código anterior, cambia el valor de "esto" al que apunta.

`$(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 ("color de fondo", "# ffe700"); // Aquí el valor de "that" es ".our-work-group> p> a" porque el valor de var that = this; así que aunque estemos en "this" = '.our-work-single-page', aún podemos usar "that" para manipular el elemento DOM anterior.

stacksonstacksonstacks
fuente