Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Atributos y métodos privados en prototipos

17/19
Recursos

Aportes 23

Preguntas 5

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Para asignar los métodos y atributos privados en Clases (ahora en ES21) seran con un #

El propio JavaScript aplica la encapsulación de privacidad de estas características de clase.

class ClassWithPrivateField {
  #privateField;
}

Ahora vamos a ver un Ejemplo

Metodos Privados

  1. Vamos a crear una clase llamada People y vamos a tener varios metodos.
class People {
  showName() {
    console.log("My name is David")
  }
  showAge() {
    console.log("David is 21")
  }
}

Para acceder a los metodos dentro de las clases, primero necesitamos instanciar la clase.

class People {
  showName() {
    console.log("My name is David")
  }
  showAge() {
    console.log("David is 21")
  }
}

const people = new People()

people.showName()
people.showAge()

Podemos ver My name is David y David is 21 en la consola.
Si queremos hacer showAge(), un método privado dentro de la clase People, por lo que fuera del alcance de la clase no es accesible.
Simplemente agregamos # a showAge() algo asi #showAge()

class People {
  showName() {
    console.log("My name is David")
  }
  #showAge() {
    console.log("David is 21")
  }
}

const people = new People()

people.showName()
people.showAge() 

Podemos ver el resultado en nuestra consola. Un error es decir people.showAge que no es una función. Esto se debe a #showAge() que ahora es un método privado dentro de la clase People y solo se puede acceder a través de un método público dentro de la clase People.

Después de ver esta clase, creo que ya entiendo porqué la gente dice que JS es un lenguaje extraño 😅

Llegados a este punto del curso creo que fue mala idea trabajarlo con Typescript :’'v
Este clase ha sido muy enriquecedora con JS.

Por cierto, el reto queda algo así, mucho mas bonito con la sintaxis de class:

.

El verdadero reto de esta clase fue entender la clase jeje.
.
Hice un diagrama de flujo que realmente no tiene nada de diagrama de flujo pero asi yo le pude entender un poquito mejor, espero les ayude a pasar este proceso de “entender js”, una vez pasando el proceso de negación, la cosa se vuelve mas sencilla, keep going!!
.

Como curiosidad aun se le puede hacer un push

para evitarlo agrege esto:

  Object.defineProperty(this.learningPaths, "push", {
        configurable: false,
    })

Con la sintaxis de clases use el prefijo # para indicar que una propiedad es privada.

class Student {
    #learningPaths = []
    constructor ({
        name = requiredParam("name"),
        email = requiredParam("email"),
        age,
        twitter,
        facebook,
        approvedCourses = [],
        learningPaths = [],
    } = {})  {
        name,
        email,
        age,
        twitter,
        facebook,
        approvedCourses

        for (let index in learningPaths) {
            if(learningPaths[index] instanceof LearningPath){
                this.#learningPaths.push(learningPaths[index])
            }
        }
    }
    set learningPaths (newLp){
        if(newLp instanceof LearningPath) {
            this.#learningPaths.push(newLp)
        } else {
            console.warn("Alguno de los LPs no es una instancia de la clase Learningpath")
        }
    }
    get learningPaths () {
        return this.#learningPaths
    }
    #metodoPrivado() {
        console.log("uwu")
    }
    metodoPublico() {
        this.#metodoPrivado()
    }
}

El for ... in que algunos reemplazamos con un for ... of, también se puede reemplazar con un forEarch:

function isObject(subject) {
	return typeof subject == "object";
}

function isArray(subject) {
	return Array.isArray(subject);
}

function requiredParam(param) {
	throw new Error(param + " es un parametro obligatorio.");
}

class LearningPath {
	constructor({ name = requiredParam("name"), courses = [] }) {
		this.name = name;
		this.courses = courses;
	}
}

class Student {
	constructor({
		name = requiredParam("name"),
		email = requiredParam("email"),
		age,
		twitter,
		instagram,
		facebook,
		approvedCourses = [],
		learningPaths = [],
	}) {
		this.name = name;
		this.email = email;
		this.age = age;
		this.approvedCourses = approvedCourses;
		this._learningPaths = this.setterCall(learningPaths);
		this.socialMedia = {
			twitter,
			instagram,
			facebook,
		};
	}

	get learningPaths() {
		return this._learningPaths;
	}

