🎃 Grandes descuentos en libros en línea, eformaciones y vídeos*. Código CALABAZA30. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Vue.js
  3. Nociones esenciales de JavaScript
Extrait - Vue.js Desarrolle aplicaciones web modernas en JavaScript con un framework progresivo
Extractos del libro
Vue.js Desarrolle aplicaciones web modernas en JavaScript con un framework progresivo Volver a la página de compra del libro

Nociones esenciales de JavaScript

Introducción

Vue.js es un framework de JavaScript y, por lo tanto, se basa en este lenguaje. Es importante conocer los conceptos básicos de este lenguaje para comprender la sintaxis que se utiliza en los ejemplos de código de los siguientes capítulos.

El lenguaje JavaScript, lanzado por Netscape en 1995, permite que las páginas web reaccionen a las acciones del usuario en el lado del cliente.

Para poder implementarse en otros navegadores, se definieron varias especificaciones:

  • DOM (Document Object Model), estandarizado por W3C, es un API web que permite a los scripts analizar y manipular lenguajes de marcado como HTML o XML;

  • ECMAScript, establecido por Ecma International, es una especificación para lenguajes de tipo script. Lo utiliza el lenguaje JavaScript, así como ActionScript en particular.

Estas especificaciones están destinadas a ser comunes a todos los navegadores. Por lo tanto, siempre hay un retraso entre el lanzamiento de una nueva versión de ECMAScript y su implementación por parte de los editores en el intérprete de JavaScript de su navegador. Entre otras cosas, también por este motivo un gran número de elementos sintácticos eran específicos de cada navegador. Cuanto más se han estandarizado estas especificidades, más se han depreciado con el paso de las diferentes versiones del navegador, para favorecer las de la especificación ECMAScript. 

ECMAScript ha evolucionado mucho y sigue evolucionando muy rápidamente; ahora estamos en la versión 10 (ES10 - ES2019).

En la Web, a veces usamos el número de versión o de la edición para referirnos a...

Bases algorítmicas

1. Variables y tipos de valores

a. Declaración de una variable

Para definir una variable puede utilizar las palabras clave var, let o const. Una variable se puede inicializar asignándole un valor o el resultado de una expresión (operación, retorno de función), directamente durante la instrucción.

Si no se inicializa con un valor, está indefinida y tendrá el valor predeterminado undefined

// declaraciones de las variables a, b y c: 
 
var a; // "undefined" 
let b; // "undefined" 
const c; // "undefined" 
 
// inicialización de las variables e, f et g: 
 
var e = 1; 
let f = "hola"; 
const g = 1 + 0.45; 

En JavaScript, usamos // para comentar una línea de código y / * * / para comentar varias líneas.

A diferencia de los lenguajes fuertemente tipados, JavaScript usa tipado dinámico. En otras palabras, podemos asignar una variable con un valor de cierto tipo y después reasignarla con un valor de otro tipo, sin que se genere un error.

let a = 2; 
a = "hola"; 

También es posible declarar varias variables en una sola línea, separando cada variable con una coma:

let a = 1, b = 2, c = 3; 

b. Tipos de valores

En JavaScript, el valor de una variable puede ser de seis tipos primitivos diferentes. Diferenciamos entre tipos primitivos y tipos nativos de objetos, que tienen propiedades o métodos.

Lista de tipos primitivos en JavaScript:

  • string: una cadena de caracteres

let a = "hola"; 
  • number: un valor entero, decimal o el valor NaN (Not a Number) cuando una operación aritmética no puede devolver un número.

let a = 2; 
let b = 3.545; 
let c = 2 / 0; // NaN 
  • boolean:

let a = true; 
let b = false; 
  • object: puede ser un objeto declarado de manera literal, una instancia de un objeto o un objeto global. Hay varios objetos globales en JavaScript: Date, Math, String, Number, Boolean, JSON, window, document, etc. Los objetos globales asociados con un tipo de datos, también se denominan objetos de tipo nativo.

let a = {a: 4, b: "hola"}; // expresión literal 
let b = new Date(); // instanciación de un objeto a partir del objeto 
de tipo nativo Date() 
let c = null; 

