Juan David Sánchez
Andrés Felipe Eslava Zuluaga
Muy buen dato! gracias!
Franks Jhon Colque Zegarra
Buena info!
¿Cómo implementar la herencia en JavaScript?
La herencia es un concepto clave en la programación orientada a objetos que permite que una clase derive de otra, compartiendo sus propiedades y métodos. En JavaScript, podemos implementar la herencia utilizando la palabra clave extends en combinación con super, permitiendo que subclases hereden características de clases madre.
¿Qué es la clase base y cómo se define?
En JavaScript, una clase base actúa como la estructura troncal a partir de la cual otras clases pueden derivar. Podemos crear una clase estudiante genérica que contenga atributos comunes a todos los estudiantes, sin importar el tipo de suscripción. Esto permite la reutilización del código.
class Student { constructor(name, email, approveCourses) { this.name = name; this.email = email; this.approveCourses = []; } // métodos comunes para todos los estudiantes }
¿Cómo crear subclases y extender la clase madre?
Para que una subclase herede las propiedades de la clase madre, utilizamos la palabra clave extends seguida del nombre de la clase madre. Además, es necesario reinstanciar el constructor utilizando super, que permite llamar al constructor de la clase madre.
class FreeStudent extends Student { constructor(...props) { super(...props); } // métodos exclusivos para estudiantes gratuitos } class BasicStudent extends Student { constructor(...props) { super(...props); } // métodos exclusivos para estudiantes básicos }
¿Cómo validar propiedades específicas en subclases?
Cada subclase puede tener métodos específicos con validaciones personalizadas. Por ejemplo, podemos crear un método approveCourse que verifique si un curso es gratuito antes de que un estudiante de tipo gratuito lo agregue a su lista de cursos aprobados.
approveCourse(newCourse) { if (newCourse.isFree) { this.approveCourses.push(newCourse); } else { console.warn(`Lo sentimos ${this.name}, solo puedes tomar cursos gratuitos.`); } }
Para un estudiante de tipo básico, podemos controlar que no apruebe cursos en inglés:
approveCourse(newCourse) { if (newCourse.lang !== 'English') { this.approveCourses.push(newCourse); } else { console.warn(`Lo sentimos ${this.name}, no puedes tomar cursos en inglés.`); } }
¿Cómo inicializar objetos específicos con sus respectivas subclases?
Para asignar estudiantes a una clase concreta, simplemente creamos instancias de las respectivas subclases. Por ejemplo:
const juan = new FreeStudent('Juan', 'juan@example.com'); const miguelito = new BasicStudent('Miguelito', 'miguelito@example.com');
En cada caso, podemos invocar métodos específicos dependiendo del tipo de estudiante.
¿Cómo añadir propiedades y probar su funcionalidad?
Implementar características adicionales como propiedades para los cursos aumenta la complejidad y flexibilidad del sistema. Agregar una propiedad opcional como isFree permite realizar decisiones condicionadas en la lógica de negocio.
class Course { constructor(name, isFree = false, lang = 'Spanish') { this.name = name; this.isFree = isFree; this.lang = lang; } } const basicCourse = new Course('Curso de Programación Básica', true);
Finalmente, es importante probar la funcionalidad en un entorno adecuado para asegurarse de que las validaciones funcionan correctamente. Cambiar las instancias y comprobar que se comportan como se espera garantiza que el sistema cumpla sus objetivos.
Implementar herencia en JavaScript mediante clases y subclases ofrece un potente mecanismo para construir aplicaciones modulares y escalables. Esta práctica anima a los desarrolladores a explorar otras técnicas y paradigmas de programación orientada a objetos, enriqueciendo su competencia técnica y profesional.
Juan David Sánchez
Andrés Felipe Eslava Zuluaga
Muy buen dato! gracias!
Franks Jhon Colque Zegarra
Buena info!
Super La palabra clave super es usada para acceder y llamar funciones del padre de un objeto.
Las expresiones super.prop y super[expr] son válidas en cualquier definición de método tanto para clases como para objetos literales (en-US).
Sintaxis // llama al método constructor del objeto padre. super([arguments]);
// llama cualquier otro método del objeto padre. super.functionOnParent([arguments]); Descripción Cuando es usado en un constructor, la palabra clave super aparece sola lo cual invoca el constructor del objeto padre. En este caso debe usarse antes de que la palabra clave this sea usada. La palabra clave super también puede utilizarse para llamar otras funciones del objeto padre. ** Ejemplo** Usando super en clases Este fragmento de código se toma del ejemplo de clases (demo en vivo). Aquí se llama a super() para evitar la duplicación de las partes del constructor que son comunes entre Rectangle y Square.
class Rectangle { constructor(height, width) { this.name = 'Rectangle'; this.height = height; this.width = width; } sayName() { console.log('Hi, I am a ', this.name + '.'); } get area() { return this.height * this.width; } set area(value) { this.height = this.width = Math.sqrt(value); } } class Square extends Rectangle { constructor(length) { this.height; // ReferenceError, super necesita ser llamado primero! // Aquí, llama al constructor de la clase padre con las longitudes // previstas para el ancho y la altura de Rectangle super(length, length); // Nota: En las clases derivadas, se debe llamar a super() antes de // poder usar 'this'. Salir de esto provocará un error de referencia. this.name = 'Square'; } }
Christian Alvarenga
Recuerdo haber visto en el curso de Fundamentos de JavaScript con el profesor Sacha como se creaba la herencia antes del ECMAScript 6 y era un dolor de cabeza. 🥴
Por si quieran ver, les dejo el link.
P.D.: No sé como dormían los desarrolladores de software antes del ECMAScript 6 (con razón nació JQuery en ese entonces).
Luciano Cavallo
Hola Christian, muy bueno el aporte. Sabes de otros cursos de Sacha?? Porque si buscas por Sacha Lyfszich en el buscador de Platzi, solo devuelve un video aislado, pero con tu enlace entre al curso entero. Gracias!
Christian Alvarenga
Saludos Luciano, creo que Sacha tiene solamente dos cursos dentro de Platzi, te dejo los enlaces.
Miguel Angel Reyes Moreno
Sebastian Moreno Olivera
Esta súper su ejemplo, gracias 😁😁
Segundo Juan
y como haces para que lo corra el navegador?
Yuliani Paoleth Alcántara Galindo
Solución de herencia con la sintaxis de prototipos:
Solo me queda duda de como pasarle un parametro directamente a la instancia de una función prototipo hija. Por ejemplo ahí en mi instancia de Perro, ¿Cómo le paso el parametro de "nombre" ademas de la "raza"?. De igual manera se puede hacer accediendo a los métodos del padre.
Josemaria Ponce Cajavilca
Uncaught TypeError: juanito.approvedCousess is not a function
tengan cuidado con el nombre del atributo y del metodo approvedCouses, tuve que cambiar el nombre del método para que pueda funcionar bien al momento de usar el comando
juan.approvedCousess(cursoProgBasica);
Cesar Gabriel Mejias Farreras
Lo mismo me sucedió casualmente
Pablo Daniel Amé
me pasa lo mismo y no lo puedo resolver
Jesús Medina
Amo a mi querido profesor Diego. Me ha enseñado tanto <3
Facundo Esteban Gasco
Les dejo un reto, hacer que los usuarios no puedan approbar dos veces el mismo curso!
Juan Castro
ProfesorLove it
Orlando Rodriguez
Bramucci Candela
Bramucci Candela
Ricardo Alfonso Chavez Vilcapoma
Gracias amigo...me sirvió mchísimo
Christian Alvarenga
En el constructor de las clases hijas se les pasa un solo atributo (props) ya que en la clase principal se usó un solo objeto para agrupar todas los atributos, no? En caso de no agrupar todos los atributos dentro de un objeto si habría que pasar cada uno de ellos o los que queramos que las clases hijas hereden, no?
Carlos Eduardo Gomez García
¡Hola! Sí, tendrías que pasarle cada uno de ellos. Si quieres pasar únicamente los que quieres que la clase hija herede tendrías que sobreescribir tu constructor en la clase hija 🤔
Christian Alvarenga
Si tengo por ejemplo el siguiente constructor:
constructor({ nombre, edad, pais })
Y quiero que mis clases hijas hereden algunos atributos, debería hacer de la siguiente manera?
constructor(sexo) { super(prop.nombre, prop.edad) this.sexo = sexo; }
O cómo lo sobrescribiría?
Obed Paz
Inheritance: Classes can also inherit from other classes. The class being inherited from is called the parent, and the class inheriting from the parent is called the child. Let's see an example, let’s say Administrator, can inherit the properties and methods of a User class: .
Jose Flores
Jerremi Aron Chancan Labajos
WILSON TOVAR
uff super aporte, estuve batallando y casi que no entiendo... mil gracias!!!!!!
Manuel Araujo
Juan, tengo una duda: Recuerdo que en el curso del primer videojuego, cuando querías que un archivo .js accediera a información de otro, simplemente colocabas en el HTML dos etiquetas script: una para el archivo que iba a "exportar" la información y debajo de esta la otra etiqueta script que iba a "importar" la información. Cuál es la diferencia entre esto y trabajar con módulos, y cuál práctica es recomendable en qué casos?
Juan Castro
ProfesorCuando tenemos poquitos archivos dejar una etiqueta script antes que la otra funciona super bien. Cuando empezamos a tener decenas o incluso cientos de archivos... puf... definitivamente necesitamos módulos para no enloquecer.
Manuel Araujo
Gracias amigo! entendido!
Vladimir Farrera Vera
tengo compañeros que son sordos y les gustaria aprender en Platzi pero no tiene subtitulos las clases yo digo que seria buena idea agregar esa opcion.
John Osorio
Por favor tenga en cuenta este comentario del compañero @teamplatzi
Sergio Antonio Ochoa Martinez
gilberto jose
Juan Castro
ProfesorPuedes poner lo que quieras. :D
Hector Maluy Fernandez
¿En este caso this hace referencia a la clase heredada o a la superclase?
Juan Castro
ProfesorA la clase donde estamos, la "heredada". Para acceder a la clase madre usamos super.
Sofía González Chavira
Estoy notando que al poner Juan en la consola de Firefox aparece asi
Mientras que en Chrome asi:
¿Por qué en forefox aparece solo como "Object" mientras que en Chrome con el nombre de su clase (freestudent)? Es el mismo archivo, mismo todo, solo diferente navegador
Juan Castro
ProfesorHmmm, qué interesante.
No creo que sea un error, sino que simplemente Firefox eligió no mostrar tan visualmente si un objeto es una instancia de otra clase.
Si quieres confirmarlo con código 100% tuyo, puedes usar el Instance Of: https://platzi.com/clases/2419-javascript-poo-intermedio/39821-instance-of-en-javascript-con-instancias-y-prototi/ :D
Para hacer la herencia usando la sintaxis de prototipos podemos hacer lo siguiente:
Suponiendo que ya tenemos creada nuestra superclase (Student). Vamos a crear una clase (FreeStudent) que va a pasar los parámetros de inicialización al constructor de la superclase, para esto hacemos uso de la función call().
function FreeStudent(props) { Student.call(this, props); }
Le pasamos como primer atributo el contexto de ejecución de nuestra nueva "clase" y como segundo parámetro los props, que son estas propiedades que recibiremos de inicialización. Después de esto, clonamos el prototipo de nuestra superclase en el prototipo de nuestra subclase:
FreeStudent.prototype = Object.create(Student.prototype);
Por último, le agregamos cualquier función extra que deseemos agregar a la subclase:
FreeStudent.prototype.approveCourse = function (newCourse) { if (newCourse.isFree) { this.approvedCourses.push(newCourse); } else { console.warn(`Lo sentimos, ${this.name}, sólo puedes tomar cursos gratis`); } }
Y eso es todo! Fuente: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
Tengo todo separado en distintos archivos: student.js:
export default class Student { constructor({ id, name, email, username, points = 0, twitter = undefined, instagram = undefined, facebook = undefined, approvedCourses = [], learningPaths = [], }) { this._id = id; this._name = name; this._email = email; this._username = username; this._points = points; this._approvedCourses = approvedCourses; this._learningPaths = learningPaths; this._socialMedia = { twitter, instagram, facebook, }; } get id () { return this._id; } set id (newID) { this._id = newID; } get name () { return this._name; } set name (newName) { this._name = newName; } get email () { return this._email; } set email (newEmail) { this._email = newEmail; } get points () { return this._points; } set points (newPoints) { this._points = newPoints; } get username () { return this._username; } set username (newUsername) { this._username = newUsername; } get approvedCourses () { return this._approvedCourses; } set approvedCourses (newApprovedCourses) { this._approvedCourses = newApprovedCourses; } get learningPaths () { return this._learningPaths; } set learningPaths (newLearningPaths) { this._learningPaths = newLearningPaths; } /*get _socialMedia () { return this._socialMedia; } set _socialMedia (newSocialMedia) { this._socialMedia = newSocialMedia; }*/ };
freeStudent.js:
import Student from './student.js' export default class FreeStudent extends Student { constructor(props) { super(props) //*Llama al constructor de la clase madre } approveCourse(newCourse) { if (newCourse.isFree){ this.approvedCourses.push(newCourse) } else { console.warn(`Lo sentimos ${this.name}, sólo puedes tomar cursos gratis`) } } }
basicStudent.js:
import Student from './student.js' export default class BasicStudent extends Student { constructor(props) { super(props) //*Llama al constructor de la clase madre } approveCourse(newCourse) { if (newCourse.lang !== "English"){ this.approvedCourses.push(newCourse) } else { console.warn(`Lo sentimos ${this.name}, no puedes tomar cursos en inglés`) } } }
expertStudent.js
import Student from './student.js' export default class ExpertStudent extends Student { constructor(props) { super(props) //*Llama al constructor de la clase madre } approveCourse(newCourse) { this.approvedCourses.push(newCourse) } }
main.js:
import FreeStudent from './freeStudent.js' import BasicStudent from './basicStudent.js' import ExpertStudent from './expertStudent.js' //* Estudiantes --------------------- const miguel = new ExpertStudent({ id: 1, name: 'Miguel', email: 'miguel@gmail.com', username: 'mike', points: 40000, approvedCourses: [cursoProgramacionBasica], learningPaths: [escuelaDesarrolloWeb] }); console.log(miguel); const juan = new FreeStudent({ id: 2, name: 'Juan', email: 'juan@gmail.com', username: 'juanDC', points: 100000, }) console.log(juan) juan.approveCourse(cursoProgramacionBasica) juan.approveCourse(cursoIntroMarketingDigital) console.log(juan.approvedCourses) const daniel = new BasicStudent({ id: 3, name: 'Daniel', }) console.log(daniel) daniel.approveCourse(cursoProgramacionBasica) daniel.approveCourse(cursoIntroMarketingDigital) console.log(daniel.approvedCourses)
Se me ocurrió usar el metodo .some para recorrer el array que contiene los cursos aprobados, si lo encuentra, entonces que me lance una advertencia y no continue con el metodo que agrega cursos a mi clase
class FreeStudent extends Student { constructor(props) { super(props); } approveCourse(newCourse){ // Recorremos el array de cursos aprobados y si en alguna iteracion coincide el nombre de un curso con el nuevo que queremos aprobar, cursoVisto = true const cursoVisto = this.approvedCourses.some((curso) => { return curso.name === newCourse.name; }) // usamos un condicional donde si cursoVisto = true y nos de una advertencia, si no que continue y agregue el curso if (cursoVisto){ console.warn('Este curso ya estaba aprobado') } else { if(newCourse.isFree){ this.approvedCourses.push(newCourse); } else { console.warn("Lo siento " + this.name + ", no puedes tomar este curso.") } } } }
Como hago para añadir más atributos a una sub clase? me explico
class X{ constructor({one, two}){ this.one = one this.two = two } } class Y extends X{ constructor(props, three){ super(props) this.three = three } } console.log(new Y({one: 1, two: 2}, 3))
Esto funciona, pero 'three' ya no es mandado como objeto, otra forma es:
class X{ constructor({one, two}){ this.one = one this.two = two } } class Y extends X{ constructor(props, {three}){ super(props) this.three = three } } console.log(new Y({one: 1, two: 2}, {three: 3}))
pero sigue siendo incómodo, no hay alguna forma de poder usar lo siguente?
console.log(new Y({one: 1, two: 2, three: 3}))
Sintaxis con funciones y prototipos
. Leí varios comentarios y busqué fuera, es curioso que existan múltiples formas, me pregunto a qué se debe, en mi resultado también aplique que el constructor recibiera un objeto, y no parámetros individuales.
function Student({ name, age, cursosAprobados = [] }) { this._name = name; this.age = age; this.cursosAprobados = cursosAprobados; } Student.prototype.aprobarCurso = function(nuevoCurso) { this.cursosAprobados.push(nuevoCurso); } Student.prototype = { get name() { return this._name; }, set name(newName) { this._name = newName } }; function FreeStudent(props){ Student.call(this, props); this.tipo = props.tipo; } FreeStudent.prototype = Object.create(Student.prototype); FreeStudent.prototype.aprobarCurso = function(nuevoCurso) { if(nuevoCurso.IsFree) { this.cursosAprobados.push(nuevoCurso); } else { console.warn(`Lo sentimos ${this.name} no puede aprobar el ${nuevoCurso.name}, porque no es gratis`); } } const pancho = new FreeStudent({ name: "pancho", age: 18, tipo: "gratis" }); const cursoDataViz = { name: "Curso de Data Visualization", IsFree: false } console.log(pancho); // output // { _name: 'pancho', age: 18, cursosAprobados: [], tipo: 'gratis' } pancho.aprobarCurso(cursoDataViz); // output // Lo sentimos pancho no puede aprobar el Curso de Data Visualization, porque no es gratis
Si te salio el error
Uncaught TypeError: juanito.approvedCousess is not a function
Es porque tenia el mismo nombre de un atributo de mi** clase student **y no me lo tomaba como un método sino me lo tomaba como atributo
como puedes ver el atributo de mi clase student se llama igual que mi metodo de mi clase FreeStudent
class Student { constructor({ approvedCourses = []; }){
Y al heredar todos los atributos de la superclase que es Student pues me marca que no es una funcion en la consola y un funcion es igual a un metodo o funcion igual a el
class FreeStudent extends Student { constructor(props) { super(props); } ** approvedCourses**(newCourse) { if (newCourse.isFree) { this.approvedCourses.push(newCourse); } else { console.log(`Lo siento, ${this.name} solo puedes tomar cursos abiertos`); } }; }
Herencia con prototipos
function extendFrom(prototypeChild, prototypeParent) { var fn = function () { }; // funcion anonima fn.prototype = prototypeParent.prototype; prototypeChild.prototype = new fn; prototypeChild.prototype.constructor = prototypeChild; } function Person(firstName, lastName, height) { this.firstName = firstName; this.lastName = lastName; this.height = height; } Person.prototype.greet = function () { console.log(`Hola, me llamo ${this.firstName} ${this.lastName}`); } Person.prototype.imTall = function () { return this.height > 1.80; } function Developer(firstName, lastName, height) { this.firstName = firstName; this.lastName = lastName; this.height = height; } // Funcion de herencia extendFrom(Developer, Person); Developer.prototype.greet = function () { console.log(`Hola, me llamo ${this.firstName} ${this.lastName} y soy un Desarrollador`); } // En esta arrow function el this es Window Person.prototype.imTall = () => this.height > 1.80; Person.prototype.imTall = function () { return this.height > 1.80; } let sergio = new Person('Sergio Antonio', 'Ochoa Martinez', 1.175); sergio.greet(); console.log(sergio.imTall()); let rocio = new Person('Rocio', 'Paredes', 1.65); rocio.greet(); console.log(rocio.imTall()); let angel = new Person('Angel', 'Martinez', 1.92); angel.greet(); console.log(angel.imTall()); let devSergio = new Developer('Sergio', 'Ochoa', 1.75); devSergio.greet(); console.log(devSergio.imTall());