Estoy buscando aprender sobre la herencia de clases y cómo se puede emular sin usar la palabra clave class . Es solo un ejercicio académico/de aprendizaje.
¿Funciona el siguiente método para hacer la herencia de clase, o me faltan algunos conceptos básicos?
function Person(name, age) { let person = Object.create(Person.prototype); person.name = name; person.age = age; return person; } Person.prototype.get_birth_year = function() { console.log(new Date().getFullYear() - this.age); } function Kid(name, age, dad, mom) { let kid = Object.create(Person.prototype); kid.dad = dad; kid.mom = mom; for (let key in Kid.prototype) { kid[key] = Kid.prototype[key]; } return kid; } Kid.prototype.callMom = function() { console.log(`MOM!!! (${this.mom})`); } function KidToy(name, age, dad, mom, toyname) { let toy = Object.create(Kid.prototype); toy.toyname = toyname; for (let key in KidToy.prototype) { toy[key] = KidToy.prototype[key]; } return toy; } KidToy.prototype.findToy = function() { console.log('Looking to find the', this.toyname); } let p = Person('Bob', 33); p.get_birth_year(); let k = Kid('Billy', 12, 'Bob', 'Margot'); k.callMom(); let t = KidToy('Billy', 12, 'Bob', 'Margot', 'Potatohead'); t.findToy();Las únicas cosas que estoy haciendo aquí para subclasificar son:
¿O me estoy perdiendo algunas cosas?
No exactamente. Hay tres enfoques para la herencia de prototipos en JS:
new con una función que actúa como constructor y asignar métodos al .prototype de esa función para que sean heredados por las instancias creadas por él.Object.create para crear manualmente objetos que heredan de otros.class , constructor y super para escribir código de estilo clásico en JS. Las clases JS solo se pueden instanciar con la new palabra clave y admiten algunas características únicas, aunque aún usan la herencia prototípica debajo. Cuando llama a una función con la new palabra clave, automáticamente crea un objeto que hereda del .prototype de la función y lo expone como this dentro del cuerpo de la función.
function Person(name, age) { this.name = name; this.age = age; } const john = new Person("John", 42); No necesita crear la instancia y devolverla, todo esto se hace con la new palabra clave. Los objetos como john arriba tendrán acceso a todas las propiedades almacenadas en Person.prototype que no están sombreadas por las propiedades asignadas al propio john .
Person.prototype es en sí mismo un objeto que se creó automáticamente cuando se definió la función Person . Hereda de Object y tiene una propiedad, constructor , que apunta a la función Person .
Algunas correcciones más sobre sus suposiciones:
let kid = Object.create(Person.prototype); Con este código, kid no hereda de Kid.prototype .
for (let key in Kid.prototype) { kid[key] = Kid.prototype[key]; } Este código copia todas las propiedades enumerables de Kid.prototype en kid en el momento de la creación. Esto significa que si alguna vez agrega propiedades a Kid.prototype más adelante, no estarán en kid . If kid actually inherited from Kid.prototype , methods could be added to Kid.prototype` incluso después de que se creó una instancia de kid, y esa instancia heredaría esos métodos.
En cambio, probablemente querías algo como esto:
function Kid(name, age, dad, mom) { // invoke super constructor Person.call(this, name, age); this.dad = dad; this.mom = mom; } // Kid extends Person Kid.prototype = Object.create(Person.prototype); Kid.prototype.constructor = Kid; Todas las instancias de niños creadas después de este código heredarán no del Kid.prototype predeterminado que a su vez hereda de Object , sino de nuestro Kid.prototype hecho a medida que hereda de Person.prototype .
Como puede ver, esto no es muy intuitivo ni práctico, razón por la cual se agregó la sintaxis de class al lenguaje.
Esperemos que esto te dé un empujón en la dirección correcta. Personalmente, rara vez uso la herencia en mi propio código y, en cambio, adopto un enfoque más FP con cierres y composición.