Prototipos de objetos en JavaScript

0 acciones
0
0
0
0

Introducción

 

Los prototipos son el mecanismo mediante el cual los objetos de JavaScript heredan propiedades entre sí. En este artículo, explicamos qué es un prototipo, cómo funcionan las cadenas de prototipos y cómo configurar un prototipo para un objeto.

Requisitos previos

Comprenda las funciones de JavaScript, familiarícese con los fundamentos de JavaScript (consulte Primeros pasos y Bloques de creación) y los principios de OOJS (consulte Introducción a los objetos).

Cadena de prototipos

En la consola del navegador, intente crear un objeto literal:

const myObject = {
city: "Madrid",
greet() {
console.log(`Greetings from ${this.city}`);
},
};
myObject.greet(); // Greetings from Madrid

Este es un objeto con una propiedad de datos, ciudad, y un método, saludo(). Si escribe el nombre del objeto seguido de un punto en la consola, como `myObject.`, esta mostrará una lista de todas las propiedades disponibles para este objeto. Verá que hay muchas más propiedades además de ciudad y saludo.

__defineGetter__
__defineSetter__
__lookupGetter__
__lookupSetter__
__proto__
city
constructor
greet
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOf

Intenta acceder a uno de ellos:

myObject.toString(); // "[object Object]"

Funciona (aunque no está claro qué hace toString()).

¿Cuáles son estas propiedades adicionales y de dónde provienen?

Cada objeto en JavaScript tiene una propiedad integrada llamada prototipo. Un prototipo es un objeto en sí mismo, por lo que tendrá su propio prototipo, creando lo que se denomina una cadena de prototipos. Esta cadena finaliza cuando se llega a un prototipo cuyo prototipo es nulo.

Nota: Una propiedad de un objeto que apunta a su prototipo no se llama prototipo. El nombre no es estándar, pero en la práctica todos los navegadores usan __proto__. La forma estándar de acceder al prototipo de un objeto es el método Object.getPrototypeOf().

 

Al intentar acceder a una propiedad de un objeto: si no se encuentra en el objeto, se busca su prototipo. Si sigue sin encontrarse, se busca el prototipo de la instancia, y así sucesivamente hasta encontrarla o llegar al final de la cadena, en cuyo caso se devuelve undefined.

Entonces, cuando llamamos a myObject.toString(), el navegador:

  • Busca toString en myObject
  • No lo encuentro allí, así que busco en el prototipo del objeto myObject para toString.
  • Lo encuentra allí y lo llama.

¿Cuál es el prototipo de myObject? Para averiguarlo, podemos usar la función Object.getPrototypeOf():

Object.getPrototypeOf(myObject); // Object { }

Este es un objeto llamado Object.prototype y es el prototipo más básico que todos los objetos tienen por defecto. El prototipo Object.prototype es nulo, por lo que se encuentra al final de la cadena de prototipos:

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes/myobject-prototype-chain.svg

El prototipo de un objeto no siempre es Object.prototype. Prueba esto:

const myDate = new Date();
let object = myDate;
do {
object = Object.getPrototypeOf(object);
console.log(object);
} while (object);
// Date.prototype
// Object { }
// null

Este código crea un objeto Date, avanza en la cadena de prototipos y registra los prototipos. Muestra que el prototipo de myDate es un objeto Date.prototype y su prototipo es Object.prototype.

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes/mydate-prototype-chain.svg

De hecho, cuando llamas a métodos familiares, como myDate2.getTime(), estás llamando a un método que está definido en Date.prototype.

Propiedades de sombreado

¿Qué sucede si se define una propiedad en un objeto cuando una propiedad con el mismo nombre está definida en el prototipo del objeto? Veamos:

const myDate = new Date(1995, 11, 17);
console.log(myDate.getTime()); // 819129600000
myDate.getTime = function () {
console.log("something else!");
};
myDate.getTime(); // 'something else!'

Esto debería ser predecible dada la descripción de la cadena de prototipos. Al llamar a getTime(), el navegador primero busca en myDate una propiedad con ese nombre y solo revisa el prototipo si myDate no lo define. Por lo tanto, al agregar getTime() a myDate, se llama a la versión en myDate.

A esto se le llama “hacer sombra” a la propiedad.

Configuración de un prototipo

