Objetos en Node.js y Javascript. Lo realmente importante
Introducción
“Objetos en Node.js y Javascript. Lo realmente importante” se refiere a los principios detrás de los objetos, lo que es realmente importante para continuar aprendiendo, mejorar el entendimiento de objetos en programación y en Node.js y Javascript.
Empecemos con el entendimiento de lo que es un objeto en programación.
¿Qué son los objetos en programación?
Se ha dicho mucho que la programación orientada a objetos representa el mundo real. La verdad es que la programación en general trata de representar el mundo real en algo digital. Así que la programación orientada a objetos también trata de representar digitalmente el mundo real, solo que con un poco más de énfasis en el uso de objetos. Pero como hemos establecido en esta publicación, lo importante NO son los objetos, lo importante es:
El paso de mensajes para la comunicación entre objetos
En programación un objeto es una forma digital de agrupar funcionalidades y que algunas de ellas pueden ser análogas a las de un objeto en la vida real, pero que es mucho más limitado y no se debe esperar que sean iguales, porque solo es una representación digital.
Cuando desarrollas software, y necesitas agregar una funcionalidad, modificar o arreglar un error, tu pensamiento lógico está basado en la representación de objetos digitales, sus relaciones y comunicaciones con otras entidades digitales. Nunca se debe pensar que como el objeto real se comporta de cierta manera, su representación también. Es de mucha ayuda conceptualmente, pero en la implementación muy a menudo son diferentes.
Recordemos lo que nos dice Alan Kay:
Lamento que hace tiempo haya acuñado el término Objetos para la programación porque hizo que las personas se enfocaran en la parte menos importante. La gran idea es «Envío de mensajes«
Alan Kay
También un objeto sirve para almacenar datos útiles que otros objetos pueden utilizar. Entonces un objeto nos sirve para:
- Crear comunicaciones entre representaciones digitales de objetos.
- Permite agrupar funcionalidades relacionadas con la representación digital del objeto.
- Almacena datos que otro objeto puede usar. (Funciona como algunas estructuras de datos).
¿Qué son los objetos en Node.js y JavaScript?
Los objetos en Node.js y Javascript, son una colección de pares nombre: valor
, parecido a los «arrays asociativos» de PHP. Estos pares de nombre/valor
se les llama propiedades, una propiedad es como una variable y puede contener cualquier tipo de valor.
La clave del párrafo anterior es que puede contener cualquier tipo de valor, incluyendo funciones. Y con esto se identifican dos tipos de objetos.
- Objetos con funcionalidades y comunicaciones
- Que en su mayor parte tienen métodos para comunicarse con otros objetos y resolver problemas.
- Objetos con datos. También llamado estructura de datos
- Que en su mayor parte contiene datos y que nos sirven para almacenar y enviar información a través de mensajes. Estos objetos son utilizados por los objetos con funcionalidades y comunicaciones.
Recordemos que todo lo relacionado con objetos también funciona en cualquier lugar donde se ejecute JavaScript, node.js incluido.
¿Cómo crear objetos en Node.js y Javascript?
La forma más sencilla de crear un objeto es a través de un objeto literal. Un objeto literal es aquel que se crea con llaves {}
, agregando sus propiedades y métodos. Como el siguiente ejemplo.
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
}
};
jaime.getNombre(); // Jaime Cervantes Velasco
Tenemos un objeto con cinco propiedades, nombre
, apellidoPaterno
y apellidoMaterno
son de tipo string, edad
es de tipo number
y la última propiedad, getNombre()
es de tipo function
. Aquí tienes más información sobre los tipos de datos.
Aunque más adelante veremos a detalle las funciones, de momento, podemos decir que las funciones nos permiten encapsular y ejecutar un conjunto de operaciones que hacemos muy a menudo. En lugar de repetir el código de estas operaciones muchas veces, mejor ponemos estas operaciones dentro de una función.
Cada vez que necesitemos ejecutar estas operaciones, simplemente ejecutamos la función, a esta ejecución de la función se le llama invocación. Ejemplo de invocación es en la última línea, donde utilizamos paréntesis, jaime.getNombre();
.
¿Cómo obtengo el valor de una propiedad?
Para obtener alguna propiedad de un objeto, podemos hacer de dos maneras:
- Notación de puntos
- Usando corchetes, muy parecido a como se acceden a los datos de un
Array
La primera forma, notación de puntos, es muy sencilla, ejemplo:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
}
};
jaime.nombre // 'Jaime'
jaime.apellidoPaterno; // 'Cervantes'
jaime.apellidoMaterno; // 'Velasco'
jaime.edad; // 33
jaime.getNombre // function () { return 'Jaime Cervantes Velasco'; }
La segunda forma, usando corchetes []
, ejemplo:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
}
};
jaime['nombre'] // 'Jaime'
jaime['apellidoPaterno']; // 'Cervantes'
jaime['apellidoMaterno']; // 'Velasco'
jaime['edad']; // 33
jaime['getNombre'] // function () { return 'Jaime Cervantes Velasco'; }
jaime['getNombre']() // 'Jaime Cervantes Velasco'
Aquí accedimos a las propiedades con corchetes usando el nombre de las propiedades, la cuales son strings
.
Destaca como invocamos a la función getNombre
. Estas dos sentencias hacen lo mismo, invocar a la función getNombre
:
jaime.getNombre();
jaime['getNombre']();
El nombre de propiedades son cadenas de caracteres
Cuando usamos la notación de corchetes, usamos una cadena de caracteres para obtener el valor. Lo que mencionamos antes sobre que Un objeto es una colección de pares nombre: valor
. El nombre
de todas las propiedades son cadena de caracteres (string).
Como los nombres de las propiedades son cadenas de caracteres, entonces podemos definir propiedades encerradas por comillas, como el siguiente ejemplo:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
},
'nueva-propiedad-con-guion-medio': 'Mi propiedad con guión medio encerrada por comillas'
};
jaime['nueva-propiedad-con-guion-medio']; // 'Mi propiedad con guión medio encerrada por comillas'
Y obtenerlos con notación de corchetes así, jaime['nueva-propiedad-con-guion-medio']
.
Una propiedad como esta, no puede ser accedida con una notación de puntos, así jaime.nueva-propoiedad-con-guion.medio
. Esta sentencia te dará un error en Javascript porque no es nombre de propiedad válido.
De hecho en Javascript el nombre de variables o propiedades válidos deben:
- Empezar con una letra, incluido
$
y_
. - No puede empezar con números o caracteres usados en el lenguaje para otros propósitos, ejemplo
-, %, /, +, &
. - Después del la letra de inicio puedes ocupar números, otras letras válidas,
$
y_
.
¿Objetos en Node.js y Javascript que contienen otros objetos?
Un objeto puede contener cualquier tipo de dato, así que puede contener otros objetos. Veamos un ejemplo:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
},
direccion: {
calle: 'Melchor Ocampo',
numero: 2,
colonia: 'Las Flores',
municipio: 'Tezonapa',
estado: 'Veracruz'
}
};
Aquí agregamos un objeto direccion
al objeto jaime
. Simplemente usando las llaves para crear objetos literales.
¿Cómo agrego, modifico y elimino propiedades?
Una vez que creas un objeto, puedes agregar más propiedades, modificarlas y eliminarlas.
Agregar propiedades a los objetos en Node.js y Javascript
Se utiliza la asignación para agregar nuevas propiedades que no existen previamente.
const jaime = {
nombre: 'Jaime',
};
jaime.apellidoPaterno = 'Cervantes';
jaime.appelidoMaterno = 'Velasco';
console.log(jaime ); // { nombre: 'Jaime', apellidoPaterno: 'Cervantes', apellidoMaterno: 'Velasco }
Actualizar propiedades a los objetos en Node.js y Javascript
Se utiliza también la asignación para actualizar el valor de las propiedades que ya existen.
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
};
jaime.apellidoPaterno = 'Perez'
jaime.apellidoMaterno = 'Moreno';
console.log(jaime ); // { nombre: 'Jaime', apellidoPaterno: 'Perez', apellidoMaterno: 'Moreno' }
Eliminar propiedades a los objetos en Node.js y Javascript
Para eliminar propiedades se usa el operador delete
. Ejemplo:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
};
delete jaime.apellidoPaterno;
// o tambien así:
delete jaime['apellidoMaterno];
console.log(jaime); // { nombre: 'Jaime' }
Las propiedades apellidoPaterno
y apellidoMaterno
, ya no existe en el objeto jaime
.
¿Objetos en Node.js y Javascript son una referencia o un valor?
Los objetos en Node.js y JavaScript siempre serán una referencia, es decir:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
};
const pedro = jaime;
pedro.nombre = 'Pedro';
jaime.nombre === pedro.nombre; // true;
jaime.nombre; // 'Pedro'
pedro.nombre; // 'Pedro'
Las constantes jaime
y pedro
hacen referencia al mismo objeto en memoria.
¿Cómo recorro las propiedades de un objeto?
Existen varias formas, la más simple de recorrer las propiedades de un objeto es la siguiente.
Tenemos el objeto jaime
:
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
}
};
Lo primero es obtener el nombre de sus propiedades con el método Object.keys
, el cual genera un Array
con los nombres de las propiedades.
const nombresProps = Object.keys(jaime);
console.log(nombresProps); // ["nombre", "apellidoPaterno", "apellidoMaterno", "edad", "getNombre"]
Ahora recorremos el arreglo nombresProps
para obtener los valores.
nombresProps.forEach(nombreProp => {
console.log(jaime[nombreProp]); // imprime el valor del nombre de propiedad actual
});
El resultado del código anterior es algo como lo siguiente.
'Jaime'
'Cervantes'
'Velasco'
33
function () {
return 'Jaime Cervantes Velasco';
}
¿El prototipo de un objeto?
Javascript es un lenguaje de programación multi-paradigma, en su lado orientado a objetos, no utiliza clases para la reutilización de código, más bien usa composición de objetos. Pero ¿Cómo hace esta composición de objetos?
Mejor composición de objetos sobre herencia de clases
Mejor composición de objetos sobre herencia de clases
Design Patterns: Elements of Reusable Object-Oriented Software
Este principio es bastante aplicado en Javascript y por consiguiente se aplica de igual manera en Node.js, de hecho así fue construido. Todos los objetos en Javascript tiene un enlace a un prototipo, y puede utilizar todas las propiedades y funciones de su prototipo.
Este enlace de prototipo puede ser referenciado en código, usando la propiedad especial __proto__
.
En el caso de los objetos literales, como jaime
, son enlazados a Object.prototype
, que es el objeto base de todos los objetos en Javascript.
const jaime = {
nombre: 'Jaime',
apellidoPaterno: 'Cervantes',
apellidoMaterno: 'Velasco',
edad: 33,
getNombre: function () {
return 'Jaime Cervantes Velasco';
}
};
jaime.__proto__; // { constructor: f Object(), hasOwnProperty: f hasOwnProperty, ... }
jaime.__proto__ === Object.prototype; // true
jaime.toString === Object.prototype.toString; // true
jaime.toString(); //[object Object]
Si expandimos los prototipos en un navegador web como chrome, se visualiza de esta manera.
Gráficamente, el enlace de prototipos es como la imagen de abajo.
El prototipo también lo puedes ver en node.js si lo enlazas a un debugger. Pero no veremos como hacer eso hoy.
¿Qué es la cadena de prototipos?
Hemos creado el objeto jaime
literalmente, pero que tal si lo creamos a través de otro objeto persona
, haciendo el objeto persona su prototipo inmediato.
const persona = {
saludar: function () {
return 'Hola';
},
comer: function () {
return 'comiendo...';
}
};
const jaime = Object.create(persona); // Creamos jaime en base al prototipo persona
Si hacemos ahora un console.log(jaime)
:
console.log(jaime);
Y expandimos el resultado en un navegador web como chrome, el resultado se visualiza de la siguiente forma.
En la imagen, podemos ver que [[prototype]]
apunta al objeto persona
, por lo que jaime
tiene acceso a los métodos comer
y saludar
del objeto persona
.
Y si hacemos estas comparaciones vemos que el enlace al prototipo persona existe.
jaime.__proto__; // { saludar: f (), comer: f () }
jaime.__proto__ === persona; // true
Podemos invocar los métodos comer
y saludar
del objeto persona como si fueran de jaime
.
jaime.comer(); // 'Comiendo...'
jaime.saludar(); // 'Hola'
Ahora si expandimos el último [[prototype]]
¿Cuál crees que sea el resultado?
Vemos claramente que el último [[prototype]]
es Object.prototype
, el prototipo base de todos los objetos en Javascript. Y puedes comprobarlo en código comparando Object.prototype
con las propiedades especiales __proto__
.
jaime.__proto__.__proto__ === Object.prototype; // true
También podemos usar el método toString
como si fuera de jaime
.
jaime.toString(); // [object Object]
A esto se le llama cadena de prototipos, un objeto puede tener los sub prototipos necesarios para reutilizar sus propiedades y métodos. En este caso fue así, jaime → persona → Object.prototype
.
Peculiaridades con la cadena de prototipos
Usa los métodos y propiedades de los sub prototipos
El objeto jaime
puede utilizar directamente las propiedades y métodos de sus prototipos.
jaime.toString(); // '[object Object]' --> de Object.prototype
jaime.hasOwnProperty('nombre'); // true --> de Object.prototype
jaime.saludar(); // 'Hola' --> de persona
jaime.comer(); // 'Comiendo...' --> de persona
jaime.nombre; // 'Jaime' --> de si mismo, objeto jaime
Actualizar propiedades no afecta a la cadena de prototipos
Cuando tenemos una propiedad que existe en varios sub prototipos, javascript usa el que esté más cerca en la cadena de prototipos. Supongamos que queremos cambiar el comportamiento del método toString
en jaime
así que en lugar de usar Object.prototype.toString
, a jaime
le agregamos un método con el mismo nombre.
Se puede decir que lo que estamos haciendo es actualizarlo.
jaime.toString = function () {
return `${this.nombre} ${this.edad}`;
};
jaime.toString(); // 'Jaime 33'
Ahora se usa el método toString
de jaime
, no se recorre la cadena de prototipos hasta llegar a Object.prototype.toString
como se hizo en los anteriores ejemplos. Por que ya existe un metodo toString
directamente en nuestro objeto jaime
.
El operador delete
no elimina las propiedades de los prototipos
Cuando ocupamos el operador delete
, este nunca toca la cadena de prototipos, solo elimina las propiedades propias del objeto en cuestión.
Si en la cadena de prototipos existe una propiedad con el mismo nombre de la que eliminamos, entonces ahora esa propiedad en la cadena de prototipos será utilizada.
Usando el ejemplo anterior, del método toString
de jaime
, si lo eliminamos con delete
, entonces ahora el toString()
de Object.prototype
es el que se usara porque el objeto persona no tiene ese método directamente.
jaime.toString = function () {
return `${this.nombre} ${this.edad}`;
};
jaime.toString(); // 'Jaime 33'
delete jaime.toString
jaime.toString(); // '[[object Object]]' --> De Object.prototype
En la última línea notamos que ahora jaime
vuelve a usar el toString()
de Object.prototype
.
Ejemplos de objetos predefinidos en Node.js y Javascript
Como ya hemos mencionado en otras publicaciones, a excepción de los tipos primitivos, todo lo demás en javascript son objetos. En futuras publicaciones veremos más detalle de los objetos de tipo function
y Array
¿Cómo crear un objeto function
?
function imprimirPersona(persona) {
console.log(persona.nombre);
console.log(persona.edad);
}
Como la función imprimirPersona
es un objeto, podemos agregarle propiedades sin ningún problema.
imprimirPersona.miPropiedad = 'Mi propiedad de una funcion';
console.log(imprimierPersona.miPropiedad); // 'Mi propiedad de una funcion
Las funciones son muy útiles, tan versátiles que con ellas podemos generar nuevos objetos, pero ese tema no pertenece a esta publicación.
¿Cómo crear un objeto Array
?
La forma recomendada es simplemente crearlos usando corchetes []
.
const vacio = [];
const numeros = [1, 2, 3, 4, 5, 6];
const animales = ['perro', 'gato', 'caballo'];
const conObjetos = [
{
nombre: 'Jaime',
edad: 33
},
function imprimirPersona(persona) {
console.log(persona);
}
];
Como un array es un objeto, igual que una función, también le puedes agregar propiedades.
const numeros = [1, 2, 3, 4, 5, 6];
numeros.miPropiedad = 'Mi propiedad de un arreglo';
console.log(numeros.miPropiedad); // 'Mi propiedad de un arreglo
El Array
es el objeto más claro para usarlo como estructura de datos.
¿Cómo crear un objeto Date
?
const fecha = new Date();
console.log(fecha); // Fri May 28 2021 10:46:27 GMT-0500 (hora de verano central)
Como te podrás imaginar, al objeto date también le podemos agregar propiedades porque es un objeto
Conclusión
En Javascript y en cualquier plataforma donde se ejecute, un ejemplo es en node.js, tienen la peculiaridad de siempre tener un enlace a un prototipo. Así es como el lenguaje está diseñado. Ya sé que actualmente existen las clases, pero en realidad estas son funciones que internamente hacen uso de prototipos, también lo explicaremos en otra publicación.
Con excepción de los datos primitivos, todo lo demás en Javascript son objetos, es importante saber como funcionan para no atorarnos en este aprendizaje.
Con esta información tienes lo suficiente para seguir avanzando y utilizar los objetos más sabiamente.
El tema de los objetos es bastante extenso, todavía quedan muchas cosas a tomar en cuenta, pero ya es demasiada información que tenemos que dividirla en más publicaciones. Así que muy pronto continuaremos. Cualquier duda, no dudes en escribirla en los comentarios, ¡Estaremos contentos de ayudarte!.