Desmitifiquemos la 'nueva' palabra clave de JavaScript

Durante el fin de semana, completé el JavaScript de Will Sentance: The Hard Parts. Puede que no suene como la forma más gloriosa de pasar un fin de semana, pero en realidad me pareció bastante divertido y relajante completar el curso. Tocó programación funcional, funciones de orden superior, cierres y JavaScript asincrónico.

Para mí, lo más destacado del curso fue cómo amplió los enfoques de JavaScript para la programación orientada a objetos (OOP) y desmitificó la magia detrás del nuevo operador. Ahora tengo una comprensión completa de lo que sucede debajo del capó cuando se usa el nuevo operador.

Programación orientada a objetos en JavaScript

La programación orientada a objetos (OOP) es un paradigma de programación basado en el concepto de "objetos". Los datos y funciones (atributos y métodos) se agrupan dentro de un objeto.

Un objeto en JavaScript es una colección de pares clave-valor. Estos pares clave-valor son propiedades del objeto. Una propiedad puede ser una matriz, una función, un objeto en sí mismo o cualquier tipo de datos primitivo, como cadenas o números enteros.

¿Qué técnicas tenemos en nuestra caja de herramientas de JavaScript para la creación de objetos?

Supongamos que estamos creando usuarios en un juego que acabamos de diseñar. ¿Cómo almacenaríamos los detalles del usuario, como sus nombres, puntos, e implementaríamos métodos como un incremento en puntos? Aquí hay dos opciones para la creación de objetos básicos.

Opción 1 - Notación literal de objeto

let user1 = { name: "Taylor", points: 5, increment: function() { user1.points++; } };

Un literal de objeto de JavaScript es una lista de pares de nombre-valor entre llaves. En el ejemplo anterior, se crea el objeto 'usuario1' y los datos asociados se almacenan en él.

Opción 2 - Object.create ()

Object.create(proto, [ propertiesObject ])

Object.create Los métodos aceptan dos argumentos:

  1. proto: el objeto que debería ser el prototipo del objeto recién creado. Tiene que ser un objeto o nulo.
  2. propertiesObject: propiedades del nuevo objeto. Este argumento es opcional.

Básicamente, pasa a Object.createun objeto del que desea heredar y devuelve un nuevo objeto que hereda del objeto que le pasó.

let user2 = Object.create(null); user2.name = "Cam"; user2.points = 8; user2.increment = function() { user2.points++; }

Las opciones básicas de creación de objetos anteriores son repetitivas. Requiere que cada uno se cree manualmente.

¿Cómo superamos esto?

Soluciones

Solución 1: generar objetos usando una función

Una solución simple es escribir una función para crear nuevos usuarios.

function createUser(name, points) { let newUser = {}; newUser.name = name; newUser.points = points; newUser.increment = function() { newUser.points++; }; return newUser; }

Para crear un usuario, ahora debe ingresar la información en los parámetros de la función.

let user1 = createUser("Bob", 5); user1.increment();

Sin embargo, la función de incremento en el ejemplo anterior es solo una copia de la función de incremento original. Esta no es una buena manera de escribir su código, ya que cualquier cambio potencial en la función deberá realizarse manualmente para cada objeto.

Solución 2: utilice la naturaleza prototípica de JavaScript

A diferencia de los lenguajes orientados a objetos como Python y Java, JavaScript no tiene clases. Utiliza el concepto de prototipos y el encadenamiento de prototipos para la herencia.

Cuando se crea una nueva matriz, automáticamente tiene acceso a una función de métodos tales como Array.join, Array.sort, y Array.filter. Esto se debe al hecho de que los objetos de matriz heredan propiedades de Array.prototype.

Cada función de JavaScript tiene una propiedad de prototipo, que está vacía de forma predeterminada. Puede agregar funciones a esta propiedad de prototipo y, de esta forma, se conoce como método. Cuando se ejecuta una función heredada, el valor de esta apunta al objeto heredado.

function createUser(name, points) { let newUser = Object.create(userFunction); newUser.name = name; newUser.points = points; return newUser; } let userFunction = { increment: function() {this.points++}; login: function() {console.log("Please login.")}; } let user1 = createUser("Bob", 5); user1.increment();

Cuando user1se creó el objeto, se formó un vínculo de cadena prototipo con userFunction.

Cuando user1.increment() está en la pila de llamadas, el intérprete buscará user1 en la memoria global. A continuación, buscará la función de incremento, pero no la encontrará. El intérprete mirará el siguiente objeto en la cadena del prototipo y encontrará la función de incremento allí.

Solución 3: palabras clave nuevas y estas

losEl operador nuevo se usa para crear una instancia de un objeto que tiene una función de constructor.

Cuando llamamos a la función constructora con new, automatizamos las siguientes acciones:

  • Se crea un nuevo objeto
  • Se une thisal objeto
  • El objeto prototipo de la función constructora se convierte en la propiedad __proto__ del nuevo objeto
  • Devuelve el objeto de la función

¡Esto es fantástico, porque la automatización da como resultado un código menos repetitivo!

function User(name, points) { this.name = name; this.points = points; } User.prototype.increment = function(){ this.points++; } User.prototype.login = function() { console.log(“Please login.”) } let user1 = new User(“Dylan”, 6); user1.increment();

Al usar el patrón de prototipo, cada método y propiedad se agrega directamente en el prototipo del objeto.

El intérprete subirá por la cadena del prototipo y encontrará la función de incremento en la propiedad prototipo de Usuario, que a su vez también es un objeto con la información dentro. Recuerde: todas las funciones de JavaScript también son objetos . Ahora que el intérprete ha encontrado lo que necesita, puede crear un nuevo contexto de ejecución local para ejecutar user1.increment().

Nota al margen: diferencia entre __proto__ y prototipo

Si ya se está confundiendo acerca de __proto__ y el prototipo, ¡no se preocupe! Estás lejos de ser el único confundido sobre esto.

El prototipo es una propiedad de la función constructora que determina qué se convertirá en la propiedad __proto__ en el objeto construido.

Entonces, __proto__ es la referencia creada, y esa referencia se conoce como el enlace de cadena del prototipo.

Solución 4 - ES6 'azúcar sintáctico'

Otros lenguajes nos permiten escribir nuestros métodos compartidos dentro del propio “constructor” del objeto. ECMAScript6 introdujo la palabra clave class , que nos permite escribir clases que se asemejan a clases normales de otros lenguajes clásicos. En realidad, es azúcar sintáctico sobre el comportamiento prototípico de JavaScript.

class User { constructor(name, points) { this.name = name; this.points = points; } increment () { this.points++; } login () { console.log("Please login.") } } let user1 = new User("John", 12); user1.increment();

En la solución 3, los métodos asociados se implementaron con precisión utilizando User.prototype.functionName. En esta solución, se obtienen los mismos resultados pero la sintaxis parece más limpia.

Conclusión

Ahora hemos aprendido más sobre las diferentes opciones que tenemos en JavaScript para crear objetos. Mientras que las declaraciones de clase y elLos nuevos operadores son relativamente fáciles de usar, es importante comprender qué está automatizado.

En resumen, las siguientes acciones se automatizan cuando se llama a la función constructora con new :

  • Se crea un nuevo objeto
  • Se une thisal objeto
  • El objeto prototipo de la función constructora se convierte en la propiedad __proto__ del nuevo objeto
  • Devuelve el objeto de la función

Thanks for reading my article, and clap if you liked it! Check out my other articles like How I built my Pomodoro Clock app, and the lessons I learned along the way.