La sintaxis normal {...}
nos permite crear un objeto. Pero a menudo necesitamos crear muchos objetos similares, como múltiples usuarios o elementos de menú, etc.
Esto se puede hacer usando funciones constructoras y el operador "new"
.
Las funciones del constructor técnicamente son funciones regulares. Sin embargo, existen dos convenciones:
Se nombran con mayúscula primero.
Deben ejecutarse únicamente con el operador "new"
.
Por ejemplo:
función Usuario (nombre) { this.nombre = nombre; this.isAdmin = falso; } let usuario = nuevo Usuario("Jack"); alerta(nombre.usuario); // Jacobo alerta(usuario.isAdmin); // FALSO
Cuando se ejecuta una función con new
, realiza los siguientes pasos:
Se crea un nuevo objeto vacío y se this
asigna.
El cuerpo de la función se ejecuta. Por lo general, modifica this
y le agrega nuevas propiedades.
Se devuelve el valor de this
.
En otras palabras, new User(...)
hace algo como:
función Usuario (nombre) { // esto = {}; (implícitamente) // agregar propiedades a esto this.nombre = nombre; this.isAdmin = falso; // devuelve esto; (implícitamente) }
Entonces let user = new User("Jack")
da el mismo resultado que:
dejar usuario = { nombre: "Jack", isAdmin: falso };
Ahora, si queremos crear otros usuarios, podemos llamar a new User("Ann")
, new User("Alice")
, etc. Mucho más corto que usar literales cada vez y también fácil de leer.
Ese es el objetivo principal de los constructores: implementar código de creación de objetos reutilizable.
Notemos una vez más: técnicamente, cualquier función (excepto las funciones de flecha, ya que no tienen this
) se puede usar como constructor. Se puede ejecutar con new
y ejecutará el algoritmo anterior. La “letra mayúscula primero” es un acuerdo común, para dejar claro que una función se ejecutará con new
.
nueva función() {...}
Si tenemos muchas líneas de código sobre la creación de un único objeto complejo, podemos envolverlas en una función constructora llamada inmediatamente, como esta:
// crea una función e inmediatamente la llama con new dejar usuario = nueva función() { this.nombre = "Juan"; this.isAdmin = falso; // ...otro código para la creación de usuarios // tal vez lógica y declaraciones complejas // variables locales, etc. };
Este constructor no se puede volver a llamar porque no se guarda en ningún lugar, solo se crea y se llama. Entonces, este truco tiene como objetivo encapsular el código que construye el objeto único, sin reutilizarlo en el futuro.
Cosas avanzadas
La sintaxis de esta sección rara vez se utiliza; sáltala a menos que quieras saberlo todo.
Dentro de una función, podemos verificar si fue llamada con new
o sin ella, usando una propiedad especial new.target
.
No está definido para llamadas regulares y es igual a la función si se llama con new
:
función Usuario() { alerta(nuevo.objetivo); } // sin "nuevo": Usuario(); // indefinido // con "nuevo": nuevo usuario(); // función Usuario {...}
Eso se puede usar dentro de la función para saber si se llamó con new
, "en modo constructor", o sin él, "en modo normal".
También podemos realizar llamadas tanto new
como regulares para hacer lo mismo, así:
función Usuario (nombre) { if (!new.target) { // si me ejecutas sin new devolver nuevo Usuario (nombre); // ...agregaré algo nuevo para ti } this.nombre = nombre; } let john = Usuario("John"); // redirige la llamada al nuevo usuario alerta(juan.nombre); // John
Este enfoque se utiliza a veces en bibliotecas para hacer la sintaxis más flexible. Para que la gente pueda llamar a la función con o sin new
y todavía funciona.
Sin embargo, probablemente no sea bueno usarlo en todas partes, porque omitir new
hace que sea un poco menos obvio lo que está pasando. Con new
todos sabemos que se está creando el nuevo objeto.
Por lo general, los constructores no tienen una declaración return
. Su tarea es escribir todo lo necesario en this
y automáticamente se convierte en el resultado.
Pero si hay una declaración return
, entonces la regla es simple:
Si se llama return
con un objeto, entonces se devuelve el objeto en lugar de this
.
Si se llama return
con una primitiva, se ignora.
En otras palabras, return
con un objeto devuelve ese objeto, en todos los demás casos se devuelve this
.
Por ejemplo, aquí return
anula this
al devolver un objeto:
función GranUsuario() { this.nombre = "Juan"; devolver {nombre: "Godzilla"}; // <-- devuelve este objeto } alerta (nuevo BigUser().nombre); // Godzilla, tienes ese objeto
Y aquí hay un ejemplo con un return
vacío (o podríamos colocar una primitiva después, no importa):
función UsuarioPequeño() { this.nombre = "Juan"; devolver; // <-- devuelve esto } alerta (nuevo SmallUser().nombre); // John
Normalmente los constructores no tienen una declaración return
. Aquí mencionamos el comportamiento especial con objetos devueltos principalmente por razones de exhaustividad.
Omitir paréntesis
Por cierto, podemos omitir los paréntesis después de new
:
let usuario = nuevo usuario; // <-- sin paréntesis // igual que dejar usuario = nuevo Usuario();
Omitir paréntesis aquí no se considera un "buen estilo", pero la sintaxis está permitida por especificación.
El uso de funciones constructoras para crear objetos proporciona una gran flexibilidad. La función constructora puede tener parámetros que definen cómo construir el objeto y qué poner en él.
Por supuesto, podemos agregar a this
no solo propiedades, sino también métodos.
Por ejemplo, new User(name)
a continuación crea un objeto con el name
de pila y el método sayHi
:
función Usuario (nombre) { this.nombre = nombre; this.sayHola = función() { alerta( "Mi nombre es: " + este.nombre ); }; } let john = nuevo Usuario("John"); juan.diHola(); // Mi nombre es: Juan /* juan = { nombre: "Juan", decir Hola: función() {...} } */
Para crear objetos complejos, existe una sintaxis más avanzada, las clases, que cubriremos más adelante.
Las funciones constructoras o, brevemente, constructores, son funciones regulares, pero existe un acuerdo común para nombrarlas con letra mayúscula primero.
Las funciones del constructor solo deben llamarse usando new
. Tal llamada implica crear un this
vacío al principio y devolver el poblado al final.
Podemos usar funciones constructoras para crear múltiples objetos similares.
JavaScript proporciona funciones de construcción para muchos objetos de lenguaje integrados: como Date
para fechas, Set
para conjuntos y otros que planeamos estudiar.
Objetos, ¡volveremos!
En este capítulo sólo cubrimos los conceptos básicos sobre objetos y constructores. Son esenciales para aprender más sobre los tipos de datos y funciones en los próximos capítulos.
Después de aprender eso, volvemos a los objetos y los cubrimos en profundidad en los capítulos Prototipos, herencia y Clases.
importancia: 2
¿Es posible crear funciones A
y B
para que new A() == new B()
?
función A() {...} función B() {...} sea a = nueva A(); sea b = nuevo B(); alerta( a == b ); // verdadero
Si es así, proporcione un ejemplo de su código.
Sí, es posible.
Si una función devuelve un objeto, entonces new
lo devuelve en lugar de this
.
Entonces pueden, por ejemplo, devolver el mismo objeto definido externamente obj
:
dejar objeto = {}; función A() {retorno obj; } función B() {retorno obj; } alerta( nueva A() == nueva B() ); // verdadero
importancia: 5
Cree una Calculator
de función constructora que cree objetos con 3 métodos:
read()
solicita dos valores y los guarda como propiedades de objeto con nombres a
y b
respectivamente.
sum()
devuelve la suma de estas propiedades.
mul()
devuelve el producto de multiplicación de estas propiedades.
Por ejemplo:
let calculadora = nueva Calculadora(); calculadora.read(); alerta( "Suma=" + calculadora.suma() ); alerta( "Mul=" + calculadora.mul() );
Ejecute la demostración
Abra una caja de arena con pruebas.
función Calculadora() { this.read = función() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); }; this.sum = función() { devolver esto.a + esto.b; }; this.mul = función() { devolver esto.a * esto.b; }; } let calculadora = nueva Calculadora(); calculadora.read(); alerta( "Suma=" + calculadora.suma() ); alerta( "Mul=" + calculadora.mul() );
Abra la solución con pruebas en un sandbox.
importancia: 5
Cree una función constructora Accumulator(startingValue)
.
El objeto que crea debe:
Almacene el “valor actual” en el value
de la propiedad. El valor inicial se establece en el argumento del constructor startingValue
.
El método read()
debe utilizar prompt
para leer un nuevo número y agregarlo al value
.
En otras palabras, la propiedad value
es la suma de todos los valores ingresados por el usuario con el valor inicial startingValue
.
Aquí está la demostración del código:
let acumulador = nuevo acumulador(1); // valor inicial 1 acumulador.read(); // agrega el valor ingresado por el usuario acumulador.read(); // agrega el valor ingresado por el usuario alerta(acumulador.valor); // muestra la suma de estos valores
Ejecute la demostración
Abra una caja de arena con pruebas.
función Acumulador (valor inicial) { this.value = valor inicial; this.read = función() { this.value += +prompt('¿Cuánto agregar?', 0); }; } let acumulador = nuevo acumulador(1); acumulador.read(); acumulador.read(); alerta(acumulador.valor);
Abra la solución con pruebas en un sandbox.