Antes de empezar con las partes prácticas es necesario aclarar varios puntos que nos pondrán en situación de comprender mejor que podemos hacer con Javascript y sus peculiaridades.

¿Qué es Javascript?

Según la Wikipedia, la definición de Javascript sería:

JavaScript (abreviado comúnmente "JS") es un lenguaje de programación interpretado, dialecto del estándar ECMAScript. Se define como orientado a objetos, basado en prototipos, imperativo, débilmente tipado y dinámico.

De esta frase conviene aclarar algo. Si habéis hecho uso de otros lenguajes como Python o Java, también orientados a objetos, estaréis familiarizados en el uso de clases para la definición de objetos. Sin embargo, Javascript no hace uso de clases. En su lugar, utiliza lo que se conoce como programación basada en prototipos.

Programación basada en Prototipos

En Javascript simplemente tenemos objetos que heredan de otros objetos. No hay clases ni las necesitamos ya que simplemente podemos instanciar objetos de forma dinámica. La herencia entre objetos se consigue haciendo uso de la cadena de prototipos, haciendo que un objeto delegue en otro para aquellos métodos o atributos que no posee él mismo.

Esto se logra mediante el uso de funciones a las cuales le indicamos un prototipo (un objeto padre) al que enlazaran al ser llamadas.

Como suena todo demasiado teórico lo explicaré con un ejemplo.

Tenemos un objeto animal con el atributo name y el método eat. Para definirlo basta con instanciar un objeto con sus propiedades de forma dinámica (sin clases).

const animal = {  
  name: 'Animal', 
  eat() { console.log('Eating') }
};

animal es demasiado básico, necesitamos ahora definir un objeto dog que haga algo mas y que herede de animal. Para ello usaremos el método Object.create introducido en ES5. Object.create toma un objeto como parámetro y se lo asigna como prototipo al objeto que devuelve (no me pararé a explicar como funciona ya que lo veremos mas adelante).

const dog = Object.create(animal);  
dog.bark = function() { console.log('Guau!') };

dog.eat()   // Eating  
dog.bark()  // Guau!  

Un solo objeto dog no nos vale ya que necesitamos varios, cada uno con un nombre distinto. Para ello ahora usaremos el objeto dog como prototipo.

const max = Object.create(dog);  
max.name = 'Max';

const tom = Object.create(dog);  
tom.name = 'Tom';

max.bark() // Guau!  
tom.eat()  // Eating!  

Si inspeccionáramos con alguna utilidad (utils.inspect en Node por ejemplo) los objetos tom y max veríamos que ninguno de los dos posee los métodos a los que hemos llamado. Sin embargo podemos llamarlos gracias a la herencia, que irá ascendiendo en la cadena de prototipos (ancestros o padres) hasta encontrar la propiedad deseada.

max > dog.bark()  
tom > dog > animal.eat()  

Más adelante explicaremos en detalle la herencia y la composición de objetos.

Todo es un objeto

Una de las peculiaridades de Javascript es que todo funciona como un objeto. Incluso las funciones son objetos con sus propios métodos y atributos. Hemos explicado la herencia a través de prototipos, la cual se consigue de hecho gracias a que las funciones constructoras se les puede asignar un prototipo.

¿Qué mas conseguimos con funciones que son objetos? Pues podemos asignarlas a variables y pasárselas a otras funciones como argumentos.

const doSomething = function() { console.log('doing something!') };

function final(myFunction) {  
  myFunction();
}

final(doSomething) // 'doing something!'  

Con esto obtenemos otro de los pilares en los que se apoya Javascript: la programación funcional.

Programación funcional

Si recurrimos de nuevo a la Wikipedia extraemos esta definición:

En ciencias de la computación, la programación funcional es un paradigma de programación declarativa basado en la utilización de funciones aritméticas que no maneja datos mutables o de estado. Enfatiza la aplicación de funciones, en contraste con el estilo de programación imperativa, que enfatiza los cambios de estado.

De forma mas informal y resumida podríamos decir que la programación funcional se basa en el uso de funciones que devuelven valores en lugar del almacenamiento y mutación de los mismos. Un ejemplo.

Si usáramos un estilo imperativo para modificar nuestros objetos haríamos esto:

const dog = { specie: 'Dog' };  
dog.name = 'Tom';  
console.log(dog.name) // 'Tom'  

Con esto mutamos el estado de un objeto para cambiarlo. En cambio, en programación funcional se intenta no manejar estados ni almacenar valores. Podríamos conseguir lo mismo de la siguiente forma.

function dog(name) {  
  return { name, specie: 'Dog' };
}

console.log( dog('Max').name ) // 'Max'  

Gracias a esto podemos crear funciones recursivas (funciones que se llaman a sí mismas) ó funciones de orden superior (funciones que aceptan como parámetro o devuelven otras funciones). Un ejemplo común de recursividad es calcular el factorial de un número.

function factorial(n) {  
  if (n > 1) {
    return n * factorial(n-1);
  } else {
    return 1;
  }
}

console.log( factorial(3) ) // 6  
console.log( factorial(4) ) // 24  

En posteriores capítulos de esta serie veremos en detalle que son las funciones de orden superior y primera clase, y cómo usarlas. Pero antes comentaré un último punto que nos aporta el lenguaje y que está en estrecha relación con la programación funcional.

Orientación a eventos

Si recurrimos de nuevo a la Wikipedia (última vez, lo prometo ;P) extraemos lo siguiente:

La programación dirigida por eventos es un paradigma de programación en el que tanto la estructura como la ejecución de los programas van determinados por los sucesos que ocurran en el sistema, definidos por el usuario o que ellos mismos provoquen.

Básicamente, definimos acciones que se ejecutan en base a eventos y de forma no secuencial. Para explicarlo mejor pondremos un ejemplo sencillo y común: asignar una acción a un botón en una web.

Tenemos un objeto dog con un método feed (acción).

const dog = { feed(){ console.log('The dog is eating') } };  

Queremos que dicha acción se ejecute cuando el usuario haga click (evento) en el botón feedingButton de un formulario. Por tanto definimos el botón y le asignamos dicha tarea.

const feedingButton = document.createElement('button');  
feedingButton.addEventListener('click', () => dog.feed());  

Lo que hemos hecho ha sido definir una función anónima (sin nombre, y usando las arrow function de ES6) y pasársela como callback (función que se ejecutan despues de finalizar una tarea) al método addEventListener. Éste método se encarga de estar a la espera del evento que queremos y ejecutar dicha función solo cuando se produzca.

Usuario > click > dog.feed() // 'The dog is eating'  

Esto es lo que se conoce como programación asìncrona, la ejecución del código se produce mediante callbacks y no en el orden que lo escribimos. Indagaremos más en ello en posteriores entregas de esta serie.

Conclusión

Javascript puede resultar sencillo para los que se inician en la programación con este lenguaje, sin embargo posee cualidades de alto nivel que lo hacen muy rico y flexible sin que tenga nada que envidiar a otros lenguajes como Python, Java o C#, ya que gracias Node y la comunidad que hay detrás ya no está solo orientado a la web.

Hoy en día la cantidad de documentación y tutoriales que podemos encontrar es inmensa, así que el enfoque que usaré no será el de un tutorial sobre el lenguaje sino el de indagar en él con casos prácticos y explicar soluciones comunes con el uso de patrones de diseño, así como en el uso de buenas prácticas. Más adelante comentaré el uso de algunos frameworks, tanto para la parte web como para Node.

"¡Sigue el camino de la baldosas amarillas!" - The Wizard of Oz (1939)