El valor null también es de tipo...

Funciones

1. Definición y utilización de las funciones

Una función se utiliza para crear un contexto de ejecución fuera del contexto de ejecución global, tan pronto como se llama. Es decir, una función permite crear un bloque de instrucciones que se pueden ejecutar varias veces en varios lugares del código, con su propio contexto de ejecución.

Esto evita duplicar el mismo bloque de instrucciones utilizado en varios lugares de la aplicación.

Una función se declara con la instrucción de function, tiene una firma y puede tener múltiples argumentos de entrada.

Una función también puede devolver un valor al contexto de ejecución principal, con la instrucción return.

Para llamar a una función, todo lo que tiene que hacer es reutilizar su firma y pasarle tantos valores como argumentos haya en su declaración.

// instrucción de una función sin argumentos 
function miFuncion() { 
  console.log("función ejecutada"); 
} 
 
// declaración de una función con argumentos y un valor de retorno  
function sumar(num1, num2) { 
  return num1 + num2 
} 
 
// Llamadas 
miFuncion(); // muestra "función ejecutada" 
var suma = sumar(2, 3); 
console.log(suma); // 5  

Es posible declarar una función como una expresión. Más adelante se puede asignar a una variable, por lo que la función no tiene firma. A esto se le llama función anónima.

let muestra = function() { 
  console.log("función ejecutada"); 
}; 
 
muestra(); 

2. Cierres (closures)

Una función anidada dentro de otra función, se llama cierre (closure en inglés). Este es uno de los principios más interesantes del lenguaje JavaScript.

Es posible anidar en cascada varias funciones; también hablamos de encadenamiento de ámbito. Al igual que la diferencia entre el ámbito local de una función y el global (contexto de ejecución global), el ámbito de una función hija no está contenido en el ámbito de una función madre.

Las variables que se declaran en la función madre, así como las transmitidas como argumentos de...

Manipulación de arrays

1. Declarar, leer, modificar, eliminar elementos

Un array se utiliza para enumerar un conjunto de valores, de cualquier tipo, a los que se puede acceder a través de su índice, es decir, su posición en el array. El índice de un array siempre comienza en 0. En JavaScript, un array se corresponde con el objeto Array nativo y puede usar sus métodos para manipularlo.

Para crear un array, es posible utilizar tres sintaxis diferentes, pero equivalentes:

let tab = ["manzana", "banana", "naranja"]; 
let tab = Array("manzana", "banana", "naranja"); 
let tab = new Array("manzana", "banana", "naranja"); 

Para leer el elemento de un array, usamos el índice de su posición:

let elmt2 = tab[0]; // "manzana" 

Para averiguar el tamaño del array, es decir el número de elementos, usamos la propiedad length. Esto es equivalente al último índice del array + 1.

let tamanio = tab.length; // 3 

Podemos rellenar un array de varias formas:

  • especificando el índice al que se asigna un valor;

  • con el método push, que asigna un valor al final del array a un nuevo índice;

  • con el método unshift, que agrega uno o más elementos al comienzo de un array.

let tab = []; 
 
tab[0] = "manzana"; 
tab[1] = "banana"; 
tab[2] = "naranja"; ...

Manipulación de objetos

1. Definir un objeto y su prototipo

Un objeto es un conjunto dinámico de propiedades. Una propiedad es una pareja formada por un nombre único (también llamado clave) y un valor asociado a él. También hablamos de array asociativo. Puede asociar cualquier valor con una clave. Si asociamos una función a una tecla, llamamos a esta propiedad "método" y al resto de propiedades, "atributos".

let obj = { prop1: "valor", prop2: 42 }; 
console.log(obj); 

Resultado:

images/CAP2_PAG29.png

Cuando miramos el resultado del siguiente código en la consola, tenemos un objeto con tres propiedades: prop1, prop2 y __proto__.

__proto__ es una propiedad especial. Esta propiedad es, de hecho, un enlace a un objeto padre que llamamos prototipo.

Todos los objetos tienen este vínculo cuando se crean. Aquí, el objeto padre es de tipo Object y nuestro objeto hereda todos sus métodos.

