this en JavaScript se decide al momento de la llamada
La mayor parte de la confusión con la palabra clave this en JavaScript viene de una idea equivocada: creer que this depende de dónde se define la función. No es así. this depende de cómo se invoca la función.
Fíjate en la misma función llamada de dos maneras distintas:
La misma función, pero con distinto this. En la primera llamada, user está antes del punto, así que this es user. En la segunda no hay nada antes, por lo que this queda como undefined. La función en sí no cambió: lo que cambió fue el lugar desde donde se la invocó.
Quédate con esta idea: para saber cuánto vale this, fíjate en la llamada, no en la definición.
Las cuatro reglas de binding de this en JavaScript
Existen cuatro formas en que se asigna this. Prácticamente cualquier duda que tengas sobre la palabra clave this en JavaScript se resuelve identificando cuál de las cuatro aplica.
1. Llamada como método: obj.fn()
Cuando una función se invoca como propiedad de un objeto, this apunta a ese objeto:
Lo que aparece antes del punto se convierte en this. Así de simple.
2. Llamada directa: fn()
Cuando invocas una función sin ningún objeto delante, this vale undefined en modo estricto (que los módulos y las clases activan por defecto), o bien apunta al objeto global en modo no estricto:
De ahí salen los famosos errores de "this is undefined". Sacar un método de su objeto convierte la llamada al método en una simple invocación de función:
No hay counter. antes de la llamada, así que no hay binding. La función no recuerda el objeto del que salió.
3. Binding explícito: .call(), .apply() y .bind()
Puedes forzar el valor de this al que tú quieras:
.call y .apply ejecutan la función al instante; la única diferencia está en cómo le pasas los argumentos. .bind, en cambio, devuelve una función nueva con this fijado de forma permanente, algo muy útil cuando trabajas con callbacks.
4. Llamada con new: new Fn()
Cuando invocas una función con new, se crea un objeto nuevo y se enlaza automáticamente a this:
Las clases usan este mecanismo por debajo. Las veremos en un capítulo posterior.
Las arrow functions no tienen su propio this
Las arrow functions rompen las reglas anteriores a propósito. No hacen binding de this en absoluto: lo heredan del scope que las rodea, fijado en el momento en el que se define la arrow function:
La arrow function en el nivel superior de un módulo captura el this del módulo, que es undefined. Aunque la llames como user.arrow(), la arrow se niega a reasignar su this.
Parece un bug, pero justamente de eso se trata. Las arrow functions brillan dentro de métodos, cuando quieres conservar el this del contexto externo:
La función flecha dentro de setInterval hereda el this de start, que se llamó como timer.start(). Por eso this.seconds funciona sin problemas. Si ahí hubieras puesto una function normal, habría tenido su propio this (el que le pasara setInterval) y todo se habría roto.
Regla práctica: usa arrow functions para los callbacks dentro de métodos, y funciones normales para los métodos en sí.
El clásico problema de this en callbacks
Esta es la forma más habitual en que this se te va de las manos. Pasas un método como callback y pierde su binding:
setTimeout invoca la función como una llamada normal, no como c.increment(). Tienes tres formas de solucionarlo:
Las tres opciones funcionan. El envoltorio con arrow function suele ser el más claro.
this en el nivel superior
El valor de this en el nivel superior depende de dónde se ejecute tu código:
- Script de navegador (no módulo):
thiseswindow. - Módulo ES (incluyendo la mayoría del código moderno empaquetado):
thisesundefined. - Módulo CommonJS de Node.js:
thisesmodule.exports.
Si necesitas una referencia confiable al objeto global en cualquier entorno, usa globalThis:
En la práctica, conviene no depender del this de nivel superior. Cuando de verdad necesites el objeto global, usa globalThis; en el resto de los casos, pasa los valores de forma explícita.
Árbol de decisión rápido
Cuando te encuentres con un this y no tengas claro a qué apunta, repasa esta lista en orden:
- ¿La función es una arrow function? Entonces
thises el mismo que el del scope que la contiene. El sitio desde donde se llama da igual. - ¿Se invocó con
new? Entoncesthises el objeto recién creado. - ¿Se llamó usando
.call,.applyo una función conbind? Entoncesthises el valor que le pasaste. - ¿Se llamó como
obj.method()? Entoncesthisesobj. - ¿Se llamó como una función suelta
fn()? Entoncesthisesundefineden modo estricto.
Esa escalera, en ese orden, resuelve cualquier caso.
Siguiente paso: funciones de orden superior
Ahora que this ya no es un misterio, estás listo para el patrón que le da a JavaScript buena parte de su expresividad: pasar funciones como si fueran valores. En la próxima sección veremos las funciones de orden superior —aquellas que reciben o devuelven otras funciones— y cómo son el motor detrás de los métodos de arrays, los manejadores de eventos y la mayor parte del código JavaScript que te vas a encontrar en el mundo real.
Preguntas frecuentes
¿A qué hace referencia this en JavaScript?
this en JavaScript?this apunta al objeto sobre el que se llama la función, no al lugar donde la función fue definida. En user.greet(), this es user. Si llamas greet() por su cuenta, this será undefined en strict mode (o el objeto global en sloppy mode). La clave está en cómo se invoca la función, no en dónde se escribió.
¿Por qué this sale undefined dentro de mi función?
this sale undefined dentro de mi función?Lo más probable es que hayas sacado el método de su objeto y lo llames suelto, o que lo pases como callback. const fn = user.greet; fn(); pierde el binding, porque al invocarlo ya no hay ningún objeto a la izquierda del punto. Se arregla con .bind(user), envolviéndolo en una arrow function, o llamándolo como user.greet().
¿En qué se diferencia this dentro de una arrow function?
this dentro de una arrow function?Las arrow functions no tienen su propio this: lo heredan del scope en el que se definen. Por eso son ideales como callbacks dentro de métodos, cuando quieres conservar el this externo. Como contrapartida, .call(), .apply() y .bind() no pueden cambiar el this de una arrow function.
¿Qué es this en el nivel superior de un script?
this en el nivel superior de un script?En un script normal del navegador, el this del nivel superior es el objeto window. En un módulo ES es undefined. En módulos CommonJS de Node.js es module.exports. Si necesitas una referencia que funcione en cualquier entorno, usa globalThis, que siempre apunta al objeto global.