	set learningPaths(newLearningPath) {
		if (newLearningPath instanceof LearningPath) {
			this._learningPaths.push(newLearningPath);
		} else {
			console.warn("Alguno de los LPs no es una instancia de los prototipos LearningPath");
		}
	}

	setterCall(learningPaths) {
		this._learningPaths = [];
		learningPaths.forEach((learningPath) => {
			this.learningPaths = learningPath;
		});
		return this._learningPaths;
	}
}

const escuelaWeb = new LearningPath({ name: "Escuela de WebDev" });
const escuelaData = new LearningPath({ name: "Escueal de Data Science" });
const brandon = new Student({
	email: "[email protected]",
	name: "Brandon",
	learningPaths: [escuelaWeb, escuelaData, { name: "Escuela del Impostor" }],
});

Aquí mi solución usando la sintaxis de Clases y RORO! Ayúdame revisando mi código y diciéndome qué harías diferente =D

class Student {
  constructor({
    name = requiredParam("name"),
    email = requiredParam("email"),
    age,
    twitter,
    instagram,
    facebook,
    approvedCourses = [],
  }) {
    this.name = name;
    this.email = email;
    this.age = age;
    this.approvedCourses = approvedCourses;
    this.socialMedia = {
      twitter,
      instagram,
      facebook,
    };
    
    const privado = {
      "_learningPaths": [],
    };

    Object.defineProperty(this, "learningPaths", {
      get() {
        return privado["_learningPaths"];
      },
      set(newLp) {
        if(newLp instanceof LearningPath) {
          privado["_learningPaths"].push(newLp);
        } else {
          console.warn("Alguno de los LPs no es una instancia del prototipo LearningPath");
        }
      },
    })
  }
}

Tuve problemas al declarar la constante ‘private’ dentro del constructor! Al parecer js aunque no suporte la palabra private ya la identifica como palabra reservada!

< | | >
Hola comunidad, les dejo mi aporte relacionado con el reto.
Espero sea de utilidad.


function isObject(subject){
    return typeof subject == "object";
}

function isArray(subject){
    return Array.isArray(subject);
}

function requiredParam(param){
    throw new Error(param + " es un parametro obligatorio.");
}

class LearningPath{
    constructor({
        name = requiredParam("name"),
        courses = [],
    }){
        this.name = name;
        this.courses = courses;
    }
}

class Student {
    constructor({
        name = requiredParam("name"), 
        email = requiredParam("email"),
        age,
        twitter,
        instagram, 
        facebook, 
        approvedCourses = [], 
        learningPaths = [],
    }){
    this.name = name;
    this.email = email;
    this.age = age;
    this.approvedCourses = approvedCourses;
    this._learningPaths = this.setterCall(learningPaths);
    this.socialMedia = {
        twitter,
        instagram,
        facebook,
    };
    }

    get learningPaths(){
        return this._learningPaths;
    }

    set learningPaths(newLP){
        if(newLP instanceof LearningPath){
            this._learningPaths.push(newLP);
        }
        else {
                        console.warn("Alguno de los LPs no es una instancia de los prototipos LearningPath");
                    }
    }

    setterCall(learningPaths){
        this._learningPaths = [];
        for(let index in learningPaths){
                this.learningPaths = learningPaths[index];
            }
        return this._learningPaths;
    }
}

const escuelaWeb = new LearningPath({name: "Escuela de WebDev"});
const escuelaData = new LearningPath({name: "Escueal de Data Science"});
const juan = new Student({ 
    name: "Juanito",
    email: "[email protected]", 
    learningPaths: [
        escuelaWeb,
        escuelaData,
        {
            name: "Escuela impostora",
            courses: [],
        },
    ],
});

Y NOS PODEMOS AHORRAR 10 MINUTOS DE NUESTRAS VIDAS SI VEMOS EL VIDEO DESDE EL MINUTO 12 39

En este codigo use dos tecnicas para implementar variables privadas. los dos atributos privados son “#breed” y “_private”

para #breed use sintaxis de ES8 que es que cuando se declara una variable con # ya se interpreta como privada, esta no estara disponible como un atributo, sus set y get.

para _private hice una copia de lo que hizo el profesor, el detalle es donde se declara, si se declara afuera del constructor, se marcara como un error de sintaxis, si se declara a dentro es el equivalente en sintaxis de clase para lo que se hizo en el video