Al intentar acceder a una propiedad, JavaScript busca primero esa propiedad en el objeto y si no puede encontrarla, accede al enlace y la busca en su prototipo. Si, nuevamente, no la encuentra en el prototipo, entonces busca en el prototipo del prototipo, y así sucesivamente.

Estos enlaces de un prototipo a otro forman lo que se conoce como la cadena de creación de prototipos. 

images/02EI02.png

Tomemos el ejemplo del método hasOwnProperty del prototipo de nuestro objeto. Este devuelve true si el valor pasado en el parámetro es un nombre de propiedad del objeto:

obj.hasOwnProperty('prop1'); // true 
obj.__proto__.hasOwnProperty('prop1') // false 
obj.__proto__.hasOwnProperty('hasOwnProperty') // true 

Por tanto, todos los objetos heredan en cascada los atributos y métodos de todos los prototipos de la cadena de creación de prototipos. Estrictamente hablando, este no es el mismo mecanismo de herencia que se encuentra en muchos lenguajes orientados a objetos. Se trata de enlaces entre objetos cuando, en la POO clásica (programación orientada a objetos), todos los atributos y métodos de una clase madre se copian en la clase hija.

2. Instanciar un objeto

Cuando instanciamos un objeto, asignamos un espacio en la memoria para almacenarlo. Para que el navegador pueda recuperar este objeto posteriormente en el lugar correcto de la memoria, este espacio tiene una referencia, es decir, una dirección...

Utilización de la palabra clave this

1. Fuera de una función

La palabra clave this se comporta en JavaScript de manera ligeramente diferente a como lo hace en otros lenguajes, donde designa al objeto en el que se usa.

En el contexto de ejecución global, además de cualquier función, this también designa el objeto window en un navegador y el objeto global si el código se ejecuta en el servidor (ejemplo con Node).

this === window; // true en un navegador 
this === global; // true en un servidor 

2. En una función llamada clásica

En una función, el valor de this depende de cómo se llame y puede ser diferente en cada llamada, según si se utiliza el método call() o apply().

En una llamada de función que no está en modo strict, this debe hacer referencia a un objeto, es decir, al objeto window predeterminado del navegador.

Con el modo estrict, si this no está definido su valor será undefined.

function test() { 
  return this; 
} 
 
// llamada simple sin modo strict 
test() === window; // true 
 
function test2() { 
  "use strict"; 
  return this; 
} 
 
// llamada simple con modo strict 
test2() === undefined; // true 

3. En una función llamada con call() y apply()

Los métodos call y apply, disponibles desde la versión...

Gestión de excepciones

1. La utilidad

Al desarrollar una aplicación, es posible que cometa errores debido a fallos de sintaxis o en el algoritmo (bucle infinito, llamadas a funciones recursivas infinitas o saturación de memoria, etc.). Durante la ejecución, JavaScript informará de estos errores en la consola. Cuando se muestra un error, también se dice que se ha lanzado una excepción. No es una buena idea manejar el lanzamiento de estas excepciones en el código. Es mejor reescribirlo para corregirlo.

Sin embargo, puede ser útil prever lanzar varias excepciones relacionadas con el uso del programa por parte del usuario (clics no deseados, demasiados archivos abiertos, querer procesar un volumen de datos demasiado grande, etc.) o relacionadas con el uso de servicios externos (solicitudes de API que no devolverían las respuestas esperadas, por ejemplo). Podríamos así mostrar un mensaje de advertencia o error al usuario.

2. La estructura try…catch…finally

Para lanzar una excepción, usamos la instrucción throw. Cuando JavaScript encuentra esta instrucción, detiene la ejecución del contexto actual y asciende por la pila de llamadas, de función en función, con sus diferentes ámbitos, buscando un bloque try. Luego lee el bloque de instrucción de la instrucción catch relacionada. La instrucción catch se llama como una función y recibe como argumento la variable manejada por la instrucción throw.

function miFuncion() { 
  let mensaje = "se ha producido un error "; 
  throw mensaje; // lanza una excepción 
  var x = 1; // código no leído 
} 
 
