Protección de Propiedades Privadas en Prototipos JavaScript
Clase 18 de 20 • Curso Intermedio de Programación Orientada a Objetos en JavaScript
Resumen
A partir del último código generado, crearemos un getter y setter a nuestra propiedad learningPaths
dentro de nuestro prototipo Student
para evitar que sea manipulado después de la creación de un estudiante.
Getters y Setters desde Object.defineProperty
-
Generamos un objeto
private
en el que colocaremos el atributo,_learningPaths
el cual al principio será un array vacío. Previo a esto, borramos todo el código que viene después de la asignación de atributos en el objetoStudent
:function isObject(subject) { return typeof subject == "object"; } function isArray(subject) { return Array.isArray(subject); } function requiredParam(param) { throw new Error(param + " es obligatorio"); } function LearningPath({ // PROTOTIPO name = requiredParam("name"), // Campo es obligatorio courses = [], // Lista de Cursos que pertencen a la ruta de aprendizaje }) { this.name = name; this.courses = courses; } function Student({ // PROTOTIPO name = requiredParam("name"), email = requiredParam("email"), age, twitter, instagram, facebook, approvedCourses = [], learningPaths = [], } = {}) { // ASIGNACIÓN DE ATRIBUTOS this.name = name; this.email = email; this.age = age; this.approvedCourses = approvedCourses; this.socialMedia = { twitter, instagram, facebook, }; const private = { // 👈👈 "_learningPaths": [], }; }
-
Con
Object.defineProperty
añadiremos el getter y setter respectivo a la propiedadlearningPaths
. En el setter es donde validaremos si un nuevo learning path que deseamos añadir es instancia del prototipoLearningPath
:function isObject(subject) { return typeof subject == "object"; } function isArray(subject) { return Array.isArray(subject); } function requiredParam(param) { throw new Error(param + " es obligatorio"); } function LearningPath({ // PROTOTIPO name = requiredParam("name"), // Campo es obligatorio courses = [], // Lista de Cursos que pertencen a la ruta de aprendizaje }) { this.name = name; this.courses = courses; } function Student({ // PROTOTIPO name = requiredParam("name"), email = requiredParam("email"), age, twitter, instagram, facebook, approvedCourses = [], learningPaths = [], } = {}) { // ASIGNACIÓN DE ATRIBUTOS this.name = name; this.email = email; this.age = age; this.approvedCourses = approvedCourses; this.socialMedia = { twitter, instagram, facebook, }; const private = { "_learningPaths": [], }; // "this" referencia al prototipo "Student" Object.defineProperty(this, "learningPaths", { // 👈👈 get() { // GETTER return private["_learningPaths"]; }, set(newLp) { // SETTER if (newLp instanceof LearningPath) { // Si es que SÍ es una instancia, añadimos al array privado "_learningPaths" private["_learningPaths"].push(newLp); } else { // "LPs" hace referencia a Learning Paths console.warn("Alguno de los LPs NO es una instancia del prototipo LearningPath"); } }, }); }
-
Con un bucle
for in
vamos a recorrer cada una de las rutas de aprendizaje que queramos asignarle al nuevo estudiante para invocar al setter que generamos. Este setter validará al learning path de turno si es en realidad una instancia del prototipoLearningPath
:function isObject(subject) { return typeof subject == "object"; } function isArray(subject) { return Array.isArray(subject); } function requiredParam(param) { throw new Error(param + " es obligatorio"); } function LearningPath({ // PROTOTIPO name = requiredParam("name"), // Campo es obligatorio courses = [], // Lista de Cursos que pertencen a la ruta de aprendizaje }) { this.name = name; this.courses = courses; } function Student({ // PROTOTIPO name = requiredParam("name"), email = requiredParam("email"), age, twitter, instagram, facebook, approvedCourses = [], learningPaths = [], } = {}) { // ASIGNACIÓN DE ATRIBUTOS this.name = name; this.email = email; this.age = age; this.approvedCourses = approvedCourses; this.socialMedia = { twitter, instagram, facebook, }; const private = { "_learningPaths": [], }; // "this" referencia al prototipo "Student" Object.defineProperty(this, "learningPaths", { get() { // GETTER return private["_learningPaths"]; }, set(newLp) { // SETTER if (newLp instanceof LearningPath) { // Si es que SÍ es una instancia, añadimos al array privado "_learningPaths" private["_learningPaths"].push(newLp); } else { // "LPs" hace referencia a Learning Paths console.warn("Alguno de los LPs que quieres añadir NO es una instancia del prototipo LearningPath"); } }, }); for (learningPathIndex in learningPaths) { // 👈👈 // Al querer hacer una asignación, estamos invocando al setter de la // propiedad "learningPaths". Entonces, la ruta de aprendizaje ubicado // en el índice actual será validado por el setter para saber si es o no // instancia del prototipo LearningPath: this.learningPaths = learningPaths[learningPathIndex]; } }
¡Listo! Nuestro atributo learningPaths
quedó protegido. Intentemos crear un estudiante con sus respectivos learning paths. Luego intentemos añadir una ruta adicional que sea instancia del prototipo LearningPath
y otra que no lo sea:
const escuelaWeb = new LearningPath({ name: "Escuela de WebDev" });
const escuelaData = new LearningPath({ name: "Escuela de Data Science" });
const juan = new Student({
email: "juanito@frijoles.co",
name: "Juanito",
learningPaths: [
escuelaWeb,
escuelaData,
],
});
console.log(juan.learningPaths); // ANTES
const escuelaMarketing = new LearningPath({ name: "Escuela de Marketing" });
juan.learningPaths = { name: "Escuela Impostora" }; // 👈👀
juan.learningPaths = escuelaMarketing;
console.log(juan.learningPaths); // DESPUES
/* > Mensaje en consola
[
LearningPath { name: 'Escuela de WebDev', courses: [] },
LearningPath { name: 'Escuela de Data Science', courses: [] }
]
Alguno de los LPs NO es una instancia del prototipo LearningPath 👈👀
[
LearningPath { name: 'Escuela de WebDev', courses: [] },
LearningPath { name: 'Escuela de Data Science', courses: [] },
LearningPath { name: 'Escuela de Marketing', courses: [] }
]
*/
Finalmente, veamos cómo crear métodos estáticos en JavaScript. 👨💻🚀
Contribución creada por: Martín Álvarez (Platzi Contributor)