new.target: Detectar el Uso de new
Aprende a usar new.target para detectar si una función o clase fue invocada con el operador new en JavaScript moderno.
TL;DR - Resumen rápido
- new.target es undefined si no se usa new, y apunta al constructor si se usa
- En herencia, new.target apunta a la clase que se está instanciando, no al padre
- Permite implementar clases abstractas que no pueden ser instanciadas directamente
- El patrón factory puede usar new.target para auto-corregir llamadas sin new
- No funciona en arrow functions (heredan contexto) ni en métodos de instancia
- Es ideal para validar constructores y crear arquitecturas polimórficas
Introducción a new.target
new.target es una propiedad especial que está disponible en todas las funciones y clases en JavaScript. Permite detectar si una función o clase fue invocada con el operador new o como una función normal.
Propiedad meta
new.target es una propiedad meta que JavaScript proporciona automáticamente en todas las funciones y clases. No necesitas definirla, simplemente puedes usarla directamente.
Sintaxis Básica de new.target
new.target es una meta-propiedad que está disponible en funciones constructoras y clases. Su valor es undefined cuando la función se invoca sin new, y apunta al constructor cuando se invoca con new. Esto permite detectar cómo fue llamada la función y actuar en consecuencia.
En este ejemplo vemos que new.target es undefined cuando llamamos a una función sin new, pero apunta al constructor cuando usamos new. Las clases ES6 siempre requieren new, por lo que intentar llamarlas sin new genera un error automático. Podemos usar la verificación !new.target para forzar el uso correcto de constructores.
Meta-propiedad automática
new.target no necesita ser definida o importada. JavaScript la proporciona automáticamente en el contexto de funciones constructoras y clases durante su invocación.
new.target en Herencia
Una de las características más poderosas de new.target es que en jerarquías de herencia, new.target apunta a la clase que realmente se está instanciando, no a la clase padre cuyo constructor se está ejecutando. Esto permite que las clases base detecten qué subclase las está llamando.
Cuando instanciamos una subclase como Perro o Gato, el constructor de la clase padre Animal se ejecuta mediante super(), pero new.target apunta a la subclase real (Perro o Gato), no a Animal. Esto es diferente de this.constructor, aunque en la práctica suelen tener el mismo valor. Esta característica es fundamental para implementar logging automático, sistemas de registro y patrones de diseño avanzados.
new.target detecta la clase real
En herencia, new.target siempre apunta a la clase que se está instanciando con new, no a la clase cuyo constructor se está ejecutando. Esto permite que las clases base sepan qué subclase las invocó.
Clases Abstractas con new.target
new.target permite implementar clases abstractas en JavaScript: clases que no pueden ser instanciadas directamente y que solo sirven como base para otras clases. Verificamos si new.target === ClaseBase y lanzamos un error si es así, permitiendo solo la instanciación de subclases.
En estos ejemplos, las clases Forma y BaseDatos son abstractas: no pueden ser instanciadas directamente. La verificación new.target === Forma detecta si estamos intentando crear una instancia de la clase base y lanza un error. Solo las subclases como Rectangulo, Circulo, MySQL o PostgreSQL pueden ser instanciadas. Además, podemos verificar que las subclases implementen métodos requeridos, simulando interfaces o contratos.
Patrón de diseño fundamental
Las clases abstractas con new.target son fundamentales para arquitecturas orientadas a objetos en JavaScript. Fuerzan el polimorfismo y garantizan que las subclases implementen la funcionalidad requerida.
Factory Pattern con Auto-corrección
Una estrategia común es usar new.target para auto-corregir llamadas incorrectas: si la función se invoca sin new, automáticamente la llamamos de nuevo con new. Esto hace que los constructores sean más amigables y funcionen correctamente sin importar cómo se llamen.
El patrón de auto-corrección verifica !new.target y, si es true, retorna new Constructor(...args). Esto permite que la función funcione correctamente tanto si se llama con new como sin él. Es especialmente útil para APIs públicas donde los usuarios podrían olvidar usar new, y para factory methods que pueden crear instancias de múltiples formas. La auto-corrección hace que el código sea más flexible y menos propenso a errores.
Flexibilidad vs Estrictez
Puedes elegir entre lanzar un error (estricto), advertir (flexible con feedback) o auto-corregir (máxima flexibilidad). La auto-corrección es ideal para APIs públicas, mientras que lanzar error es mejor para código interno donde quieres detectar bugs temprano.
Diferentes Formas de Verificar el Uso de new
Existen múltiples estrategias para manejar el uso (o la falta) del operador new en constructores. Cada estrategia tiene sus ventajas según el contexto: desde lanzar errores estrictos hasta permitir auto-corrección silenciosa, pasando por advertencias y verificaciones condicionales.
Las estrategias van desde la más estricta (lanzar TypeError) hasta la más permisiva (auto-corrección silenciosa). Lanzar un error es ideal para APIs públicas donde quieres forzar el uso correcto. Las advertencias son útiles durante migraciones o refactorizaciones. La auto-corrección es conveniente pero puede ocultar errores. También puedes combinar new.target con verificaciones de tipo para implementar clases abstractas que solo permiten subclases, no instancias directas de la clase base.
Elige según tu caso de uso
Para bibliotecas públicas usa errores estrictos. Para código interno donde controlas todo usa auto-corrección. Para clases base abstractas usa new.target === ClaseBase para permitir solo subclases.
Limitaciones y Casos Especiales
new.target tiene varias limitaciones importantes. No funciona en arrow functions (aunque heredan el valor del contexto externo), no está disponible en métodos de instancia, y no funciona con funciones llamadas mediante call, apply o bind. También tiene comportamientos especiales con Object.create y en funciones anidadas.
Las arrow functions heredan new.target del contexto donde fueron definidas, pero las funciones anidadas normales no. new.target solo está disponible durante la fase de construcción con new, no en métodos de instancia normales. Si necesitas la referencia al constructor después de la construcción, debes guardarla en una propiedad durante el constructor. Las funciones llamadas con call, apply o bind tendrán new.target como undefined porque no se están llamando con new.
Solo en constructores
new.target solo está disponible en constructores (funciones llamadas con new) y en arrow functions definidas dentro de constructores. No intentes usarlo en métodos de instancia o funciones normales.
Ejemplo Práctico: Sistema de Plugins
Este ejemplo completo demuestra cómo usar new.target para crear un sistema de plugins con auto-registro, clases abstractas, verificación de implementación de métodos y detección automática del tipo de plugin. Combina múltiples conceptos en una arquitectura real y práctica.
Este sistema de plugins usa new.target para múltiples propósitos: previene la instanciación directa de la clase abstracta Plugin, detecta automáticamente el nombre de la subclase para registro, y verifica que cada subclase implemente los métodos requeridos. Cada plugin se auto-registra en un Map global durante su construcción, permitiendo un sistema de gestión centralizado. Los plugins concretos (CachePlugin, LoggerPlugin, ValidadorPlugin) solo necesitan extender Plugin e implementar los tres métodos abstractos.
Arquitectura completa
Este ejemplo muestra cómo new.target permite crear arquitecturas extensibles con clases abstractas, auto-registro, verificación de contratos y polimorfismo, todo sin librerías externas.
Resumen: new.target
Conceptos principales:
- •new.target es undefined sin new, apunta al constructor con new
- •En herencia apunta a la subclase real, no a la clase padre
- •Permite implementar clases abstractas verificando new.target === Base
- •El patrón factory puede auto-corregir con return new Constructor()
- •No funciona en métodos de instancia ni con call/apply/bind
- •Arrow functions heredan new.target del contexto externo
Mejores prácticas:
- •Usa new.target === Clase para prevenir instanciación de clases base
- •Implementa auto-corrección para APIs públicas amigables
- •Lanza errores estrictos en código interno para detectar bugs temprano
- •Guarda new.target.name durante construcción para logging/telemetría
- •Verifica métodos requeridos en constructores de clases abstractas
- •Combina con instanceof para arquitecturas polimórficas robustas