try { 
  miFuncion(); ...

Utilización de las promesas

1. Objeto nativo Promise

Una promesa designa al tipo de objeto nativo Promise. Permite encapsular una función que se ejecutará de forma asíncrona. Hasta que se inicie la ejecución del procesamiento asíncrono, el estado de la promesa está pendiente.

Al realizar este tratamiento, decimos que "consumimos" la promesa. El resultado puede ser la finalización o el fracaso de este tratamiento. Luego decimos que la promesa se cumple o se rompe.

Creamos una promesa con la instrucción new. El constructor Promise() recibe como argumento de entrada una función, que también se llama ejecutor.

El propio ejecutor recibe otras dos funciones como argumentos: resolve y reject.

new Promise (function (resolve, reject) {/ * ... * /}); 

La llamada a resolve o reject permite mantener o romper la promesa.

La función resolve(valor) finaliza el procesamiento asíncrono y llama al primer administrador del método then() (al que llamaremos "ifHold") con el valor llamado de resolución, que se le pasa como argumento.

La función reject(motivo) también termina el procesamiento asíncrono y llama al segundo administrador (que llamaremos "ifRejected") del método then(), con el motivo que le damos como argumento.

Cada administrador no es más que una función que luego se llamará en el método then().

2. Método then()

Para consumir una promesa, usamos el método then() del objeto Promise. Este método recibe dos funciones de callback como argumentos de entrada. La primera función representa al administrador, llamado por el método resolve() de la promesa, cuando se mantiene.

La segunda función de callback representa el controlador al que llama el método reject() de la promesa, cuando se rechaza.

const promesa = new Promise((resolve, reject) => { 
  resolve("tratamiento terminado"); 
  // reject("error"); 
}); 
 
promesa.then(ifHold); 
promesa.then(ifHold, ifRejected); 
 
promesa.then((valor) => { 
  // promesa mantenida 
  console.log(valor); // "tratamiento terminado" 
}, (raison) => { 
  // promesa rechazada 
  console.log(raison);...

Utilización de los módulos JavaScript

1. La historia de los módulos JavaScript

a. Introducción

A lo largo de los años, cuanto más potentes se han vuelto los dispositivos, computadoras, tabletas y teléfonos inteligentes, más aplicaciones web han incorporado la lógica de negocio del lado del cliente, en lugar del lado del servidor. El objetivo es acortar los tiempos de respuesta del usuario. Por lo tanto, han utilizado cada vez más código JavaScript, sobre todo a través de una multitud de librerías.

En el lado del cliente, esto ha dado lugar rápidamente al concepto de SPA (Single Page Application). Se trata de aplicaciones web de una sola página, que se comunican a menudo a través de llamadas API con los servidores, para optimizar la experiencia del usuario. Hoy en día, los frameworks JavaScript como Vue.js, React.js o Angular facilitan el desarrollo de este tipo de aplicación.

JavaScript también está invitado en el lado del servidor y se usa cada vez más con el framework Node.js, por ejemplo.

Por lo tanto, era necesario que el lenguaje se dotara de mecanismos para dividir y organizar el código JavaScript más fácilmente. El concepto de módulo ha solucionado este problema.

Un módulo permite encapsular varias funcionalidades en un bloque de software, también llamado "paquete". La idea es utilizar una interfaz unificada para importar un módulo a otros módulos, con el fin de utilizar sus funcionalidades; esto sirve para evitar que las variables y funciones de un módulo se carguen en el contexto de ejecución global. Cuando un módulo usa referencias de otros módulos, decimos que tiene dependencias. Normalmente se utiliza un archivo por módulo.

Los módulos de JavaScript han evolucionado mucho en los últimos diez años y ha surgido una serie de términos nuevos, sin que sea fácil saber exactamente qué significa cada uno y cuándo se necesita: CommonJS, AMD (Asynchronus Module Definition), RequireJS, Bower, Browserify, Webpack, Babel, Npm, NodeJS...

Hay algo sobre lo que es fácil confundirse. Es posible que tenga la oportunidad de leer código con instrucciones define, exports, module.exports o incluso require en el código...