En JavaScript, cada función tiene una referencia this creada automáticamente cuando la declaramos.
El this de JavaScript es similar a lo que podríamos encontrar en otros lenguajes basados en clases como Java o C# (JavaScript en realidad es un lenguaje basado en prototipos y no tiene un concepto de "clase"): apunta al objeto que está llamando a la función (este objeto a veces se llama contexto). Sin embargo, en JavaScript, las funciones internas de la referencia this pueden vincularse a diferentes objetos dependiendo de dónde se llame a la función.
Aquí hay 5 reglas básicas para enlazar this en JavaScript:
Regla 1
Cuando se llama a una función en el ámbito global, this hace referencia de forma predeterminada al objeto global (window en el navegador, o global en Node.js). Por ejemplo:
function foo() {
this.a = 2;
}
foo();
console.log(a); // 2Nota: Si declaramos la función foo() en modo estricto, y luego la llamamos en el ámbito global, this será undefined y la asignación this.a = 2 arrojará una excepción Uncaught TypeError (error de tipo no detectado).
Regla 2
Veamos el siguiente ejemplo:
function foo() {
this.a = 2;
}
const obj = {
foo: foo
};
obj.foo();
console.log(obj.a); // 2Claramente, en el fragmento anterior, la función foo() se llama en el contexto del objeto obj, por lo tanto this ahora hace referencia a obj. Entonces, cuando se llama a una función con un objeto de contexto, la referencia this se vincula a dicho objeto.
Regla 3
.call, .apply y .bind pueden utilizarse para enlazar explícitamente this. Usar .bind(this) es algo que podemos ver en muchos componentes de React.
const foo = function() {
console.log(this.bar)
}
foo.call({ bar: 1 }) // 1Aquí vemos algunos ejemplos rápidos de como utilizar cada uno para enlazar this:
.call():fn.call(thisObj, fnParam1, fnParam2).apply():fn.apply(thisObj, [fnParam1, fnParam2]).bind():const newFn = fn.bind(thisObj, fnParam1, fnParam2)
Regla 4
function Point2D(x, y) {
this.x = x;
this.y = y;
}
const p1 = new Point2D(1, 2);
console.log(p1.x); // 1
console.log(p1.y); // 2Lo que notamos aquí es que al llamar a la función Point2D utilizando la palabra clave new, la referencia this se vincula al objeto p1. Entonces, cuando llamamos a una función utilizando new, un nuevo objeto es creado y la referenciathis se vincula al mismo.
Nota: A una función que llamamos utilizando la palabra clave new también la conocemos como función constructora.
Regla 5
JavaScript determina el valor de this en tiempo de ejecución, según el contexto actual. Por eso a veces this podría no estar apuntando a dónde esperamos.
Veamos este ejemplo, una clase Gato que tiene un método llamado hacerSonido(), siguiendo lo que vimos en la regla anterior, utilizamos una función constructora y la palabra clave new.
const Gato = function(nombre, sonido) {
this.nombre = nombre;
this.sonido = sonido;
this.hacerSonido = function() {
console.log( this.nombre + ' decir: ' + this.sonido );
};
}
const gatito = new Gato('Papá Gordo', 'Mrrooowww');
gatito.haceSonido(); // Papá Gordo dice: MrrooowwwAhora vamos a agregarle al gato el método molestar() para que puedan molestar a las personas repitiendo su sonido 100 veces, con un período de repetición de medio segundo.
const Gato = function(nombre, sonido) {
this.nombre = nombre;
this.sonido = sonido;
this.hacerSonido = function() {
console.log( this.nombre + ' decir: ' + this.sonido );
};
this.molestar = function() {
let contar = 0, max = 100;
const t = setInterval(function() {
this.hacerSonido(); // <-- esta línea falla con `this.hacerSonido no es una función`
count++;
if (contar === max) {
clearTimeout(t);
}
}, 500);
};
}
const gatito = new Gato('Papá Gordo', 'Mrrooowww');
gatito.molestar();Esto no funcionará porque dentro de setInterval hemos creado un nuevo contexto con alcance global, entonces this ya no apunta a nuestra instancia gatito. En el navegador, this apuntará al objeto Window, que no tiene un método hacerSonido().
Algunas formas de solucionarlo:
1. Antes de crear el nuevo contexto, asignar this a una variable local llamada me, o self, o cualquier nombre que elijamos, y referenciar esa variable dentro del callback.
const Gato = function(nombre, sonido) {
this.nombre = nombre;
this.sonido = sonido;
this.hacerSonido = function() {
console.log( this.nombre + ' decir: ' + this.sonido );
};
this.molestar = function() {
let contar = 0, max = 100;
const self = this;
const t = setInterval(function() {
self.hacerSonido();
contar++;
if (contar === max) {
clearTimeout(t);
}
}, 500);
};
}
const gatito = new Gato('Papá Gordo', 'Mrrooowww');
gatito.molestar();2. Con ES6 podemos evitar tener que asignar this a una variable local usando una arrow function, que automáticamente enlaza this al contexto del código donde ha sido definida.
const Gato = function(nombre, sonido) {
this.nombre = nombre;
this.sonido = sonido;
this.hacerSonido = function() {
console.log( this.nombre + ' decir: ' + this.sonido );
};
this.molestar = function() {
let contar = 0, max = 100;
const t = setInterval(() => {
this.hacerSonido();
contar++;
if (contar === max) {
clearTimeout(t);
}
}, 500);
};
}
const gatito = new Gato('Papá Gordo', 'Mrrooowww');
gatito.molestar();Traducido del artículo - The Complete Guide to this in JavaScript