Tutorial de Javascript y las mejoras desde ES6 (ES2015) hasta ES9 (ES2018) – 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:

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 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.