Menu

this en JavaScript: reglas de binding y errores comunes

Cómo funciona realmente this en JavaScript: las cuatro reglas de binding, por qué las arrow functions son distintas y cómo evitar el típico error 'this is undefined'.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

.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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

setTimeout invoca la función como una llamada normal, no como c.increment(). Tienes tres formas de solucionarlo:

index.js
Output
Click Run to see the output here.

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): this es window.
  • Módulo ES (incluyendo la mayoría del código moderno empaquetado): this es undefined.
  • Módulo CommonJS de Node.js: this es module.exports.

Si necesitas una referencia confiable al objeto global en cualquier entorno, usa globalThis:

index.js
Output
Click Run to see the output here.

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:

  1. ¿La función es una arrow function? Entonces this es el mismo que el del scope que la contiene. El sitio desde donde se llama da igual.
  2. ¿Se invocó con new? Entonces this es el objeto recién creado.
  3. ¿Se llamó usando .call, .apply o una función con bind? Entonces this es el valor que le pasaste.
  4. ¿Se llamó como obj.method()? Entonces this es obj.
  5. ¿Se llamó como una función suelta fn()? Entonces this es undefined en 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 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?

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?

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?

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.

Aprende a programar con Coddy

COMENZAR