Funciones constructoras y new en JavaScript | Nicolás Garzón
Una función constructora es una función diseñada para ejecutarse con new y configurar objetos que comparten un prototipo.
JavaScript
Copiar function User ( name, role ) {
this . name = name;
this . role = role;
}
const user = new User ( "Nicolás" , "developer" ) ; JavaScript
Copiar const user = new User ( "Nicolás" , "developer" ) ; JavaScript realiza conceptualmente estos pasos:
Crea un objeto nuevo.
Conecta su [[Prototype]] con User.prototype.
Ejecuta User usando el objeto nuevo como this.
Retorna el objeto nuevo, salvo que el constructor retorne explícitamente otro objeto.
JavaScript
Copiar function emulateNew ( Constructor, ... args ) {
const instance = Object. create ( Constructor . prototype) ;
const result = Constructor . apply ( instance, args) ;
const returnedObject =
result !== null &&
( typeof result === "object" ||
typeof result === "function" ) ;
return returnedObject ? result : instance;
} Es un modelo educativo, no una implementación completa de todas las reglas internas.
JavaScript
Copiar function Product ( name, price ) {
this . name = name;
this . price = price;
this . isActive = true ;
} Cada llamada con new crea propiedades propias en una instancia diferente.
JavaScript
Copiar const keyboard = new Product ( "Keyboard" , 120 ) ;
const mouse = new Product ( "Mouse" , 60 ) ;
keyboard !== mouse; No es necesario crear la misma función por instancia.
JavaScript
Copiar Product . prototype. calculateTax = function ( rate ) {
return this . price * rate;
} ; JavaScript
Copiar keyboard. calculateTax === mouse. calculateTax; Ambas instancias encuentran el método en Product.prototype.
Si el constructor define el método dentro:
JavaScript
Copiar function Product ( name, price ) {
this . name = name;
this . price = price;
this . calculateTax = function ( rate ) {
return this . price * rate;
} ;
} Cada instancia recibe una función diferente.
Esto puede ser correcto si el método necesita un closure privado, pero no debe hacerse sin intención.
JavaScript
Copiar User . prototype; Es un objeto utilizado por new como prototipo de las instancias.
JavaScript
Copiar Object. getPrototypeOf ( user) === User . prototype; La propiedad prototype no es el prototipo de la función User como objeto. Son relaciones distintas:
JavaScript
Copiar Object. getPrototypeOf ( User) === Function . prototype;
User.prototype: objeto que heredarán sus instancias.
Object.getPrototypeOf(User): prototipo de la propia función.
El objeto creado como User.prototype suele contener una propiedad constructor que apunta a User.
JavaScript
Copiar User . prototype. constructor === User; No es una garantía de seguridad ni el mecanismo principal de creación. Puede cambiarse.
JavaScript
Copiar user. constructor = AnotherFunction; Por eso no debe utilizarse como prueba confiable de tipo en datos no controlados.
JavaScript
Copiar user instanceof User ; instanceof revisa si User.prototype aparece en la cadena de prototipos de user.
No compara el nombre del constructor ni la forma de las propiedades.
JavaScript
Copiar const fakeUser = {
name : "Nicolás" ,
role : "developer" ,
} ;
fakeUser instanceof User ; Tiene la misma forma, pero no la misma cadena prototípica.
JavaScript
Copiar function User ( name ) {
this . name = name;
return 100 ;
}
new User ( "Nicolás" ) ; El primitivo se ignora y se retorna la instancia.
JavaScript
Copiar function User ( name ) {
this . name = name;
return {
name : "Replacement" ,
} ;
} El objeto explícito reemplaza la instancia creada por new.
Esto suele confundir y puede romper la relación con User.prototype. Evítalo salvo que exista una razón clara.
JavaScript
Copiar function User ( name ) {
this . name = name;
}
User ( "Nicolás" ) ; En modo estricto, this es undefined y se produce un error.
En código no estricto, podría escribir accidentalmente en el objeto global. Es una de las razones por las que las clases modernas exigen new.
JavaScript
Copiar function User ( name ) {
if ( ! new . target) {
return new User ( name) ;
}
this . name = name;
} new.target indica qué constructor inició la llamada con new.
Otra estrategia es lanzar un error para mantener un contrato explícito.
JavaScript
Copiar if ( ! new . target) {
throw new TypeError ( "User must be called with new" ) ;
} JavaScript
Copiar function Base ( ) {
console. log ( new . target?. name) ;
}
function Child ( ) {
Base . call ( this ) ;
}
Child . prototype = Object. create ( Base . prototype) ;
Child . prototype. constructor = Child;
new Child ( ) ; En esta llamada manual mediante Base.call, new.target dentro de Base es undefined porque Base no fue invocada con new.
Las clases y super() conservan correctamente la construcción derivada mediante mecanismos internos.
Una arrow function no puede usarse con new.
JavaScript
Copiar const User = ( ) => { } ;
new User ( ) ; Los métodos abreviados tampoco son constructibles.
JavaScript
Copiar const object = {
create ( ) { } ,
} ;
new object. create ( ) ; Muchas funciones normales sí implementan capacidad de construcción, aunque no todas estén diseñadas semánticamente como constructores.
JavaScript
Copiar class User {
constructor ( name ) {
this . name = name;
}
} JavaScript
Copiar new User ( "Nicolás" ) ;
User ( "Nicolás" ) ; Las clases ofrecen una sintaxis más estricta sobre el sistema de prototipos.
JavaScript
Copiar const user = new User ( "Nicolás" ) ;
Usa new.
Inicializa mediante this.
Conecta la instancia con User.prototype.
Funciona naturalmente con instanceof.
JavaScript
Copiar const user = createUser ( "Nicolás" ) ;
Se llama como función normal.
Retorna el objeto explícitamente.
Puede usar closures y composición.
No necesita representar una instancia de un tipo prototípico.
Ningún enfoque es universalmente superior.
JavaScript
Copiar function Product ( name, price ) {
if ( ! name?. trim ( ) ) {
throw new TypeError ( "Name is required" ) ;
}
if ( ! Number. isFinite ( price) || price < 0 ) {
throw new TypeError (
"Price must be a non-negative number" ,
) ;
}
this . name = name. trim ( ) ;
this . price = price;
} Una construcción fallida no retorna una instancia utilizable.
Sin embargo, un constructor con demasiada lógica, I/O o dependencias ocultas puede ser difícil de probar. La creación compleja puede delegarse a factories o servicios.
JavaScript
Copiar function User ( name ) {
this . name = name;
}
const first = new User ( "First" ) ;
User . prototype. greet = function ( ) {
return ` Hello, ${ this . name} ` ;
} ;
first. greet ( ) ; La instancia apunta al mismo objeto User.prototype, que fue modificado.
Pero reemplazar el objeto completo afecta solo a instancias futuras.
JavaScript
Copiar User . prototype = {
greet ( ) {
return ` Hi, ${ this . name} ` ;
} ,
} ;
const second = new User ( "Second" ) ;
Object. getPrototypeOf ( first) ===
Object. getPrototypeOf ( second) ; Modificar prototipos después de crear objetos puede volver el sistema difícil de razonar. Define su comportamiento de forma estable.
JavaScript
Copiar function Order ( id, items = [ ] ) {
if ( ! new . target) {
throw new TypeError ( "Order requires new" ) ;
}
this . id = id;
this . items = [ ... items] ;
this . status = "pending" ;
}
Order . prototype. complete = function ( ) {
this . status = "completed" ;
} ;
Order . prototype. calculateTotal = function ( ) {
return this . items. reduce (
( total, item ) =>
total + item. price * item. quantity,
0 ,
) ;
} ; Las propiedades pertenecen a cada pedido; los métodos se comparten.
Pensar que new solo llama la función normalmente.
Confundir Constructor.prototype con el prototipo de la función.
Crear métodos dentro del constructor sin considerar que se duplican.
Olvidar new y modificar el objeto global en código antiguo.
Retornar otro objeto desde un constructor sin intención.
Usar constructor como verificación confiable de tipo.
Pensar que instanceof compara propiedades.
Intentar construir una arrow function o método abreviado.
Reemplazar prototype después de crear instancias.
Usar constructor para operaciones asíncronas complejas.
new crea un objeto, conecta su prototipo y llama al constructor con ese this.
Los métodos pueden compartirse mediante Constructor.prototype.
instanceof revisa la cadena de prototipos.
Un objeto retornado explícitamente reemplaza la instancia.
Los primitivos retornados se ignoran.
new.target detecta una construcción.
Arrow functions y métodos abreviados no son constructibles.
Las clases utilizan el mismo modelo prototípico con reglas más estrictas.
Factories y constructores resuelven necesidades distintas.
JavaScript
Copiar function User ( name ) {
this . name = name;
return {
name : "Replacement" ,
} ;
}
const user = new User ( "Nicolás" ) ;
console. log ( user. name) ;
console. log ( user instanceof User ) ; Respuesta Imprime "Replacement" y false. El objeto retornado explícitamente reemplaza la instancia conectada a User.prototype.
Prototipos explica el mecanismo de delegación que permite a las instancias compartir comportamiento.