Tutorial de Javascript y las mejoras desde ES6 (ES2015) hasta ES10 (ES2019) – Parte 4 (Arrays, Objetos y funciones)
Arrays, Objetos y funciones
En esta nueva entrada definiremos y aprenderemos que son los arrays, los objetos y las funciones, mediante ejemplos y breves explicaciones.
¿Que es un array?
Un array es un objeto de tipo lista, cuya longitud y el tipo de datos que contiene no son fijos, dado que tanto su contenido como su tamaño pueden variar en cualquier momento.
Para crear un array podemos hacerlo de la siguiente forma:
// Diferentes formas de crear un array const arr1 = [1, 2, 3]; const arr2 = new Array(1, 2, 3);
Si necesitamos obtener el valor del array usaremos su indice.
Muy importante y a tener en cuenta en un array:
- El valor del indice comienza en 0.
- Según el tipo de dato:
- Primitivos (undefined, null, boolean, number, string, symbol): cuando asignamos un valor a esa variable se hace por valor.
- Resto (Arrays, Objetos, Promesas, etc…): Cuando asignamos un valor a esa variable se hace por referencia, básicamente el valor es la referencia.
const arr1 = [1, 2, 3]; console.log(arr1[0]); // 1
Una vez creado podemos realizar diferentes operaciones con el:
- Array.from(array, mapFn [op], this [op]): Crear una instancia nueva desde un objeto iterable
-
// Crear una instancia nueva desde un objeto iterable const arr = Array.from('Javascript'); // ["J", "a", "v", "a", "s", "c", "r", "i", "p", "t"] // El método Array.from tiene otros parámetros opcionales bastante curiosos const criptCesar = Array.from(arr, data => data.charCodeAt(0) + 4); // ["N", "e", "z", "e", "w", "g", "v", "m", "t", "x"]
- Array.isArray(obj): Nos devuelve si el valor pasado es un Array
-
Array.isArray(['a']); // true Array.isArray({a: 1}); // false Array.isArray('a'); // false Array.isArray(1); // false Array.isArray(true); // false Array.isArray(null); // false Array.isArray(undefined); // false
- Array.of(elementN): Crear una instancia nueva con un número variable de elementos que se pasan como parámetro
-
// Un array con un elemento Array.of(10); // [10] // Un array con diez elementos Array(10); // [empty × 10]
- Array.concat(arrayN): Nos devuelve un nuevo Array con la unión de dos o más Arrays.
-
const data1 = [1,2,3,4]; const data2 = [5,6,7,8]; const data3 = data1.concat(data2); console.log(data3); // (8) [5, 6, 7, 8, 1, 2, 3, 4]
- Array.copyWithin(target, start [op], end [op]): Realiza una copia plana de una sección a otra dentro del mismo y devuelve el array resultante. Esta acción no modifica su longitud.
-
const test = []; for (let cont = 0; cont < 10; cont++) { test.push(`Y-${cont}`); } console.log(test); // ["Y-0", "Y-1", "Y-2", "Y-3", "Y-4", "Y-5", "Y-6", "Y-7", "Y-8", "Y-9"] console.log(test.copyWithin(0, 5)); // ["Y-5", "Y-6", "Y-7", "Y-8", "Y-9", "Y-5", "Y-6", "Y-7", "Y-8", "Y-9"] const test2 = []; for (let cont = 0; cont < 10; cont++) { test2.push(`Y-${cont}`); } console.log(test2); // ["Y-0", "Y-1", "Y-2", "Y-3", "Y-4", "Y-5", "Y-6", "Y-7", "Y-8", "Y-9"] console.log(test2.copyWithin(0, 5, 6)); // ["Y-5", "Y-1", "Y-2", "Y-3", "Y-4", "Y-5", "Y-6", "Y-7", "Y-8", "Y-9"]
- Un ejemplo sencillo pero que nos muestra la potencia que nos ofrecen los arrays en Javascript
-
/* El método split() nos divide un objeto de tipo String en un array. (String) El método reverse() invierte el orden de los elementos de un array. (Array) El método join() une todos los elementos de una matriz en una cadena. (Array) */ const response = 'Texto de prueba'; const reverse = response.split('').reverse().join(''); console.log(reverse); // "abeurp ed otxeT"
Con los ejemplos anteriores he querido mostrar algunos de los métodos que ofrece Javascript para trabajar con arrays.
Métodos y propiedades de los Arrays
A continuación, para no extenderme con cada definición, voy añadir un listado completo pero sin ejemplos:
- Propiedades
- Array.length: Devuelve el numero de elementos.
- Array.prototype: Podemos añadir nuevas propiedades.
- Métodos
- Array.from(): Crea un array de un objeto iterable.
- Array.isArray(): No devuelve un valor boleano (true es array) indicando si la variable es un array o no.
- Array.of(): Crea un array con los datos pasados como argumentos.
- Transformadores (Modifican el array)
- Array.prototype.pop(): Elimina el último elemento del array y devuelve el valor eliminado.
- Array.prototype.push(): Añade un elemento al final del array y devuelve la nueva longitud.
- Array.prototype.reverse(): Invierte el orden del array y devuelve el array.
- Array.prototype.shift(): Elimina el primer elemento del array y devuelve el valor eliminado.
- Array.prototype.sort(): Ordena los elementos del array.
- Array.prototype.splice(): Añade y/o elimina elementos del array.
- Array.prototype.unshift(): Añade un elemento al principio del array y devuelve la nueva longitud.
- Accesores (No modifican el array y devuelve un nuevo array)F
- Array.prototype.concat(): Nos permite unir múltiples arrays y nos devuelve un nuevo array con la unión.
- Array.prototype.join(): Une todos los elementos del array y devuelve una cadena de texto.
- Array.prototype.slice(): Devuelve un nuevo array en base a los parámetros indicados.
- Array.prototype.toString(): Devuelve una cadena de texto con el contenido del array.
- Array.prototype.indexOf(): Devuelve el indice del primer elemento que coincida.
- Array.prototype.lastIndexOf(): Devuelve el indice del último elemento que coincida.
- Repetición
- Array.prototype.filter(): Devuelve un nuevo array con todos los elementos que cumplan la función de filtrado.
- Array.prototype.forEach(): Ejecuta una función por cada elemento del array.
- Array.prototype.every(): Devuelve true si todos los elementos cumplen la condición de la función.
- Array.prototype.map(): Devuelve un nuevo array con los resultados de la función invocada.
- Array.prototype.some(): Devuelve true si algunos de los elementos cumplen la condición de la función.
Bien, con esto hemos dado un repaso a los arrys en Javascript, a continuación funciones.
¿Que es una función?
Una función la podemos definir como un pequeño programa que se compone de un número determinado de declaraciones.
Esta puede recibir o no valores y siempre devuelve un valor, ya que el hecho de devolver o no (opcional) lo tendríamos que denominar procedimiento.
Las funciones en Javascript son objetos de tipo Function, así que las podemos manipular y transmitir como si de un objeto se tratara.
Para que una función devuelva un valor, necesitamos hacer uso de la palabra reservada return.
En el caso de no hacerlo, devolverá el valor predeterminado que sera undefined.
Si usásemos la palabra reservada new el valor predeterminado sería el valor de su parámetro.
class Clase1 { constructor() { this.valor = 'Clase1'; } } class Clase2 { } function Clase3() { const valor = 'Clase3'; } function Clase4() { const valor = 'Clase4'; return valor; } console.log(new Clase1()); // Clase1 {valor: "Clase1"} console.log(new Clase2()); // Clases2 {} console.log(Clase3()); // undefined console.log(Clase4()); // Clase4
Los parámetros que le pasamos a las funciones son los argumentos, y estos se pasan por valor.
Esto que significa lo siguiente:
- Si el valor es de tipo primitivo este cambio no se refleja en ninguna parte, ya que se modifica el valor.
- Si el valor es de tipo objeto este cambio se vera reflejado en cualquier sitio donde se haga referencia al objeto pasado.
function referencia(dato) { dato.a = 1000; } function valor(dato) { dato = 1000; } let ref = { a: 1 }; let val = 1; console.log('ANTES DE LA FUNCIÓN - REF:', ref); // ANTES DE LA FUNCIÓN - REF: {a: 1} console.log('ANTES DE LA FUNCIÓN - VAL', val); // ANTES DE LA FUNCIÓN - VAL 1 referencia(ref); // Llamo a la función y cambio el valor (referencia - objeto) valor(val); // Llamo a la función y cambio el valor (valor - primitivo) console.log('DESPUES DE LA FUNCIÓN - REF:', ref); // DESPUES DE LA FUNCIÓN - REF: {a: 1000} console.log('DESPUES DE LA FUNCIÓN - VAL', val); // DESPUES DE LA FUNCIÓN - VAL 1
A continuación algunas formas para definir una función en Javascript.
// Forma normal, como una sentencia function suma(a, b) { return a + b; } // Como una expresión const suma2 = function (a, b) { return a + b; } // Arrow function (Incluidas desde ES6 (EcmacScript 6)) const suma3 = (a, b) => a + b; // Aplicando recursividad llamandose así misma function fact(n) { const end = n == 0 || n == 1; let result; if (end) { result = 1; } else { result = (n * fact(n - 1)) } return result; } suma(2, 2); // 4 suma2(3, 3); // 6 suma3(4, 4); // 8 fact(8); // 40320
Otra de las características que ofrece Javascript son las closures.
Las closures en JavaScript podemos decir que se basa en el anidamiento de funciones nos permite tener acceso a todas las variables y funciones definidas dentro de la función
Tenemos que tener en cuenta que la función externa no tiene acceso al contenido de la función interna, esto nos ofrece cierta seguridad y protección.
Ahora un ejemplo para entender y asentar los conceptos.
const persona = (nombre, edad) => { // PRIVADO var nombre; var edad; var setNombre = (n) => { nombre = n; }; var getNombre = () => nombre; var setEdad = (e) => { edad = e; }; var getEdad = () => edad; return { // PÚBLICO get: (data = '') => { let result; try { if (data.toLowerCase() === 'nombre') { result = getNombre(); } else if (data.toLowerCase() === 'edad') { result = getEdad(); } else { throw new Error('Debes indicar "nombre" o "edad"'); } } catch(err) { console.log(err); } return result; }, set: (data = '', value = '') => { try { if (data.toLowerCase() === 'nombre') { setNombre(value); } else if (data.toLowerCase() === 'edad') { setEdad(value); } else { throw new Error('Debes indicar "nombre" o "edad"'); } } catch(err) { console.log(err); } }, } }; let p1 = persona('a',1); p1.get(); // Error: Debes indicar "nombre" o "edad" p1.get('nombre'); // "a" p1.get('edad'); // 1 p1.set(); // Error: Debes indicar "nombre" o "edad" p1.set('nombre', 'new'); p1.get('nombre'); // "new" p1.set('edad',100); p1.get('edad'); // 100 p1.nombre; // undefined - No tenemos acceso ni a las funciones ni a las variables p1.edad; // undefined - No tenemos acceso ni a las funciones ni a las variables p1; // {get: ƒ, set: ƒ}
Usando closures podemos usar patrones como modulo y namespace.
Para terminar esta parte, veremos que las funciones tienen un objeto llamado arguments y corresponde con los argumentos pasado a la función.
También debemos recordar que el objeto arguments no esta disponible en las arrow function, ya que en este tipo de definición el contexto no es el de la propia función.
function fn() { console.log('Function', arguments); } const fn2 = () => { console.log('Arrow', arguments); } fn(); // Function Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]length: 0callee: ƒ fn()Symbol(Symbol.iterator): ƒ values()__proto__: Object fn2(); // Uncaught ReferenceError: arguments is not defined fn(1,2,3,4,5,6,7,8); // Function Arguments(8) [1, 2, 3, 4, 5, 6, 7, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ] fn(1,2,3,4); // Function Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
En este punto ya sabes que son y como manipular los arrays, que es una función y como declararla y usarla.
Continuemos con la parte final de esta entrada, los Objetos.
¿Que es una objeto?
Un objeto lo podemos definir como una colección de propiedades, las cuales se componen de un par de valores llamados clave (key) y su valor (value).
La idea de un objeto es poder representar un objeto en la vida real, y aprovechar sus propiedades para definir sus características.
const Vehiculo = { color: '', velocidad: '', ruedas: '', tipo: '', motor: '', };
Como podemos ver en el ejemplo anterior tenemos el objeto vehículo, que tiene unas propiedades que definen las características del mismo.
Si necesitamos acceder a sus propiedades, esto se puede realizar de diferentes formas.
const Vehiculo = { color: 'Rojo', velocidad: '100', ruedas: '4', tipo: 'Terrestre', motor: true, }; const engine = 'motor'; // Usando el punto Vehiculo.color; // Rojo // Usando corchetes Vehiculo['color']; // Rojo // Usando una variable Vehiculo[engine]; // true
Con los ejemplos anteriores ya hemos realizado una pequeña introducción y definición de un objeto, ahora vamos a ver como crearlos.
Creando y usando nuestros objetos
Para trabajar con objetos usaremos una función constructora y crearemos una instancia usando el operador new.
// Función constructora function Vehiculo(color, velocidad, ruedas, tipo, motor) { this.color = color; this.velocidad = velocidad; this.ruedas = ruedas; this.tipo = tipo; this.motor = motor; } // Creando una instancia del objeto usando el operador 'new' let v1 = new Vehiculo('rojo', 100, 4, 'terrestre', true);
Con el código anterior podríamos crear tantos Vehiculos como queramos, ya que cada nueva instancia es independiente y tiene sus propias características.
También podemos usar el método Object.create para crear nuestros objetos sin tener que usar la función constructora como se muestra en el ejemplo anterior.
const Vehiculo = { color: '', velocidad: '', ruedas: '', tipo: '', motor: true, }; let v1 = Object.create(Vehiculo); v1.color = 'rojo'; v1.velocidad = 100; v1.ruedas = 4; v1.tipo = 'terrestre'; v1.motor = true; console.log(v1); /* color: "rojo" velocidad: 100 ruedas: 4 tipo: "terrestre" motor: true */
Al igual que ocurre en la programación orienta a objetos en otros lenguajes de programación, en Javascript también existe la herencia.
En Javascript todos los objetos heredan al menos de otro objeto, y el objeto del cual se esta heredando se conoce como prototipo (prototype).
Con esta característica podemos añadir nuevas propiedades a un objeto usando la propiedad prototype.
Debemos tener en cuenta que esta propiedad va a ser compartida por todos los objetos del tipo especificado.
function Vehiculo(color, velocidad, ruedas, tipo, motor) { this.color = color; this.velocidad = velocidad; this.ruedas = ruedas; this.tipo = tipo; this.motor = motor; } Vehiculo.prototype.propiedadComun = 'Cómun a todos los objetos creados'; let v1 = new Vehiculo('rojo', 100, 4, 'terrestre', true); let v2 = new Vehiculo('amarillo', 10, 2, 'terrestre', false); let v3 = new Vehiculo('verde', 40, 8, 'terrestre', true); let v4 = new Vehiculo('azul', 70, 3, 'terrestre', true); v1; // Vehiculo {color: "rojo", velocidad: 100, ruedas: 4, tipo: "terrestre", motor: true} v2; // Vehiculo {color: "amarillo", velocidad: 10, ruedas: 2, tipo: "terrestre", motor: false} v3; // Vehiculo {color: "verde", velocidad: 40, ruedas: 8, tipo: "terrestre", motor: true} v4; // Vehiculo {color: "azul", velocidad: 70, ruedas: 3, tipo: "terrestre", motor: true} v1.propiedadComun; // "Cómun a todos los objetos creados" v2.propiedadComun; // "Cómun a todos los objetos creados" v3.propiedadComun; // "Cómun a todos los objetos creados" v4.propiedadComun; // "Cómun a todos los objetos creados"
Para terminar con las propiedades, debemos saber que las propiedades tiene unos atributos los cuales definen como se comportaran esas propiedades.
function Vehiculo(color, velocidad, ruedas, tipo, motor, metodo) { this.color = color; this.velocidad = velocidad; this.ruedas = ruedas; this.tipo = tipo; this.motor = motor; this.metodoInside = () => { console.log(this); }; this.meotodoOutside = metodo; } const suma = (a, b) => { console.log(this); return a + b; }; let v1 = new Vehiculo(1, 2, 3, 4, 5, suma); // Vamos a ver los atributos de la propiedad color console.log(Object.getOwnPropertyDescriptor(v1, 'color')); /* value: 1 writable: true enumerable: true configurable: true */
A continuación una breve explicación de cada atributo.
- Writable: Definimos si el valor de esa propiedad se puede modificar.
- Configurable: Definimos si los atributos de esa propiedad se pueden modificar.
- Enumerable: Definimos si la propiedad se debe mostrar cuando se intenten enumerar.
Añadiendo métodos a nuestro objeto
Javascript nos permite definir propiedades que sean métodos.
function Vehiculo(color, velocidad, ruedas, tipo, motor, metodo) { this.color = color; this.velocidad = velocidad; this.ruedas = ruedas; this.tipo = tipo; this.motor = motor; this.metodoInside = () => { console.log(this); }; this.meotodoOutside = metodo; } const suma = (a, b) => { console.log(this); return a + b; }; let v1 = new Vehiculo(1, 2, 3, 4, 5, suma); v1; /* Vehiculo { color: 1, velocidad: 2, ruedas: 3, tipo: 4, motor: 5, metodoInside: () => { console.log(this); }, meotodoOutside: (a, b) => { console.log(this; return a + b; } */ v1.metodoInside(); // Vehiculo {color: 1, velocidad: 2, ruedas: 3, tipo: 4, motor: 5, …} v1.meotodoOutside(100, 100); // Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …} 200
En el código anterior hemos observado como tenemos dos métodos, uno definido dentro de la función constructora y otro que se pasa como parámetro.
En ambos métodos estamos mostrando por consola el contenido de this, que no es mas que la referencia al objeto actual.
Con esto terminados este tema, que aunque interesante ha sido algo más extenso de lo esperado.