Hay varias formas de establecer el prototipo de un objeto en JavaScript, y aquí describiremos dos: Object.create() y el constructor.

Usando Object.create

El método Object.create() crea un nuevo objeto y le permite especificar un objeto que se utilizará como prototipo del nuevo objeto.

Aquí tenéis un ejemplo:

const personPrototype = {
greet() {
console.log("hello!");
},
};
const carl = Object.create(personPrototype);
carl.greet(); // hello!

Aquí creamos un objeto personPrototype con un método greet(). Luego, usamos Object.create() para crear un nuevo objeto con personPrototype como prototipo. Ahora podemos llamar a greet() en el nuevo objeto y el prototipo proporcionará su implementación.

Usando el constructor

En JavaScript, todas las funciones tienen una propiedad llamada prototipo. Al llamar a una función como constructor, esta propiedad se establece en el prototipo del objeto recién creado (por convención, en una propiedad llamada __proto__).

Entonces, si establecemos el prototipo de un constructor, podemos garantizar que todos los objetos creados con ese constructor reciban ese prototipo:

const personPrototype = {
greet() {
console.log(`hello, my name is ${this.name}!`);
},
};
function Person(name) {
this.name = name;
}
Object.assign(Person.prototype, personPrototype);
// or
// Person.prototype.greet = personPrototype.greet;

Aquí creamos:

  • Un objeto personPrototype que tiene un método greet()
  • Una función constructora Person() que inicializa el nombre de la persona que se creará.

Luego, colocamos los métodos definidos en personPrototype en la propiedad prototipo de la función Persona usando Object.assign.

Después de este código, los objetos creados con Person.prototype() recibirán como prototipo, el cual contiene automáticamente el método greet.

const reuben = new Person("Reuben");
reuben.greet(); // hello, my name is Reuben!

Esto también explica por qué dijimos anteriormente que el prototipo de myDate se llama Date.prototype: esta es la propiedad prototipo del constructor Date.

Propiedad propia

Los objetos que creamos usando el constructor Persona anterior tienen dos propiedades:

  • Una propiedad de nombre, establecida en el constructor, para que aparezca directamente en los objetos Persona
  • Un método greet() establecido en el prototipo.

Es común ver este patrón: los métodos se definen en el prototipo, pero las propiedades de los datos se definen en el constructor. Esto se debe a que los métodos suelen ser los mismos para todos los objetos que creamos, mientras que a menudo queremos que cada objeto tenga su propio valor único para sus propiedades de datos (como en este caso, cada persona tiene un nombre diferente).

Las propiedades que se definen directamente en el objeto, como el nombre aquí, se denominan propiedades propias y puedes comprobar si una propiedad es una propiedad específica utilizando el método estático Object.hasOwn():

const irma = new Person("Irma");
console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // false

Nota: También puede utilizar el método no estático Object.hasOwnProperty() aquí, pero recomendamos utilizar Object.hasOwn() si es posible.

Prototipos y herencia

Los prototipos son una característica potente y altamente flexible de JavaScript que permite la reutilización de código y la composición de objetos.

Admiten específicamente una versión de la herencia. La herencia es una característica de los lenguajes de programación orientados a objetos que permite a los programadores expresar la idea de que algunos objetos de un sistema son versiones más especializadas de otros.

Por ejemplo, si modelamos una escuela, podríamos tener profesores y alumnos: ambos son personas, por lo que comparten algunas propiedades (p. ej., ambos tienen nombre), pero cada uno puede añadir propiedades adicionales (p. ej., los profesores tienen una asignatura que imparten) o pueden implementar la misma propiedad de diferentes maneras. En un sistema POO, podríamos decir que tanto profesores como alumnos heredan de Personas.

Puedes ver cómo en JavaScript, si los objetos Profesor y Estudiante pueden tener prototipos Persona, pueden heredar propiedades comunes, mientras agregan y redefinen propiedades que necesitan ser diferentes.

En el próximo artículo, analizaremos la herencia junto con otras características fundamentales de los lenguajes de programación orientados a objetos y veremos cómo JavaScript las admite.

Resultado

Este artículo cubre los prototipos de objetos de JavaScript, incluido cómo las cadenas de prototipos de objetos permiten que los objetos hereden propiedades entre sí, la propiedad de prototipo y cómo usarla para agregar métodos a los constructores y otros temas relacionados.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

También te puede gustar