y para ambos metodos tuve que usar Object.defineProperty()
para blindar los setters y getters

class Dog {
  #breed;

  constructor({ name, breed, age }) {
    let _private = "I am private";
    this.name = name;
    this.#breed = breed;
    this.age = age;
    this.setMisterius = (newMisterius) => {
      _private = newMisterius;
    };
    this.getMisterius = () => {
      return _private;
    };
  }
  setBreed(newBreed) {
    this.#breed = newBreed;
  }
  getBreed() {
    return this.#breed;
  }
}

let puppy = new Dog({ name: "Brownie", breed: "Pug", age: 5 });

Object.defineProperty(brownie, "getMisterius", {
  writable: false,
  configurable: false,
});

En el minuto 11:04, en vez de eliminar la comprobación de que si learningPaths es un array (que definitivamente debería ser eliminada, ya que siempre va a ser cierta porque el valor por defecto es un array), debería remplazarse por la siguiente comprobación:

si learningPaths no esta vacío, pasa al for loop

learningPaths 😵

que clase tan enredada, repites mucho el nombre de la propiedad y se vuelve molesto 😦

Si terminaste la clase ultra confundido porque dijo LearninnPaths hasta el cansancio, te explico la solución de forma limpia usando metodos de arrays.

Escondemos la propiedad

const private = {
        _learningPaths: []
    }

Modificamos el metodo .map con el que validabamos y empujabamos cada elemento que viniera dentro learningPaths, para que ahora haga uso de un setter que se utilice al llamar a la propiedad.

if( !isArray(learningPaths) ){
        throw new Error('LP no es una lista valida')
    } else{
        learningPaths.map(elm => {
            this.learningPaths = elm
        })
    }

Por último, declaramos una propiedad learningPath con el getter y setter para acceder a la propieda oculta, y dentro del setter hacemos la validación (que antes haciamos dentro del .map) para saber si es instancia del prototipo LearningPath

Object.defineProperty(this, "learningPaths", {
        get(){
            return private._learningPaths
        },
        set(newLp){
            // debugger;
            if(newLp instanceof LearningPath){
                private._learningPaths.push(newLp)
            } else{
                console.warn(`${newLp} no es una LP valida`)
            }
        }
    })

Y listo, sin enredos, sin un monton de scroll, sin jergas adolescentes innecesarias, simplemente claro y consiso.
El codigo completo queda así

const private = {
        _learningPaths: []
    }

    Object.defineProperty(this, "learningPaths", {
        get(){
            return private._learningPaths
        },
        set(newLp){
            // debugger;
            if(newLp instanceof LearningPath){
                private._learningPaths.push(newLp)
            } else{
                console.warn(`${newLp} no es una LP valida`)
            }
        }
    })

    if( !isArray(learningPaths) ){
        throw new Error('LP no es una lista valida')
    } else{
        learningPaths.map(elm => {
            this.learningPaths = elm
        })
    }

Saludos

la verdad esta un poco enrredada la clase pero creo que se debe mas que nada a haber utilizado siempre el mismo nombre de la variable learningPaths.
osea le pueden llamar
learningPathsParam,
_learningPaths
learningPaths
ya con esos tres nombre esta mas facil ubicar de cual estamos hablando.

Una implementación en las clases con algúnis métodos modernos ;D

class Student {
  #learningPaths = [];

  constructor({
    name = haveParam('name'),
    email = haveParam('email'),
    age,
    twitter,
    instagram,
    facebook,
    approvedCourses = [],
    learningPaths = [],
  } = {}) {
    this.name = name;
    this.email = email;
    this.age = age;
    this.approvedCourses = approvedCourses;
    this.socialMedia = { twitter, instagram, facebook };
    learningPaths.forEach( path => this.learningPaths = path )
  }

