💚 Like si te enamoraste de Object.defineProperty
con esta clase 👊
Introducción
¿Qué hay dentro de los objetos en JavaScript?
Profundicemos en los objetos
Static: atributos y métodos estáticos en JavaScript
Métodos estáticos del prototipo Object
Object.defineProperty
Cómo copiar objetos en JavaScript
Cómo funciona la memoria en JavaScript
Shallow copy en JavaScript
Qué es JSON.parse y JSON.stringify
Recursividad en JavaScript
Qué es recursividad
Deep copy con recursividad
Playgrounds: Hacer freeze de un objeto de forma recursiva
Abstracción y encapsulamiento sin prototipos
Abstracción con objetos literales y deep copy
Factory pattern y RORO
Module pattern y namespaces: propiedades privadas en JavaScript
Getters y setters
Cómo identificar objetos
Qué es duck typing
Duck typing en JavaScript
Instance Of en JavaScript con instancias y prototipos
Atributos y métodos privados en prototipos
Creando métodos estáticos en JavaScript
Próximos pasos
¿Quieres más cursos de POO en JavaScript?
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Última oportunidad para asegurar tu aprendizaje por 1 año a precio especial
Antes: $249
Paga en 4 cuotas sin intereses
Termina en:
En JavaScript no tenemos keywords para indicar que un atributo es privado o público a diferencia de otros lenguajes de programación. Sin embargo, podemos aplicar ciertas técnicas y métodos para lograrlo.
Modificaremos la función creada anteriormente con la que podíamos generar nuevos objetos. Esto con la finalidad de separar los atributos que queremos que sean privados (por ahora solo el atributo name
) y públicos, además de crear 2 funciones: una para poder modificar el atributo privado y otra para obtener el valor de esa propiedad privada:
Declaramos un objeto privateAtributos
en el que colocaremos las propiedades que deseamos que sean privadas y otro objeto publicAtributos
en el que queremos que sean públicas. Por ahora, solo name
será privada y por convención se coloca un guion bajo delante del nombre de aquel atributo privado:
function isObject(subject) {
return typeof subject == "object";
}
function isArray(subject) {
return Array.isArray(subject);
}
function requiredParam(param) {
throw new Error(param + " es obligatorio");
}
function createStudent({
name = requiredParam("name"),
email = requiredParam("email"),
age,
twitter,
instagram,
facebook,
approvedCourses = [],
learningPaths = [],
} = {}) {
const privateAtributos = { // 👈👈
// '_name' es el atributo privado
// 'name' es el parámetro que recibe la función
"_name": name,
};
const publicAtributos = { // 👈👈
// El resto de atributos serán públicos:
email,
age,
approvedCourses,
learningPaths,
socialMedia: {
twitter,
instagram,
facebook,
}
}
return publicAtributos ;
}
Por consiguiente, solo retornaremos publicAtributos
, pues contiene las propiedades a las que sí podrán acceder los usuarios.
Crearemos las funciones con las que el usuario puede editar y leer el valor del atributo privado _name
:
function isObject(subject) {
return typeof subject == "object";
}
function isArray(subject) {
return Array.isArray(subject);
}
function requiredParam(param) {
throw new Error(param + " es obligatorio");
}
function createStudent({
name = requiredParam("name"),
email = requiredParam("email"),
age,
twitter,
instagram,
facebook,
approvedCourses = [],
learningPaths = [],
} = {}) {
const privateAtributos = {
"_name": name,
}
const publicAtributos = {
// El resto de atributos serán públicos:
email,
age,
approvedCourses,
learningPaths,
socialMedia: {
twitter,
instagram,
facebook,
},
readName() { // 👈👈
return privateAtributos["_name"];
},
changeName(newName) { // 👈👈
privateAtributos["_name"] = newName;
},
}
return publicAtributos ;
}
Finalmente, deberíamos evitar que el usuario modifique o elimine los métodos readName
y changeName
y dar así mejor seguridad a estos. Con Object.defineProperty
haremos las configuraciones respectivas para evitar lo mencionado:
function isObject(subject) {
return typeof subject == "object";
}
function isArray(subject) {
return Array.isArray(subject);
}
function requiredParam(param) {
throw new Error(param + " es obligatorio");
}
function createStudent({
name = requiredParam("name"),
email = requiredParam("email"),
age,
twitter,
instagram,
facebook,
approvedCourses = [],
learningPaths = [],
} = {}) {
const privateAtributos = {
"_name": name,
};
const publicAtributos = {
// El resto de atributos serán públicos:
email,
age,
approvedCourses,
learningPaths,
socialMedia: {
twitter,
instagram,
facebook,
},
readName() {
return privateAtributos["_name"];
},
changeName(newName) {
privateAtributos["_name"] = newName;
},
};
Object.defineProperty(publicAtributos, "readName", { // 👈👈
writable: false,
configurable: false,
});
Object.defineProperty(publicAtributos, "changeName", { // 👈👈
writable: false,
configurable: false,
});
return publicAtributos ;
}
// Creamos un nuevo objeto
const juan = createStudent({ email: "[email protected]", name: "Juanito" });
// Intentamos eliminar y alterar los métodos changeName y readName
delete juan.changeName; // false
delete juan.readName; // false
juan.changeName = function (nombreImpostor) { // NO se ve afectada la función original
return "patatas";
}
La desventaja de protegerlos es que no nos permitiría trabajar con el polimorfismo (uno de los pilares de POO).
El funcionamiento de nuestros métodos generados changeName
y readName
es muy similar a los Getters y Setters. Veamos cómo aplicar estos en JavaScript. 🤔💪
Contribución creada por: Martín Álvarez (Platzi Contributor)
Aportes 28
Preguntas 11
💚 Like si te enamoraste de Object.defineProperty
con esta clase 👊
Existe una manera de definir o modificar varias propiedades a la vez 👀. Con la función Object.defineProperties()
se puede pasar un objeto con las propiedades de cada key que quieren ser modificadas:
// Con defineProperty
Object.defineProperty(public, "readName", {
writable: false,
configurable: false,
});
Object.defineProperty(public, "changeName", {
writable: false,
configurable: false,
});
// Con defineProperties
Object.defineProperties(public, {
readName: {
configurable: false,
writable: false,
},
changeName: {
configurable: false,
writable: false,
},
});
Que buen maestro es Timothée Chalamet
JavaScript no es un lenguaje fuertemente tipado, osea, que no tenemos que definir el tipo de nuestras variables. JavaScript entenderá que tipo de variable estamos usando.
En javascript no hay una palabra clave para definir una variable privada por lo que podemos crear una función que nos permita crear esta variable.
Por lo general, podemos usar el Object.defineProperty() para crear la variable publica o privada
Otro forma de lllamar lo que se esta realizando en esta clase es el uso de closures para retornar un contexto controlado y poder usar variables privadas en JS vanilla 😄
Todo era felicidad y entusiasmo hasta que llegué a estas últimas clases y cada vez me empecé a perder más 😥
actualmente podemos encapsular atributos y metodos privados en JS gracias al ES2022 simplemente agregando un # adelante del nombre de la variable o funcion y estas no entraran en conflicto con los metodos o atributos publicos, facilmente, podriamos tener declarados dos atributos con el mismo nombre y mas aun, una puede contener a la otra y no habra problema alguno, no se si me estoy adelantando pero solo me dio curiosidad jisjis, aqui la ref. 💚
Hola! como estan?
Bien, ese pequeno reto del profe para que el usaurio no pueda hacer juan name = “nicolas”, lo resolvi de esta manera:
newName(name) {
Object.defineProperty(juan, "name", {
value: name,
writable: false,
configurable: true,
enumerable: true,
})
},
changeName(newName) {
this.name = newName;
},
En este punto estoy extremadamente perdido 😦
en este caso, object.freeze() no es posible usarlo debido al scope de las funciones
changeName(newName){
const nombre = newName.toLowerCase()
console.log(nombre)
for(i in malaPalabras){
if(nombre.includes(malaPalabras[i].toLowerCase())){
return console.log(`Esta palabra esta prohibida`)
}
}
this.firsName = newName
}
ame la terrible palabra “PATAAAATAAAAA”
De hecho, ya hay private class fields en JS
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
Para el reto del profe deja una solución posible
function verify(nameVerify){
let blackList = ["puto","marica","verga","idiota"];
let verification = true;
for (ind in blackList){
if (nameVerify === blackList[ind]){
verification = false;
}
}
if (verification){
console.log("Cambio realizado con exito");
}
else {
console.log("El nombre de usuario no esta permitido");
}
};
Excelente explicación del profesor
Para poder solucionar lo planteado en el minuto 3:50 (hacer que el nombre no cambie después), se logra con Object.defineProperty. De esta forma:
changeName(newName){
this.name = newName;
Object.defineProperty(this, 'name', {
writable: false,
})
},
//dentro del return de la función
Para el reto sobre nombres prohibidos, cree un array con la lista de nombres no permitidos.
const noNames = ["nombreProhibido", "nombreProhibido1", "nombreProhibido2"]
Luego simplemente en nuestro metodo changeName, agregamos un condicional, si el newName es alguno de los elementos del array, usando el metodo .includes nos lanzara true y una advertencia, si es false entonces se cambia el nombre.
changeName(newName) {
if (noNames.includes(newName)) {
console.warn("Nombre invalido, seleccione otro")
} else {
private._name = newName;
}
}
function verify(newName){
const blackList = ["fuck","Bastard","Berk","Knob","Manky","Minger","Pillock"];
for(ind in blackList){
if(newName == blackList[ind]){
return false;
}else{
return true;
}
}
}
if(verify(newName)){
_private["_name"];
}else{
throw new Error(newName + " no es una palabra permitida >:(");
}
},
Wow interesante forma de aplicar programación orientada a objetos, pero no solo con POO, también lo podemos aplicar para nuestro desarrollo en backend o frontend, haciendo más mantenible y legible nuestro código, aunque sigo prefiriendo la sintaxis de CLASS para POO en javascript
PATATAS!
vuelvo y digo…
La mejor frase.
“Esto nos lleva a diferentes problemas"
o más bien
"A diferentes soluciones”
Te explicaré el concepto de atributos públicos y privados en JavaScript.
.
En JavaScript, no existe una declaración explícita para definir atributos como públicos o privados, como en otros lenguajes de programación. Sin embargo, puedes utilizar convenciones y técnicas para simular atributos privados.
.
Atributos públicos:
Los atributos públicos en JavaScript son aquellos a los que se puede acceder y modificar desde cualquier parte del código. Se definen directamente en el objeto o función, sin ningún tipo de restricción de acceso.
.
Aquí tienes un ejemplo de un atributo público en un objeto:
var persona = {
nombre: "Juan",
edad: 25
};
console.log(persona.nombre); // Acceso al atributo público "nombre"
persona.edad = 30; // Modificación del atributo público "edad"
En este caso, tanto el atributo “nombre” como el atributo “edad” son accesibles y modificables desde cualquier parte del código.
.
Atributos privados:
Los atributos privados en JavaScript se simulan mediante convenciones y técnicas para limitar el acceso directo a ellos desde fuera del ámbito en el que se definen. No existe una forma nativa de hacerlos privados, pero se pueden utilizar diferentes enfoques para lograrlo.
.
Un enfoque común es utilizar el concepto de clausura (closure) para crear un ámbito local y definir los atributos como variables dentro de ese ámbito. De esta manera, los atributos solo serán accesibles desde las funciones internas y no desde fuera.
.
Aquí tienes un ejemplo que demuestra cómo se pueden crear atributos privados utilizando clausuras:
function crearPersona(nombre) {
var edad = 25; // Atributo privado
// Función que accede al atributo privado
function obtenerEdad() {
return edad;
}
// Función que modifica el atributo privado
function cambiarEdad(nuevaEdad) {
edad = nuevaEdad;
}
// Objeto que expone solo las funciones públicas
return {
nombre: nombre,
obtenerEdad: obtenerEdad,
cambiarEdad: cambiarEdad
};
}
var persona = crearPersona("Juan");
console.log(persona.nombre); // Acceso al atributo público "nombre"
console.log(persona.obtenerEdad()); // Acceso al atributo privado "edad"
persona.cambiarEdad(30); // Modificación del atributo privado "edad"
console.log(persona.obtenerEdad()); // Acceso al atributo privado "edad" modificado
En este ejemplo, utilizamos una función llamada crearPersona
para crear un objeto que encapsula tanto atributos privados como públicos. La variable edad
se define dentro de la función y, por lo tanto, solo es accesible desde las funciones internas (obtenerEdad
y cambiarEdad
). El objeto devuelto por crearPersona
solo expone las funciones públicas (nombre
, obtenerEdad
y cambiarEdad
), lo que permite acceder a los atributos privados de manera controlada.
.
Aunque estos atributos no son estrictamente privados en el sentido tradicional, el uso de clausuras y técnicas similares nos permite ocultarlos y controlar el acceso a ellos.
.
Espero que esta explicación te ayude a entender cómo se pueden crear atributos públicos y privados en JavaScript. Si tienes más preguntas, no dudes en hacerlas.
Patatas 🤬🤬🤬
Mi resumen:
// ### Evitar modificaciones en atributos de objetos
/*Modificaremos la función creada anteriormente con la que podíamos generar nuevos objetos.
Esto con la finalidad de separar los atributos que queremos que sean privados (por ahora solo el atributo name) y públicos.
Además de crear 2 funciones: una para poder modificar el atributo privado y otra para obtener el valor de esa propiedad privada:*/
function createStudent3({
name,
age
} = {}) {
const privateAttributes = { //métodos privados
"_name" : name,
}
const publicAttributes = {//lo que se podrá ver y manipular
age,
readName(){
return privateAttributes["_name"];
},
changeName(param) {
privateAttributes["_name"] = param;
}
}
return publicAttributes;
}
const alfonso = createStudent3({name : "alfonso",age : 14});
console.log(alfonso);
/*
{
age: 14,
readName: [Function: readName],
changeName: [Function: changeName]
}
*/
console.log(alfonso.readName()); //alfonso
alfonso.changeName("alfonsito"); //alfonso //solo a través del método changeName es posible cambiar el nombre, ya que no es posible hacerlo de manera directa
alfonso._name = "GOKU"; //No cambiará de nombre
console.log(alfonso.readName()); //alfonsito
//Sin embargo si lo dejamos así, se podrá modificar el valor de métodos, por lo que se usará Object.defineProperty() para evitar ello
Object.defineProperty(alfonso,"readName",{
writable : false,
configurable : false
})
Object.defineProperty(alfonso,"changeName",{
writable : false,
configurable : false
})
console.log(Object.getOwnPropertyDescriptors(alfonso));
/*
{
age: { value: 14, writable: true, enumerable: true, configurable: true },
readName: {
value: [Function: readName],
writable: false,
enumerable: true,
configurable: false
},
changeName: {
value: [Function: changeName],
writable: false,
enumerable: true,
configurable: false
},
_name: {
value: 'GOKU',
writable: true,
enumerable: true,
configurable: true
}
}
*/
Con cada cosa nueva que veo de Javascript me hace desear más el momento de llegar a aprender Typescript.
Hoal! Este es mi codigo para poder firzar el retorno del objeto!
const isObject = (object) => {
return typeof object == "object"
}//es un array o un objeto
function deepFreeze(obj) {
for (key in obj) {
let keyIsObject = isObject(obj[key])
if(keyIsObject) {
deepFreeze(obj[key])
} else {
Object.freeze(obj[key])
}
}
return Object.freeze(obj)
}
function user({
name,
contactInfo = requiredParam('contactInfo'), //parametro por defecto. Si no le paso el contactInfo, llamare a la fn contactInfo y retornara un error
active = true,
apellido,
cursos = []
} = {} )
{
return deepFreeze({
name,
contactInfo,
addCourse(course) {
this.cursos.push(course)
},
changeName(newName) {
this.name = newName
},
active,
apellido,
cursos
})
}
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?
o inicia sesión.