Introducci贸n
La palabra clave this
de una funci贸n se comporta un poco diferente en Javascript en comparaci贸n con otros lenguajes. Adem谩s tiene algunas diferencias entre el modo estricto y el modo no estricto.
En general, el valor de this
est谩 determinado por c贸mo se invoca a la funci贸n. No puede ser establecida mediante una asignaci贸n en tiempo de ejecuci贸n, y puede ser diferente cada vez que la funci贸n es invocada. ES5 introdujo el m茅todo bind()
para establecer el valor de la funci贸n this
independientemente de como es llamada, y ES2015 introdujo las funciones flecha que no proporcionan su propio "binding" de this
(se mantiene el valor de this
del contexto l茅xico que envuelve a la funci贸n)
Sintaxis
this
Valor
El objeto contexto de JavaScript en el cual se est谩 ejecutando el c贸digo actual.
Contexto global
En el contexto de ejecuci贸n global (fuera de cualquier funci贸n), this
se refiere al objeto global, ya sea en modo estricto o no.
console.log(this.document === document); // true
// En los navegadores web, el objeto window tambi茅n es un objeto global:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
Nota: Puedes obtener el objeto global usando la propieda global globalThis
, no importa el contexto donde se ejecute esta propiedad, siempre har谩 referencia al objeto global.
Contexto de la funci贸n
Dentro de una funci贸n, el valor de this depende de c贸mo la funci贸n es llamada.
Llamada simple
function f1(){
return this;
}
f1() === window; // objeto global
function f2(){
"use strict"; // consultar modo estricto
return this;
}
f2() === undefined;
Nota: En el segundo ejemplo, this
deber铆a ser undefined
, porque f2
fue llamado sin proporcionar ninguna base (ej. window.f2()
). Esta caracter铆stica no fue implementada en algunos navegadores cuando se comenz贸 a dar soporte al modo estricto. Como resultado, retorna incorrectamente el objeto window.
Cuando una funci贸n es llamada como un m茅todo de un objeto, el this
cambia por el metodo del objeto llamado.
En el siguiente ejemplo, cuando o.f()
es invocado, dentro de la funci贸n this
es ligado al objeto o
.
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37
Note que el comportamiento no es del todo afectado por c贸mo o d贸nde la funci贸n fue definida. En el ejemplo anterior, nosotros definimos la funci贸n en l铆nea como el elemento f
durante la definici贸n de o
. Sin embargo, podriamos haber definido con la misma facilidad la primera funci贸n y luego adjuntarlo a o.f
. Hacerlo da como resultado el mismo comportamiento.
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
Esto demuestra que s贸lo importa que la funci贸n fue invocada del elemento f
de o
.
Asimismo, el enlace this
s贸lo se ve afectado por la referencia del miembro m谩s inmediata. En el siguiente ejemplo, cuando invocamos a la funci贸n, lo llamamos como metodo g
del objeto o.b
. Esta vez durante la ejecuci贸n, this
dentro de la funci贸n se referir谩 a o.b
. El hecho de que el objeto es en s铆 mismo un elemento de o
no tiene ninguna consecuencia, la referencia m谩s inmediata es todo lo que importa.
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // logs 42
... en la cadena de prototipo
El mismo concepto es v谩lido para los m茅todos definidos en alguna parte de la cadena de prototipo del objeto. Si el m茅todo esta sobre una cadena de prototipo del objeto, this
se referir谩 al objeto donde est谩 el m茅todo de donde fue llamado. Como si ese m茅todo estuviera dentro del objeto.
var o = {f:function(){ return this.a + this.b; }};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
En este ejemplo, el objeto asignado a la variable p
no tiene su propia propiedad f
, esto lo hereda de su prototipo. Pero no importa que la b煤squeda de f
eventualmente encuentre un elemento con ese nombre en o
; la b煤squeda comenz贸 como una referencia a p.f
, asi this
dentro de la funcion toma el valor del objeto referido como p
. Es decir, desde que f
es llamado como m茅todo de p
, su this
refiere a p
. Esto es una interesante caracter铆stica de la herencia de prototipo de JavaScript.
... o como un getter o setter
Nuevamente, el mismo concepto es v谩lido cuando una funci贸n es invocada de un getter o un setter. Una funci贸n usado como getter o setter tiene su enlace this
al objeto desde el cual la propiedad esta siendo establecida u obtenida.
function modulus(){
return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
re: 1,
im: -1,
get phase(){
return Math.atan2(this.im, this.re);
}
};
Object.defineProperty(o, 'modulus', {get: modulus, enumerable:true, configurable:true});
console.log(o.phase, o.modulus); // logs -0.78 1.4142
Como un constructor
Cuando una funci贸n es usada como un constructor (con la palabra clave new
), su this
es enlazado al nuevo objeto en construcci贸n, a menos que la ejecuci贸n de los resultados del constructor en el motor JavaScript encuentren una instrucci贸n de retorno donde el valor de retorno sea un objeto.
/*
* Los constructores trabajan algo asi:
*
* function MyConstructor(){
* // El cuerpo del c贸digo de la funci贸n actual va aqu铆. Crear las propiedades en |this| como
* // se desee mediante la asignaci贸n a los mismos. E.g.,
* this.fum = "nom";
* // etcetera...
*
* // Si la funci贸n tiene una sentencia de retorno este retorna un objeto,
* // este objeto ser谩 el resultado de la expresi贸n |new|. Por otro lado, el
* // resultado de la expresi贸n es el objeto actualmente enlazado a |this|
* // (i.e., el caso m谩s com煤n suele verse).
* }
*/
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2(){
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); // logs 38
En el 煤ltimo ejemplo (C2
), debido a que un objeto fue devuelto durante la construcci贸n, el nuevo objeto que fue enlazado a this
simplemente se descarta.( Esto esencialmente hace de la declaraci贸n "this.a = 37;
" codigo muerto. No esta exactamente muerto,porque es ejecutado pero se puede eliminar sin efectos externos.)
call y apply
Cuando una funci贸n usa la plabra clave this
en su cuerpo, su valor puede ser enlazado a un objeto particular durante la ejecuci贸n del m茅todo call()
or apply()
que todas las funciones hereden de Function.prototype
.
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
// El primer par谩metro es el objeto a usar como 'this', par谩metros posteriores se pasan como argumentos
// en la llamada a la funci贸n
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
//El primer par谩metro es el objeto a usar como 'this''this', la segunda es una matriz cuyos elementos
// se utilizan como argumentos en la llamada a la funci贸n
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
Funciones enlazadas
ECMAScript 5 introduce Function.prototype.bind()
. Llamando a f.bind(someObject)
crea una nueva funci贸n con el mismo cuerpo y alcance de f
, pero donde this
se produce en la funci贸n original, en la nueva funci贸n esto esta permanentemente ligado al primer argumento de bind
, independientemente de c贸mo la funci贸n est谩 siendo utilizada.
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
Como un controlador de eventos DOM
Cuando una funci贸n es usada como un controlador de eventos, su this
es cambiado desde el elemento del evento disparado (algunos navegadores no siguen esta convenci贸n para los listeners agregados din谩micamente con otros m茅todos addEventListener
).
// Cuando se llama como un listener, convierte en azul el elemento
// relacionado
function bluify(e){
console.log(this === e.currentTarget); // Siempre true
console.log(this === e.target); // true cuando currentTarget y target son el mismo objeto
this.style.backgroundColor = '#A5D9F3';
}
// Consigue una lista de cada elemento en un documento
var elements = document.getElementsByTagName('*');
// A帽ade bluify como un click listener asi cuando se hace click sobre el elemento,
// este cambia a azul
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}