¿Qué significa 'var that=this;' en JavaScript?
En un archivo de JavaScript vi:
function Somefunction(){
var that = this;
...
}
¿Cuál es el propósito de declarar that y asignarle this?
Respuesta #1
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 demostraba esto con jQuery, que es solo 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 this cambia frecuentemente cuando cambias el alcance al llamar a una nueva función, no puedes acceder al valor original usándolo. Hacer un alias a that te permite seguir accediendo al valor original de this.
Personalmente, no me gusta el uso de that como alias. Rara vez es evidente a qué se refiere, especialmente si las funciones tienen más de un par de líneas. Yo siempre uso un alias más descriptivo. En mis ejemplos anteriores, probablemente usaría clickedEl.
Respuesta #2
De Crockford
Por convención, hacemos una variable privada that. 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 causa que this se establezca 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...
UsaEso que piensa que se llama Dave
UsesThis piensa que se llama indefinido
Respuesta #3
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 defines una función dentro de otra, this se establece automáticamente en el ámbito global. Esto puede ser confuso porque esperas que this tenga 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 creas una función como un método de un objeto (como car.start en el ejemplo) y luego creas una función dentro de ese método (como activateStarter). En el método de nivel superior this apunta al objeto del que es método (en este caso, car) pero en la función interna this ahora apunta al ámbito global. Esto es un fastidio.
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 funciones de jQuery). Por eso se utiliza el nombre de sonido muy general that. Es una convención fácilmente reconocible para superar una limitación del lenguaje.
Como sugiere El Ronnoco, Douglas Crockford piensa que esto es una buena idea.
Respuesta #4
El uso de that no es realmente necesario si haces 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);
};