  get learningPaths() { return this.#learningPaths }

  set learningPaths(newLp) {
    if (newLp instanceof LearningPath) this.#learningPaths.push(newLp);
  }
}

Reto
El ejercicio realizado en clases en sintaxis de clase:

class LearningPath {
  constructor({ nombre, courses }) {
    this.nombre = nombre;
    this.courses = courses;
  }
}

class Student {
  constructor({
    nombre,
    puntos = 0,
    userName,
    email,
    twitter = null,
    facebook = null,
    instagram = null,
    aprovedCourses = [],
    learningPaths = [],
  }) {
    this.nombre = nombre;
    this.puntos = puntos;
    this.userName = userName;
    this.email = email;
    this.socialMedia = {
      twitter,
      facebook,
      instagram,
    };
    this.aprovedCourses = aprovedCourses;
    const privado = {
      _learningPaths: [],
    };
    Object.defineProperty(this, "learningPath", {
      get() {
        return privado["_learningPaths"];
      },
      set(newLearningPath) {
        if (newLearningPath instanceof LearningPath) {
          privado["_learningPaths"].push(newLearningPath);
        } else {
          console.warn(
            "Alguno de los learningPaths no es un verdadero learningPath"
          );
        }
      },
    });
    if (Array.isArray(learningPaths)) {
      for (let learningPath of learningPaths) {
        this.learningPath = learningPath;
      }
    }
  }
}

const nuevaRuta = new LearningPath({
  nombre: "Desarrollo web",
  courses: ["Curso de programacion JavaScript", "Curso de CSS3 Avanzado"],
});

const newStudent = new Student({
  nombre: "Eliezer",
  learningPaths: [
    nuevaRuta,
    {
      nombre: "Ruta falsa",
      courses: ["Curso falso"],
    },
  ],
});

Nunca crei que JS fuese tan jodido, habia escuchado de sus ‘cosas raras’ pero viendolo bien, raro es poco :v

Mi solución al reto! Publico solo la clase LearningPath al ser la más corta, la clase Student es igual.

const requiredParam = param => {
    throw new Error(`${param} is required`)
}

class LearningPath {
    #name
    #courses
    constructor({ name = requiredParam('name'), courses = [] } = {}) {
        this.#name = name
        this.#courses = courses
    }

    get name() {
        return this.#name
    }

    set name(newName) {
        if (newName.lenght > 0) this.#name = newName
        else console.warn('The new name must have at least one character')
    }

    get courses() {
        return this.#courses
    }

    set courses(newCourse) {
        if (!this.#courses.includes(newCourse)) this.#courses.push(newCourse)
        else console.warn('The course already has been included')
    }
}

la frase tipica del profe es ‘si, pero, no. pero si’

ponemos privada _learningPaths pero, todavía no la tenemos privada hay una forma de solucionar esto y es devolver esta propiedad de learningPaths a dentro de nuestro prototipo student.
lo que hacemos es definir una propiedad para nuestro student pero ya no vamos a llamar a student.prototype vamos a cambiarlo por this porque todas la asignaciones cuando estamos dentro de un prototipo las podemos hacer con this
vamos a crear una propiedad learningPath que tiene un get y set
ahora podemos utilizar lo que aprendimos de la propiedades privadas de js aprovechando el scope de JS y en este caso de nuestro prototype para nu guardar tododo dentro de _learningPaths porque aunque es privado en nomenclatura no es privado en realidad.
creamos una const de atributos privados y le asignamos el objeto ‘_learningPaths’ y por ahora es un objeto vacio
const private ={’_learningPaths’: [],};
en el get vamos a retornar del objeto private la propiedad _learninpaths

return private["_learningPaths"]

ahora en el if del set ingresamos el private de learninpaths para poder ingresar los datos con push

set(newLp) {
if (newLp instanceof LearningPath) {
private["_learningPaths"].push(newLp);

por cada uno de los learningPath del parametro que estamos recibiendo en nuestro prototipo learningPaths vamos a ir por cada uno de ellos y vamos a llamar al seter
y le decimos que intente agregar nuestro nuevo learning path

const private = [
’_doors’,
’_price’
]

class car {
constructor(_doors, _price){
this._doors = _doors;
this._price = _price;
this.cotizar = [];
}
}
Object.defineProperty(this, “_doors”, “__price”, {
get() {
return private[_doors * _price];
},
set(cotizar) {
if (cotizar = _doors * _price) {
private[_doors*_price].push(cotizar);
} else {
console.warn(“el precio no es correcto”);
}
},
});

Para alguien que es novato en Programación Orientado en Objetos y es su primera vez que lo trabaja en JS, creo que le resultaría muy dificil entender que se está haciendo en la clase. Y es un poco tedioso y enredado que de repente, se te olvida que es lo que quieres hacer o para que lo quieres aplicar.