├── manuscript ├── 11_modulos.md ├── 00_Introduccion.md ├── 10_colecciones_de_clave_unica.md ├── Book.txt ├── 12_Proxies.md ├── apendice_1_recursos_y_bibliografia.md ├── 08_Simbolos.md ├── 13_Promesas.md ├── 09_Literales_de_objetos_mejorados.md ├── 09_iteradores_y_generadores.md ├── 07_Clases.md ├── 14_Reflection.md ├── 05_Arrow_functions_y_ambito_lexico.md ├── 03_Plantillas_de_cadenas_de_texto.md ├── 01_Instalacion_y_uso_de_Babel.md ├── 15_Decoradores.md ├── 06_Conceptos_de_programacion_orientada_a_objetos_en_javascript.md ├── 04_Desestructuracion_de_arrays_y_objetos.md └── 02_Variables_de_ambito_de_bloque_y_constantes.md ├── .gitignore └── README.md /manuscript/11_modulos.md: -------------------------------------------------------------------------------- 1 | # Módulos 2 | -------------------------------------------------------------------------------- /manuscript/00_Introduccion.md: -------------------------------------------------------------------------------- 1 | # Introducción a ECMAScript 2015 (ES6) -------------------------------------------------------------------------------- /manuscript/10_colecciones_de_clave_unica.md: -------------------------------------------------------------------------------- 1 | # Colecciones de clave única 2 | 3 | ## Map 4 | 5 | ## Set 6 | 7 | ## WeakMap 8 | 9 | ## WeakSet 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /manuscript/Book.txt: -------------------------------------------------------------------------------- 1 | 00_Introduccion.md 2 | 01_Instalacion_y_uso_de_Babel.md 3 | 02_Variables_de_ambito_de_bloque.md 4 | 03_Plantillas_de_cadenas_de_texto.md 5 | 04_Desestructuracion_de_arrays_y_objetos.md 6 | 05_Arrow_functions_y_ambito_lexico.md 7 | 06_Conceptos_de_programacion_orientada_a_objetos_en_javascript.md 8 | 07_Clases.md 9 | 08_Simbolos.md 10 | 09_Literales_de_objetos_mejorados.md 11 | apendice_1_recursos_y_bibliografia.md -------------------------------------------------------------------------------- /manuscript/12_Proxies.md: -------------------------------------------------------------------------------- 1 | # Proxies 2 | Los proxies te permiten interceptar y personalizar operaciones realizadas en los objetos. Son una característica de _metaprogramación_. 3 | 4 | En el siguiente ejemplo, `proxy` es el objeto cuyas operaciones estamos interceptando y el `handler` es el objeto que maneja dichas intercepciones. En este caso, solamente estamos interceptando una única operación: `get`: 5 | 6 | ```javascript 7 | const target {}; 8 | const handler = { 9 | get(target, propKey, receiver) { 10 | console.log(`get ${propKey}`); 11 | return 'hello'; 12 | } 13 | } 14 | 15 | const proxy = new Proxy(target, handler); 16 | 17 | console.log(proxy.foo); 18 | // get foo 19 | // hello 20 | ``` -------------------------------------------------------------------------------- /manuscript/apendice_1_recursos_y_bibliografia.md: -------------------------------------------------------------------------------- 1 | # Apéndice 1: Recursos y bibliografía 2 | 3 | * [JavaScript constructors, prototypes, and the `new` keyword, by Pivotal Labs](https://blog.pivotal.io/labs/labs/javascript-constructors-prototypes-and-the-new-keyword) 4 | * [You Don't Know JS: ES6 & Beyond, by Kyle Simpson](https://github.com/getify/You-Dont-Know-JS/tree/master/es6%20%26%20beyond) 5 | * [Exploring ES6: Upgrade to the next version of JavaScript, by Dr. Axel Rauschmayer](http://exploringjs.com/) 6 | * [Introduction to ES6 Promises – The Four Functions You Need To Avoid Callback Hell, by James K. Nelson](http://jamesknelson.com/grokking-es6-promises-the-four-functions-you-need-to-avoid-callback-hell/#exercise) 7 | * [Understanding Scope and Context in JavaScript, by Ryan Morr](http://ryanmorr.com/understanding-scope-and-context-in-javascript/) 8 | -------------------------------------------------------------------------------- /manuscript/08_Simbolos.md: -------------------------------------------------------------------------------- 1 | # Símbolos en ECMAScript 2015 2 | 3 | Los símbolos son uno tipo primitivo en ECMAScript 2015. Un símbolo es un tipo de dato **único** e **inmutable** que puede ser utilizado como identificador para propiedades de objetos. 4 | 5 | ## Sintaxis 6 | 7 | ```javascript 8 | Symbol([description]); 9 | ``` 10 | ### Parámetros ### 11 | #### description `optional` #### 12 | De tipo string, opcional. Una descripción del símbolo que puede ser utilizada para depurar, pero no para acceder al propio símbolo. 13 | 14 | ## Uso 15 | Para crear un nuevo símbolo primitivo, simplemente escribimos `Symbol()` con un parámetro opcional de tipo string como cadena de texto a modo de descripción. 16 | 17 | ```javascript 18 | var symbol = Symbol(); 19 | var anotherSymbol = Symbol("another"); 20 | ``` 21 | 22 | El hecho de utilizar una descripción para la creación un símbolo, no significa que el símbolo creado contenga ese string. Se crea un símbolo nuevo cada vez, aunque se utilice la misma descripción. De este modo, dos símbolos con la misma descripción no son iguales: 23 | 24 | ```javascript 25 | Symbol("foo") === Symbol("foo"); //false 26 | ``` 27 | 28 | Tampoco tiene sentido utilizar la palabra reservada `new` para crear un símbolo: 29 | 30 | ```javascript 31 | var symbol = new Symbol(); // TypeError 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Aprende ECMAScript 2015 (ES6) 2 | ======= 3 | Comienzo este proyecto con ilusión, como tantas otras cosas en mi vida que he terminado dejando aparcadas por falta de motivación o de tiempo. Espero que este no sea el caso, ya que me gustaría al menos poder completar los 19 capítulos que tengo pensados. 4 | 5 | Y soy optimista al respecto, ya que lo voy a tener que hacer *por narices*, al encontrarme durante estos días preparando un curso para [Escuela IT](http://escuela.it/) sobre [ECMAScript 2015](http://www.ecma-international.org/ecma-262/6.0/). Cuando me enteré de que iba a tener la oportunidad de hacerlo, pensé que era una ocasión única para documentarme a fondo sobre el nuevo estándar. Hasta entonces sólo había ido aprendiendo las características a medida que las había ido necesitando, sin profundizar demasiado en ninguna. Y ya que lo hacía, podía aprovechar para dejar todo lo que he aprendido durante estos meses trabajando con JavaScript en un manual completo, en castellano y con ejemplos prácticos y concisos. 6 | 7 | Ese al menos es mi objetivo al comenzar este libro. No sé si lo terminaré. No sé si tendrá la calidad suficiente para servirle a alguien, pero sí tengo claro que pocas veces voy a tener una oportunidad como esta para forzarme a escribir algo así, ya que lo voy a tener que hacer sí o sí para el curso. Así que, ¡Habrá que aprovecharlo! 8 | 9 | Si estás leyendo esto antes de terminar el libro, espero que disfrutes con su evolución. Y si decides ayudarme, ¡bienvenida sea! 10 | -------------------------------------------------------------------------------- /manuscript/13_Promesas.md: -------------------------------------------------------------------------------- 1 | # Promesas 2 | > Un objeto **Promise** representa un valor que puede no estar disponible aún. 3 | 4 | El objeto **Promise** se utiliza para realizar operaciones asíncronas. Típicamente, llamadas a sistemas remotos o a procesos que pueden demorarse y bloquear la ejecución de nuestro código. 5 | 6 | La promesa representa un valor que no es necesario conocer en el momento en el que dicha promesa es creada. Nos permite asociar un *handler* a la acción asíncrona que eventualmente puede completarse con éxito o fallar por cualquier motivo. De este modo, los métodos asíncronos pueden retornar valores como si fuesen síncronos. De ahí el nombre de promesa: son métodos que *prometen* devolver un valor en algún momento del futuro. 7 | 8 | ## Sintaxis 9 | 10 | ```javascript 11 | new Promise(function(resolve, reject) { ... }); 12 | ``` 13 | 14 | El constructor de la promesa recibe una función de *callback*, que a su vez contiene dos argumentos que podremos llamar una vez que la operación se haya completado: 15 | 16 | * **`resolve`**: Completa la promesa devolviendo el valor. 17 | * **`reject`**: Rechaza la promesa. 18 | 19 | Internamente una Promesa puede estar en uno de los siguientes tres estados: 20 | 21 | * *pending*: El estado inicial, ni completada ni rechazada. 22 | * *fullfilled*: Significa que la operación se ha completado con éxito. 23 | * *rejected*: Significa que la operación ha fallado. 24 | 25 | ## Utilidad del uso de Promise 26 | Las promesas son una forma fantástica de hacer tu código más limpio, reducir dependencias de librerías externas y simplificar el conocido *callback hell*, con el que tenemos que lidiar los que programamos en JavaScript tanto en lado del cliente como en servidor. 27 | 28 | Para demostrar el problema con los *callbacks*, probaremos lo siguiente: 29 | 1. Ejecutar un código 30 | 2. Esperar un segundo 31 | 3. Ejecutar otro código 32 | 4. Esperar otro segundo 33 | 5. Ejecutar un tercer fragmento de código 34 | 35 | Este es el patrón que normalmente utilizaríamos para hacer animaciones con CSS3. Lo implementaremos utilizando `setTimeout`, a modo ilustrativo: 36 | 37 | ```JavaScript 38 | runAnimation(0); 39 | setTimeout(function() { 40 | runAnimation(1); 41 | setTimeout(function() { 42 | runAnimation(2); 43 | }, 1000) 44 | }, 1000); 45 | ``` 46 | Parece horrible, ¿verdad? Imagina una aplicación que requiere animaciones complejas, con 10 pasos en lugar de 3. La anidación de funciones de *callback* convertiría tu código en algo completamente ilegible y difícil de debugar. De hecho, es un problema tan peliagudo que se ha popularizado el uso de *callback hell* (el infierno de los *callbacks*) para definirlo. Las promesas vienen a poner fin a este infierno. 47 | 48 | 49 | 50 | TODO: Completar con información de: 51 | * https://www.promisejs.org/ 52 | * http://jamesknelson.com/grokking-es6-promises-the-four-functions-you-need-to-avoid-callback-hell/#exercise 53 | -------------------------------------------------------------------------------- /manuscript/09_Literales_de_objetos_mejorados.md: -------------------------------------------------------------------------------- 1 | # Mejoras en literales de objetos 2 | 3 | Los "literales de objetos", u *object literals* en JavaScript se definen como una lista de pares de clave valor rodeados por corchetes: 4 | 5 | ```javascript 6 | var myObject = { 7 | name: 'Daniel', 8 | age: 32, 9 | married: true 10 | } 11 | ``` 12 | En lenguaje coloquial, a mi me gusta llamar a este conjunto simplemente un *plain object*, u objeto plano y a los *object literals*, propiedades. Pero no se lo digas a nadie ;-) 13 | 14 | Los *object literals* pueden ser de cualquier tipo de datos, incluyendo arrays, funciones e incluso otros *object literals* anidados. He aquí otro ejemplo un tanto más complejo. 15 | 16 | ```javascript 17 | var mySuperCoolObject = { 18 | images: ["one.gif", "two.gif", "three.gif", "four.gif"], 19 | location: { 20 | x: 40.8374, 21 | y: 300.1771 22 | }, 23 | move: function(x, y) { 24 | this.location.x += x; 25 | this.location.y += y; 26 | } 27 | }; 28 | ``` 29 | 30 | ## Novedades en ECMAScript 6 31 | En Javascript, los métodos no son más que propiedades cuyos valores son funciones. 32 | 33 | En ES5, los métodos son creados del mismo modo que cualquier otra propiedad, utilizando un literal de objeto: 34 | 35 | ```javascript 36 | // ES5 37 | var myObject = { 38 | foo: function() { 39 | ... 40 | } 41 | } 42 | ``` 43 | 44 | En ECMAScript 6 se ha introducido una sintaxis especial para definir métodos: 45 | 46 | ```javascript 47 | // ES6 48 | const myObject = { 49 | foo() { 50 | ... 51 | }, 52 | bar(x, y) { 53 | ... 54 | } 55 | } 56 | ``` 57 | 58 | ### Nombres de propiedades dinámicos o computados 59 | En ECMAScript 5 solo teníamos una forma de especificar una clave cuando creábamos una propiedad: mediante un nombre fijo: 60 | 61 | ```javascript 62 | // ES5 63 | object.foo = true; 64 | ``` 65 | 66 | En ES6 también podemos hacerlo de forma dinámica, o computada: 67 | 68 | ```javascript 69 | // ES6 70 | const propertyKey = 'foo'; 71 | const object = { 72 | [propertyKey]: true, 73 | [`b${ar}`]: 123 74 | }; 75 | ``` 76 | 77 | Esta nueva sintaxis también sirve para definiciones de métodos: 78 | 79 | ```javascript 80 | // ES6 81 | const methodKey = 'greeting'; 82 | const object = { 83 | [methodKey]() { 84 | return 'Hello!'; 85 | } 86 | }; 87 | ``` 88 | 89 | ### Getters y setters 90 | Los `getters` y `setters` continúan funcionando exactamente igual que en ES5, aunque con la nueva sintaxis han terminado siendo prácticamente iguales que las definiciones de métodos: 91 | 92 | ```javascript 93 | const object = { 94 | get foo(){ 95 | console.log('GET foo'); 96 | return 123; 97 | } 98 | set bar(value){ 99 | console.log(`SET bar to ${value}`); 100 | } 101 | } 102 | ``` 103 | 104 | ### Atajos de asignación de valores 105 | Los atajos de valores en las propiedades nos permiten abreviar la definición de una propiedad en un literal de objeto. Si el nombre de la variable que especifica el valor de la propiedad coincide con el de la clave de la propiedad, podemos **omitir la clave**: 106 | 107 | ```javascript 108 | // ES6 109 | const x = 4; 110 | const y = 1; 111 | const object = { x, y }; 112 | ``` 113 | Lo anterior sería equivalente a: 114 | 115 | ```javascript 116 | // ES6 117 | const object = { x: x, y: y }; 118 | ``` 119 | 120 | Si unimos estos atajos para la asignación de valores a propiedades, tenemos una de las combinaciones más potentes de ECMAScript 2015: 121 | 122 | ```javascript 123 | const object = { x: 4, y: 1 }; 124 | const {x, y} = object; 125 | console.log(x); // 4 126 | console.log(y); // 1 127 | ``` 128 | -------------------------------------------------------------------------------- /manuscript/09_iteradores_y_generadores.md: -------------------------------------------------------------------------------- 1 | # Iteradores y generadores 2 | 3 | Los iteradores son una solución que propone ECMAScript 2015 para recorrer colecciones de objetos de forma ordenada. Los generadores son un nuevo tipo de función que puede devolver más de un valor. Una vez devuelto un valor por primera vez, podemos volver a llamarla y continuará ejecutándose en el mismo punto en que la dejamos. Un generador, por tanto, conserva el contexto de ejecución. 4 | 5 | En este capítulo veremos cómo utilizar ambas características. 6 | 7 | ## Iteradores 8 | Si has programado algo en JavaScript, es muy probable que hayas terminado haciendo algo como esto: 9 | 10 | ```JavaScript 11 | var colors = [ 'red', 'green', 'blue' ]; 12 | 13 | for (var i = 0; i < colors.length; i++) { 14 | console.log(colors[i]); 15 | } 16 | ``` 17 | El código anterior no es más que un bucle estándar que utiliza la variable `i` para mantener un cursor sobre el índice del array que se está recorriendo. Aunque es un ejemplo bastante sencillo, la complejidad aumenta cuando tenemos que crear bucles anidados. 18 | 19 | Los **iteradores** no son más que objetos con una interfaz específica. La interfaz consiste en un método `next()` que devuelve un objeto con un resultado. Dicho objeto, contiene dos variables: `value`, que representa el valor siguiente; y `done`, el cual solo valdrá `true` cuando no haya más valores que devolver. 20 | 21 | Veamos un ejemplo de iterador: 22 | 23 | ```javascript 24 | function makeCountDown(counter) { 25 | let _counter = counter; 26 | return { 27 | next: function() { 28 | const value = _counter--; 29 | const done = _counter <= 0; 30 | 31 | return { 32 | value, 33 | done 34 | }; 35 | } 36 | } 37 | } 38 | 39 | const countdown = makeCountDown(5); 40 | console.log(countdown.next()); // { value: 5, done: false } 41 | console.log(countdown.next()); // { value: 4, done: false } 42 | console.log(countdown.next()); // { value: 3, done: false } 43 | console.log(countdown.next()); // { value: 2, done: false } 44 | console.log(countdown.next()); // { value: 1, done: true } 45 | console.log(countdown.next()); // { value: 0, done: true } 46 | ``` 47 | 48 | ## Sintaxis de un generador 49 | 50 | Un generador es un tipo especial que funciona como factoría para iteradores. Una función se convierte en un generador si contiene una o más expresiones `yield` (producir) y si utiliza la sintaxis `function*` en su declaración. 51 | 52 | ```javascript 53 | const enumerator = function* () { 54 | let index = 0; 55 | while(true) 56 | yield index++; 57 | }; 58 | 59 | const enumGenerator = enumerator(); 60 | 61 | console.log(enumGenerator.next().value); // 0 62 | console.log(enumGenerator.next().value); // 1 63 | console.log(enumGenerator.next().value); // 2 64 | ``` 65 | Los generadores computan sus valores *producidos* bajo demanda, lo cual les permite representar secuencias de valores que son caras , computacionalmente hablando. O incluso infinitas, como se ha demostrado en el ejemplo anterior. 66 | 67 | El método `next()` también permite un valor que puede ser utilizado para modificar el estado interno del generador. Un valor pasado por parámetro a `next()` será tratado como el resultado de la última expresión `yield` que pausó el generador. 68 | 69 | Modifiquemos un poco el enumerador del ejemplo anterior para que sea reseteable: 70 | 71 | ```javascript 72 | const enumerator = function*() { 73 | let index = 0; 74 | while(true) { 75 | let reset = yield index++; 76 | if(reset) 77 | index = 0; 78 | } 79 | } 80 | 81 | const enumGenerator = enumerator(); 82 | 83 | console.log(enumGenerator.next().value); // 0 84 | console.log(enumGenerator.next().value); // 1 85 | console.log(enumGenerator.next().value); // 2 86 | console.log(enumGenerator.next().value); // 3 87 | console.log(enumGenerator.next().value); // 4 88 | console.log(enumGenerator.next(true).value); // 0 89 | console.log(enumGenerator.next().value); // 1 90 | ``` 91 | -------------------------------------------------------------------------------- /manuscript/07_Clases.md: -------------------------------------------------------------------------------- 1 | # Clases 2 | 3 | El paradigma de orientación a objetos es algo que siempre ha generado gran controversia entre la comunidad de desarrolladores Javascript. Si bien muchos defienden la programación funcional reactiva más pura, lo cierto es que Javascript siempre se ha definido como un lenguaje de programación orientado a objetos, así que ambas abstracciones son válidas. 4 | 5 | A día de hoy, con la introducción de ECMAScript 2015 la controversia sigue vigente, sobre todo desde la introducción de la palabra reservada `class` en el lenguaje. Toda una declaración de intenciones. 6 | 7 | Y no es justo ahora vayamos a poder utilizar clases en Javascript: ya era algo que podíamos hacer en versiones anteriores del estándar. Pero con una sintaxis mucho menos intuitiva. 8 | 9 | En este capítulo nos detendremos a refrescar algunos conceptos que ya eran vigentes en ECMAScript 5, antes de ver cómo creamos las mismas estructuras de código en la versión 6 del estándar. 10 | 11 | ## Sintaxis básica de declaración de clases 12 | El siguiente fragmento de código ilustra cómo definiríamos una clase en JavaScript. 13 | 14 | ```javascript 15 | class Point { 16 | constructor(x, y) { 17 | this.x = x; 18 | this.y = y; 19 | } 20 | toString() { 21 | return `(${this.x}, ${this.y})`; 22 | } 23 | } 24 | ``` 25 | 26 | Esta definición de clase nos permite utilizarla como cualquier otra función constructor en ES5: 27 | 28 | ```javascript 29 | var p = new Point(25, 8); 30 | p.toString(); //(25, 8) 31 | ``` 32 | 33 | Si obtienes el tipo de la clase `Point`, te darás cuenta de que es no es más que una función. 34 | 35 | ```javascript 36 | typeof Point; // function 37 | ``` 38 | 39 | A diferencia de las funciones constructoras, no podemos utilizarla directamente. En su lugar, tenemos que crear una instancia utilizando `new`: 40 | 41 | ```javascript 42 | Point(); 43 | > TypeError: Classes can’t be function-called 44 | ``` 45 | 46 | ## Sintaxis de declaración de subclases 47 | La forma de hacer subclases sería la siguiente: 48 | 49 | ```javascript 50 | class ColorPoint extends Point { 51 | constructor(x, y, color) { 52 | super(x, y); 53 | this.color = color; 54 | } 55 | toString() { 56 | return `${super.toString()} in ${this.color}`; 57 | } 58 | } 59 | 60 | const cp = new ColorPoint(25, 8, 'green'); 61 | cp.toString(); // (25, 8) in green 62 | cp instanceof ColorPoint // true 63 | cp instanceof Point // true 64 | ``` 65 | La "herencia" de propiedades de la clase se realiza mediante la palabra reservada `extends`. Esto provoca que el nuevo objeto instanciado sea tanto del tipo asignado como del tipo heredado. 66 | 67 | En el constructor, mediante la sentencia `super()` se llama a la función constructora de la clase padre. 68 | 69 | ## El cuerpo de una definición de clase 70 | El cuerpo de una clase solo puede contener métodos. Los prototipos que tienen propiedades contenedoras de datos generalmente se consideran un antipatrón, así que esta particularidad de las clases solo refuerza que se siga esta buena práctica. 71 | 72 | Los tipos de métodos que puede contener el cuerpo de una función son: 73 | 74 | * constructor 75 | * métodos estáticos 76 | * métodos de prototipo 77 | 78 | ```javascript 79 | class Sample{ 80 | constructor(value){ 81 | this.value = value; 82 | } 83 | static staticMethod(){ 84 | return 'this is a class method'; 85 | } 86 | prototypeMethod(){ 87 | return 'this is a prototype method'; 88 | } 89 | } 90 | 91 | const sample = new Sample(123); 92 | ``` 93 | 94 | ### Método constructor de clase 95 | No es más que una función constructora, tal y como ya existían en ES5. Sin embargo, el constructor de una clase tiene la particularidad de poder llamar a `super`, lo cual nos da la ventaja de poder inicializar las propiedades de la clase padre (o "superclase"). Algo que no era posible con el estándar anterior del lenguaje. 96 | 97 | ```javascript 98 | // ES6 99 | class Example extends Sample { 100 | constructor(key, value){ 101 | super(value); 102 | this.key = key; 103 | } 104 | } 105 | 106 | const example = new Example(987, 123); 107 | example// Example { value: 123, key: 987 } 108 | ``` 109 | 110 | ### Métodos estáticos 111 | Las propiedades estáticas, o propiedades de clase, son propiedades de la propia clase, por lo tanto no necesitan de una instancia para ser utilizados, a diferencia de los métodos de prototipo: 112 | 113 | ```javascript 114 | Sample.staticMethod(); // this is a class method 115 | Sample.prototypeMethod();// TypeError: Sample.prototypeMethod is not a function 116 | ``` 117 | 118 | ### Métodos de prototipo 119 | En contraposición a los métodos estáticos, los métodos de prototipo son heredados por todas las instancias de la clase: 120 | 121 | ```javascript 122 | const sample = new Sample(); 123 | sample.prototypeMethod(); // this is a prototype method 124 | sample.staticMethod(); //TypeError: sample.staticMethod is not a function 125 | ``` 126 | 127 | Fíjate que los métodos estáticos no se transmiten a las instancias de la clase. -------------------------------------------------------------------------------- /manuscript/14_Reflection.md: -------------------------------------------------------------------------------- 1 | > Work in progress. Check: https://github.com/tvcutsem/harmony-reflect/wiki 2 | 3 | # Reflection 4 | 5 | *Reflect* es un nuevo objeto que proporciona métodos para interceptar operaciones de JavaScript. A diferencia de la mayoría de objetos globales, *Reflect* no es una función constructora. No es posible utilizarlo con el operador `new`o invocarlo como una función. Todas las propiedades y métodos de *Reflect* son estáticas (igual que en el objeto *Math*, por ejemplo). 6 | 7 | La técnica de la *reflexión* es casi tan antigua como las ciencias de computación. Los primeros ordenadores estaban programados en **lenguaje de ensamblador**, el cual es reflexivo de forma inherente. Ha permanecido vigente hasta hoy en día, con estandartes como Java o C# haciendo gala de su uso; si bien no todos los lenguajes de programación han encontrado la necesidad de implementarla. 8 | 9 | En ECMAScript 5 también era posible utilizar esta técnica, mediante la evaluación de funciones o utilizando algunos de los métodos proporcionados por `Object` o `Function`. Ahora, gracias a ECMAScript 2015, disponemos de una API más específica, flexible y segura a tal efecto. 10 | 11 | Consiste en la capacidad de un programa de **modificar su estructura y comportamiento en tiempo de ejecución**. 12 | 13 | ## Por qué usar `Reflect` ## 14 | 15 | ### Valores de retorno más útiles ### 16 | Los métodos de `Reflect` son muy similares a los que podemos encontrar llamando a `Object`, pero proporcionan un resultado más significativo. 17 | 18 | ```JavaScript 19 | // ES 5 20 | try { 21 | Object.defineProperty(obj, name, desc); 22 | // property defined successfully 23 | } catch (e) { 24 | // possible failure (and might accidentally catch the wrong exception) 25 | } 26 | ``` 27 | El código anterior era válido en ES5 para definir una propiedad dentro de un objeto, sin embargo podía devolver varioes tipos de error si algo no iba bien, que nos obligaban a gestionar con bloques `try-catch`. 28 | 29 | Con ES2015, podemos refactorizar el código anterior en lo siguiente: 30 | 31 | ```JavaScript 32 | if(Reflect.defineProperty(obj, name, desc)) { 33 | // success 34 | } else { 35 | // failure 36 | } 37 | ``` 38 | Al devolver un booleano, nos libramos de la gestión de errores y podemos actuar en consecuencia si la propiedad que se ha intentado definir no ha sido creada. 39 | 40 | ### Aplicación de funciones más fiable ### 41 | En ES5, cuando queríamos llamar a una función `f` con un número de argumentos variable vinculando el valor de `this` a un objeto, podíamos utilizar: 42 | 43 | ```JavaScript 44 | f.apply(obj, args); 45 | ``` 46 | 47 | Sin embargo, `f` podría ser un objeto en el que alguien ya hubiese definido un método `apply`. Si queremos asegurarnos de que vamos a llamar al método `apply` proporcionado por el lenguaje, hasta ahora podíamos hacerlo de la siguiente forma: 48 | 49 | ```JavaScript 50 | // ES 5 51 | Function.prototype.apply.call(f, obj, args); 52 | ``` 53 | 54 | Con ECMAScript 2015 tenemos una alternativa igual de fiable y más fácil de llamar: 55 | 56 | ```JavaScript 57 | // ES 6 58 | Reflect.apply(f, obj, args); 59 | ``` 60 | 61 | ### Constructores con un número arbitrario de argumentos 62 | Si queremos llamar a un constructor con un número arbitrario de argumentos, en ES6, gracias a la sintaxis del operador de propagación, podemos hacerlo de la siguiente manera: 63 | 64 | ```JavaScript 65 | var obj = new F(...args); 66 | ``` 67 | 68 | En ES5 esto es más complicado de escribir, ya que tan solo podríamos utilizar `F.apply` o `F.call` para llamar a una función con un número variable de argumentos. Pero no existe una función `F.construct` para instanciar la función constructora con un número arbitrario de parámetros. Con `Reflect`, ahora podemos: 69 | 70 | ```JavaScript 71 | var obj = Reflect.construct(F, args); 72 | ``` 73 | 74 | Veámoslos en detalle: 75 | 76 | ## `Reflect.apply()` ## 77 | 78 | Llama a una función de destino cuyos argumentos se especifican utilizando el parámetro `argumentsList`. 79 | 80 | ### Sintaxis 81 | 82 | ```JavaScript 83 | Reflect.apply(target, thisArgument, argumentsList) 84 | ``` 85 | 86 | * **target**: La función de destino a llamar 87 | * **thisArgument**: El valor de `this` para proporcionar a la llamada de la función de destino. 88 | * **argumentsList**: Un array de objetos que representa los argumentos con los que se debe llamar a la función de destino. 89 | 90 | En ES5, podíamos utilizar `Function.prototype.apply()` para llamar a una función proporcionándole un valor para `this` y una lista de argumentos: 91 | 92 | ```javascript 93 | // ES5 94 | Function.prototype.apply.call(Math.floor, undefined, [1.75]); 95 | ``` 96 | 97 | Con `Reflect.apply`, el comportamiento es idéntico, pero menos *verboso* y fácil de entender. Veamos algunos ejemplos: 98 | 99 | ```javascript 100 | // ES6 101 | Reflect.apply(Math.floor, undefined, [1.75]); 102 | // 1; 103 | 104 | Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]); 105 | // "hello" 106 | 107 | Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index; 108 | // 4 109 | 110 | Reflect.apply("".charAt, "ponies", [3]); 111 | // "i" 112 | ``` 113 | 114 | ## `Reflect.defineProperty()` 115 | 116 | ```javascript 117 | var obj = {}; 118 | Reflect.defineProperty(obj. "x", {value: 7}); //true 119 | obj.x; // 7 120 | ``` 121 | -------------------------------------------------------------------------------- /manuscript/05_Arrow_functions_y_ambito_lexico.md: -------------------------------------------------------------------------------- 1 | # Arrow functions y ámbito léxico 2 | 3 | ## Diferencias entre arrow functions y funciones clásicas 4 | 5 | Una de las novedades más interesantes de ECMAScript 6 son las denominadas **funciones flecha**, o *arrow functions*. Las funciones flecha son, como su nombre indica, definidas mediante una nueva sintaxis que utiliza una "flecha" (`=>`) a la que estarás acostumbrado si has trabajado en otros lenguajes con [*expresiones lamda*](https://msdn.microsoft.com/es-es/library/bb397687.aspx). 6 | 7 | Las funciones flecha se comportan de forma sensiblemente distinta a las funciones tradicionales de JavaScript en una serie de detalles que cabe tener presentes: 8 | 9 | * **No pueden llamarse con `new`**: Al no tener un método constructor, no pueden ser utilizadas como constructores. Las funciones flecha lanzarán un error cuando se utilicen con `new`. 10 | * **No hay prototipo**: Al no disponer de constructor, tampoco es necesario un prototipo. Por lo tanto, no existirá la propiedad `prototype` en una función flecha. 11 | * **No crean un nuevo contexto**. El valor de `this`, `super`, `arguments` y `new.target` dentro de la función será el mismo que el de la función tradicional (*non-arrow*) más cercana. 12 | * **No puedes cambiar `this`**: El valor de `this` dentro de la función flecha permanece inmutable a lo largo de todo el ciclo de vida de la función. 13 | * **No hay objeto `arguments`**: Tan solo es posible proporcionarle parámetros a una función flecha mediante parámetros nombrados y *rest parameters*. 14 | * **No es posible duplicar parámetros con el mismo nombre**: Tanto en modo estricto como no estricto, a diferencia de las funciones clásicas, que no lo permiten tan solo en modo estricto. 15 | 16 | ## Sintaxis de arrow functions 17 | 18 | El ejemplo más simple de *arrow function* es el siguiente, aunque veremos en los ejemplos siguientes que existen diversas variaciones. 19 | 20 | ```javascript 21 | // ES6 22 | const echo = text => text; 23 | ``` 24 | 25 | Esta función sería equivalente a la siguiente: 26 | 27 | ```javascript 28 | // ES5 29 | var echo = function(text) { 30 | return text; 31 | }; 32 | ``` 33 | 34 | En ambos casos, la ejecución de la función daría la siguiente salida: 35 | 36 | ```javascript 37 | console.log(echo('Hola Mundo!')); // Hola Mundo! 38 | ``` 39 | 40 | Como con cualquier función, podemos pasar tantos argumentos como queramos a la función: 41 | 42 | ```javascript 43 | const sum = (a, b) => a + b; 44 | console.log(sum(1, 1)); // 2 45 | ``` 46 | 47 | O ninguno, claro: 48 | 49 | ```javascript 50 | const greet = () => 'Hola, forastero!'; 51 | console.log(greet()); // Hola, forastero! 52 | ``` 53 | 54 | Si queremos realizar operaciones más complicadas, podemos hacerlo con corchetes y definiendo un valor de retorno: 55 | 56 | ```javascript 57 | const resize = ({x, y}, ratio) => { 58 | return { 59 | x: x * ratio, 60 | y: y * ratio 61 | }; 62 | }; 63 | 64 | console.log(resize({x: 5, y: 15}, 100)); 65 | ``` 66 | 67 | ## Una función flecha no crea un nuevo contexto 68 | Una de las mayores fuentes de errores en JavaScript venía dada por la creación de distintos contextos en una función dependiendo de quien las esté ejecutando. Tomemos el siguiente ejemplo: 69 | 70 | ```javascript 71 | const randomWinner = function(drivers) { 72 | const winner = Math.floor(Math.random() * (0 - drivers.length) + drivers.length); 73 | return drivers[winner]; 74 | }; 75 | 76 | const F1Race = { 77 | drivers: [ 78 | 'Alonso', 79 | 'Vettel', 80 | 'Button', 81 | 'Massa' 82 | ], 83 | 84 | init: function() { 85 | console.log('Los siguientes pilotos van a comenzar la carrera:', this.drivers); 86 | setTimeout((function() { 87 | console.log('El ganador es', randomWinner(this.drivers)); 88 | }), 1000); 89 | } 90 | }; 91 | 92 | F1Race.init(); 93 | ``` 94 | 95 | `F1Race` es un objeto que lanza una carrera de Formula 1 mediante su función `init()`. Al cabo de un segundo, se ejecutará la función `randomWinner()`, que a partir de un array de conductores, seleccionará uno al azar. 96 | 97 | Cuando ejecutamos la función `init()`, el programa escribe por consola lo siguiente: 98 | 99 | ``` 100 | Los siguientes pilotos van a comenzar la carrera: [ 'Alonso', 'Vettel', 'Button', 'Massa' ] 101 | ``` 102 | Esto es posible ya que la función init tiene como contexto el propio objeto `F1Race`. Sin embargo, la función da error tras un segundo, mientras intenta calcular el ganador de forma aleatoria. ¿Cómo es posible? 103 | 104 | El motivo es que la función de *callback* que se le pasa a `setTimeout` crea un nuevo contexto, en el que no existe el array `drivers`. 105 | 106 | En ECMAScript 5 podíamos solucionar este problema utilizando `bind(this)` para asignar el contexto de la función de callback al del objeto que la contiene, de la siguiente forma: 107 | 108 | ```javascript 109 | init: function() { 110 | console.log('Los siguientes pilotos van a comenzar la carrera:', this.drivers); 111 | setTimeout((function() { 112 | console.log('El ganador es', randomWinner(this.drivers)); 113 | }).bind(this), 1000); 114 | } 115 | ``` 116 | 117 | Con ECMAScript 2015, podemos solucionar este contratiempo utilizando *arrow functions* de una forma mucho más elegante, ya que al no crear un nuevo contexto, `this` siempre vendrá determinado por la función que lo contiene: 118 | 119 | ```javascript 120 | init: function() { 121 | console.log('Los siguientes pilotos van a comenzar la carrera:', this.drivers); 122 | setTimeout(() => console.log('El ganador es', randomWinner(this.drivers)), 1000); 123 | } 124 | ``` 125 | -------------------------------------------------------------------------------- /manuscript/03_Plantillas_de_cadenas_de_texto.md: -------------------------------------------------------------------------------- 1 | # Plantillas de cadenas de texto 2 | 3 | La manipulación de **strings**, o cadenas de texto en JavaScript siempre ha estado algo verde respecto a las posibilidades que ofrecen otros lenguajes como Java o C#, por citar un par de ejemplos. 4 | 5 | Con ECMAScript 6 se ha tratado de dar respuesta a algunas de las necesidades más básicas de los programadores a la hora de trabajar con strings, introduciendo la característica de las **plantillas de cadenas de texto**. 6 | 7 | ## Sintaxis para plantillas de cadenas de texto 8 | 9 | El constructor de una plantilla de texto se invoca delimitando la string con el carácter de **acento grave:** ` ` `. Es decir, lo que antes de ES2015 hubiese sido: 10 | 11 | ```javascript 12 | // ES5 13 | var hello = "Hola Mundo"; 14 | // o bien 15 | var hello = 'Hola Mundo'; 16 | ``` 17 | 18 | Ahora puede expresar con: 19 | 20 | ```javascript 21 | // ES6 22 | let hello = `Hola Mundo`; 23 | ``` 24 | 25 | ## Cadenas de texto de varias líneas 26 | 27 | Podemos disponer de cadenas de texto de múltiples líneas, y pasar de esto: 28 | 29 | ```javascript 30 | // ES5 31 | var text = ['En un lugar', 'de la mancha,', 'de cuyo nombre', 'no quiero acordarme'].join('\n'); 32 | ``` 33 | 34 | A esto: 35 | 36 | ```javascript 37 | // ES6 38 | var quijote = `En un lugar 39 | de la mancha, 40 | de cuyo nombre 41 | no quiero acordarme`; 42 | ``` 43 | 44 | ## Interpolación de parámetros en plantillas de cadenas de texto 45 | 46 | En ES5, si queríamos crear un string con contenido no estático, no había muchas formas mejores de hacerlo que la siguiente: 47 | 48 | ```javascript 49 | // ES5 50 | var dani = { 51 | name: 'Daniel', 52 | age: 32 53 | }; 54 | 55 | var greet = function(person) { 56 | return 'Hello! My name is ' + person.name + ' and I\'m ' + person.age + ' years old'; 57 | }; 58 | 59 | greet(dani); 60 | ``` 61 | 62 | Sin embargo, con ES2015 ahora podemos convertir la función `greet` en: 63 | 64 | ```javascript 65 | // ES6 66 | var greet = function(person) { 67 | return `Hello! My name is ${person.name} and I'm ${person.age} years old`; 68 | }; 69 | ``` 70 | 71 | Esto tiene una ventaja añadida: la de poder hacer sustituciones no solo por un valor, sino por cualquier expresión válida en JavaScript dentro de los símbolos de interpolación: 72 | 73 | ```javascript 74 | let myAge = `Mi edad es ${person.age + 3} años`; 75 | ``` 76 | 77 | ## Plantillas de string con funciones de etiquetado 78 | 79 | Una forma avanzada de plantillas de texto son las plantillas de texto *etiquetadas*. Con ellas seremos capaces de modificar la salida de las plantillas de texto utilizando una función. Dicha función debe tener la siguiente firma: 80 | 81 | ```javascript 82 | function tag(string, values) { } 83 | ``` 84 | 85 | No hace falta que la función se llame `tag`, podemos llamarla como queramos. Tan solo la he llamado así de forma ilustrativa. 86 | 87 | El primer argumento contiene un array de literales y el segundo, cada uno de los valores de substitución. 88 | 89 | ```javascript 90 | // ES6 91 | const tag = (strings, args) => { 92 | return strings.map(s => 93 | s.split('').map(s => `${s}.`).join('')) 94 | .join(''); 95 | } 96 | 97 | console.log(tag`Hello, World!`); 98 | // "H.e.l.l.o.,. .W.o.r.l.d.!." 99 | ``` 100 | 101 | En el ejemplo anterior, hemos creado una función de etiquetado en la que "rompemos" el string en un array de caracteres, lo recorremos con una función `map` y añadimos un punto a cada carácter mediante un template string. Por último, lo unimos todo y el resultado es una función que te añade un punto a cada carácter del string facilitado. 102 | 103 | Obviamente, este tipo de comportamiento no es demasiado útil, pero el límite del potencial que ofrecen las funciones de etiquetado está en nuestra imaginación. 104 | 105 | Podríamos, por ejemplo pensar en cómo haríamos una función de internacionalización (i18n) de literales para nuestra aplicación. Con una función de etiquetado en todos nuestros strings, podríamos devolver en tiempo de ejecución el literal adecuado a la cultura del usuario. 106 | 107 | Una aproximación muy sencilla podría ser la siguiente: 108 | 109 | ```javascript 110 | // ES6 111 | const dict = { 112 | es: { 113 | 'days ago': 'hace %{count} días' 114 | }, 115 | en: { 116 | 'days ago': '%{count} days ago' 117 | } 118 | }; 119 | 120 | const i18n = (strings, args) => { 121 | const key = strings.join('').trim(); 122 | return dict[culture][key].replace('%{count}', args); 123 | }; 124 | 125 | var culture = 'es'; 126 | console.log(i18n`${8} days ago`); 127 | 128 | culture = 'en'; 129 | console.log(i18n`${8} days ago`); 130 | ``` 131 | 132 | Creamos un diccionario que contenga todas las traducciones de nuestra aplicación. En nuestro caso, tiene los idiomas inglés (en) y español (es). donde la clave es el literal a traducir y el valor es el literal traducido. Queremos traducir un literal que informa al usuario cuántos días han pasado desde que ocurrió un evento. 133 | 134 | En este ejemplo, hemos añadido un "place holder", `${count}`, que nos servirá para sustituir el contador de días. Este elemento es totalmente arbitrario, no tiene nada que ver con "template strings": su nombre lo hemos decidido nosotros. Pero nos será útil porque en inglés el orden de las palabras es distinto que en castellano. 135 | 136 | A continuación tenemos nuestra función de etiquetado: `i18n`. Fíjate que, llamándolo de esta manera, el cometido de la etiqueta en los "template strings" queda muy explícito. Lo único que hace esta función es asegurarse de que tiene una única clave, ya que recordemos que el primer argumento es un array de strings; y posteriormente buscar el string en el diccionario para devolver el traducido. No sin antes, reemplazar el elemento `%{count}` por el valor proporcionado. 137 | 138 | El resultado, es el literal traducido en cada idioma. 139 | 140 | La variable culture simplemente ilustra cómo funcionaría la función cambiando de cultura, pero obviamente en una aplicación real habría que buscar una forma más elegante de obtenerla. Para trabajar con i18n, te recomiendo la librería de Airbnb [Polyglot](http://airbnb.io/polyglot.js/). -------------------------------------------------------------------------------- /manuscript/01_Instalacion_y_uso_de_Babel.md: -------------------------------------------------------------------------------- 1 | # Instalación y uso de Babel 2 | 3 | ## `babel-cli` 4 | El intérprete de línea de comandos **Babel CLI** es una herramienta simple que nos permite compilar archivos con Babel desde nuestro terminal. 5 | 6 | Puede instalarse globalmente para tenerlo disponible en cualquier directorio que lo necesitemos, aunque si pensamos en distribuir nuestros proyectos *open source*, es preferible instalarlo a nivel local. En este ejemplo lo haremos de la segunda forma. 7 | 8 | ### Creando un entorno de pruebas 9 | Para comenzar, abrimos una nueva sesión de terminal del sistema e inicializamos un nuevo package de npm: 10 | 11 | ```terminal 12 | $ npm init -y 13 | ``` 14 | 15 | Esto nos añadirá el archivo `package.json` en el directorio: 16 | 17 | ```terminal 18 | . 19 | └── package.json 20 | ``` 21 | 22 | Una vez creado el package del proyecto, añadiremos un nuevo archivo: 23 | 24 | ```terminal 25 | $ touch my-file.js 26 | ``` 27 | 28 | Y copiaremos el siguiente código: 29 | 30 | ```javascript 31 | const square = (n) => n * n; 32 | console.log(square(2)); 33 | ``` 34 | 35 | No te preocupes demasiado por entender la sintaxis, ya que la trabajaremos más adelante con detenimiento. Simplemente ten en cuenta que al ejecutar el código con `node my-file.js` debería obtener como resultado el cuadrado de 2, o sea: 4. 36 | 37 | ### Instalando babel-cli como un script de npm 38 | Volviendo a Babel, el archivo que acabamos de crear nos servirá para mostrar el resultado de una compilación de ES6 a ES5. 39 | 40 | Pero antes tenemos que instalarlo y configurarlo como uno de los scripts de `npm` de nuestro proyecto. Para instalarlo, escribe: 41 | 42 | ```terminal 43 | $ npm install --save-dev babel-cli 44 | ``` 45 | 46 | Y luego, en el archivo `package.json`, añadiremos las siguientes líneas: 47 | 48 | ```json 49 | { 50 | "scripts": { 51 | "babel": "babel my-file.js" 52 | }, 53 | } 54 | ``` 55 | 56 | Una vez añadida, ya tendremos una tarea de NPM que podremos ejecutar desde consola para compilar el código. Ejecuta el siguiente comando: 57 | 58 | ```terminal 59 | $ npm run babel 60 | ``` 61 | 62 | Esto debería producir la siguiente salida de consola: 63 | 64 | ``` 65 | $ npm run babel 66 | 67 | > my-dir@1.0.0 babel /path/to/my-dir 68 | > babel my-file.js 69 | 70 | const square = n => n * n; 71 | console.log(square(2)); 72 | ``` 73 | 74 | ### Especificar un archivo de salida 75 | Como verás, se imprime por consola el contenido de tu archivo. Esto tiene escasa utilidad, más allá de comprobar que efectivamente, `babel-cli` está funcionando. Si en lugar de la consola queremos que el *output* tenga como destino un archivo de destino, modificaremos el script en nuestro `package.json` con lo siguiente: 76 | 77 | ```json 78 | { 79 | "scripts": { 80 | "babel": "babel my-file.js --out-file compiled.js" 81 | }, 82 | } 83 | ``` 84 | 85 | En esta ocasión, hemos indicado el nombre del archivo de destino con el modificador `--out-file` (también serviría simplemente `-o`. Si volvemos a ejecutar la tarea `npm run build`, deberíamos observar la siguiente salida en la consola: 86 | 87 | ```terminal 88 | $ npm run babel 89 | 90 | > my-dir@1.0.0 babel /path/to/my-dir 91 | > babel my-file.js --out-file compiled.js 92 | ``` 93 | Además, en esta ocasión se habrá creado un nuevo archivo `compiled.js` con el contenido del archivo original. Todavía no estamos aplicando ninguna transformación. 94 | 95 | ### Compilar todo el contenido de una carpeta 96 | 97 | Si nuestra aplicación crece, es normal que tengamos varios módulos, así que es poco habitual que solamente compilemos un único archivo. Para especificar todo un directorio, modificaremos nuestro script en el `package.json` de esta manera: 98 | 99 | ```json 100 | { 101 | "scripts": { 102 | "babel": "babel src --out-dir lib" 103 | }, 104 | } 105 | ``` 106 | 107 | Si organizamos todo el código de nuestra aplicación bajo la carpeta `src`, este script compilará todo lo que encuentre y lo escribirá en la carpeta `lib`. Los nombres de carpetas son totalmente arbitrarios, podéis elegir el que más os guste. 108 | 109 | ## Configuración de Babel 110 | 111 | Hasta ahora hemos preparado un entorno para compilar nuestros archivos con Babel, pero realmente no estamos haciendo ninguna transformación al código. Eso se debe a que todavía no le hemos dicho a Babel que haga nada en concreto. 112 | 113 | Desde la publicación de Babel 6, esta funcionalidad se ha extraído del núcleo de Babel para ofrecerse como paquetes por separado, a los que sus creadores llaman *presets*. Dichos *presets* son muy específicos y hacen que la herramienta sea mucho más modular y extensible. 114 | 115 | Así que si queremos trabajar con ECMAScript 6, deberemos instalar el preset correspondiente. Lo haremos mediante la siguiente instrucción de línea de comandos: 116 | 117 | ```terminal 118 | $ npm install --save-dev babel-preset-es2015 119 | ``` 120 | 121 | Ahora, todo lo que necesitamos es crear un fichero `.babelrc` en la raíz de nuestro proyecto y añadir la siguiente configuración: 122 | 123 | ```json 124 | { 125 | "presets": [ "es2015" ], 126 | "plugins": [] 127 | } 128 | ``` 129 | 130 | Si lo preferimos, esta configuración puede ir directamente en el `package.json` de la siguiente forma: 131 | 132 | ```json 133 | "babel": { 134 | "presets": [ "es2015" ], 135 | "plugins": [] 136 | } 137 | ``` 138 | 139 | Como consejo, te diría que intentes mantener el archivo `package.json` lo más limpio posible y pongas la configuración de Babel en su propio archivo de configuración, ya que el `package.json` tiene bastantes cosas de por si. Aunque es una elección personal. 140 | 141 | Una vez configurado, si ejecutamos nuestra tarea de compilación, observaremos un resultado muy distinto en el fichero generado: 142 | 143 | ```javascript 144 | "use strict"; 145 | 146 | var square = function square(n) { 147 | return n * n; 148 | }; 149 | console.log(square(2)); 150 | ``` 151 | 152 | Como verás, se ha transformado el código ECMAScript 6 en un código de ECMAScript 5 que será compatible con la gran mayoría de navegadores del mercado y además es muy legible. 153 | 154 | A partir de ahora, ya podemos incorporar la sintaxis ES2015 a nuestros proyectos, pues tan solo con incluir Babel en nuestro flujo de trabajo podremos desplegar código compatible con cualquier cliente sin preocuparnos por ello. -------------------------------------------------------------------------------- /manuscript/15_Decoradores.md: -------------------------------------------------------------------------------- 1 | # Decoradores 2 | 3 | ## Sintaxis 4 | Quien haya programado en Java o C# seguro que sabrá que existen los atributos de clase, método o propiedad. Estos atributos son programables y modifican de alguna forma el objetivo sobre el que se colocan. 5 | La sintaxis en esos lenguajes es muy similar a la que se ha escogido en JavaScript. 6 | 7 | Para una clase: 8 | ```javascript 9 | @anotation 10 | class Vehicle { } 11 | ``` 12 | 13 | Para el método de una clase: 14 | ```javascript 15 | class Vehicle{ 16 | constructor(){ } 17 | 18 | @fitipaldi 19 | run(){} 20 | } 21 | ``` 22 | 23 | También es posible usarlo dentro de una propiedad de una clase: 24 | 25 | ```javascript 26 | class Vehicle { 27 | @uppercase 28 | get model(){ 29 | return this._model 30 | } 31 | } 32 | ``` 33 | 34 | ## Cómo funciona 35 | Un decorador es una función que intercepta la definición de propiedades de un objeto. Por lo tanto, permite modificar tal definición en tiempo de ejecución. 36 | 37 | Crearemos un decorador de la forma siguiente: 38 | 39 | ```javascript 40 | const decorator = function ( target, name, descriptor); 41 | ``` 42 | 43 | Lo hace llamando a una función que recibe los mismos parámetros que Object.defineProperty(). Así, el decorador devuelve un objeto que se usará como definición de las propiedades. 44 | 45 | El decorador toma tres argumentos: 46 | 47 | * **Target**: El objeto al que queremos modificar su definición de propiedades 48 | * **Name**: El nombre de la propiedad a modificar 49 | * **Descriptor**: La descripción de la propiedad del objeto, que explica cómo esta se comporta. La descripción tiene los siguientes puntos: 50 | configurable: si es true la propiedad puede ser modificada; 51 | enumerable: si es true esta propiedad aparece cuando hagamos un for of del objecto; 52 | ** **value**: es el valor asociado a la propiedad. Puede ser desde un tipo simple hasta una función; 53 | writable: indica si la propiedad puede ser cambiada con una asignación; 54 | ** **get**: si la propiedad es un getter del objeto, aquí podemos escribir una función. Lo devuelto por esa función se asociará al valor del atributo. En caso de no ser una función y ser undefined, se usará value, como vimos antes; 55 | ** **set**: similar a lo anterior, pero en este caso la función recibirá el nuevo valor del atributo. 56 | 57 | Veamos un ejemplo: 58 | 59 | Supón que quieres enviar un mensaje a la consola cada vez que un método es ejecutado. Para ello vamos a crear un decorador llamado logger. Como es un decorador, no es más que una función que recibe tres argumentos. 60 | 61 | const logger = function( target, name, descriptor ){} 62 | De estos tres argumentos, el más importante es el descriptor, que es el que tenemos que devolver; es decir, devolveremos un objeto que sea similar al descriptor que nos ha llegado, pero con las modificaciones especificadas por el decorador: 63 | 64 | const logger = function( target, name, descriptor ){ 65 | // Guardamos una referencia al generador de la función 66 | const initializer = descriptor.initializer(); 67 | 68 | // Creamos una copia del descriptor original 69 | let desc = Object.assign({}, descriptor) 70 | 71 | // Modificamos la copia para decorarlo con el log a consola 72 | desc.initializer = function(){ 73 | return function(...args){ 74 | console.log( `LOGGER: call ${name} with ${args}` ) 75 | initializer.apply(this, args) 76 | } 77 | 78 | } 79 | 80 | // Devolvemos nuestro nuevo descriptor modificado 81 | return desc 82 | } 83 | Una vez hemos creado el decorador, solo tenemos que usarlo como hemos visto: 84 | 85 | const mathematician = { 86 | @logger 87 | add(first, second){ 88 | console.log( `La suma es ${first + second}` ); 89 | } 90 | } 91 | 92 | mathematician.add( 1, 2 ) 93 | La salida de este script es la siguiente: 94 | 95 | LOGGER: call add with 1,2 96 | La suma es 3 97 | No sé qué os parece a vosotros, pero a mí me parece una forma súper elegante de hacer un logger sobre los métodos que me puedan interesar de un objeto :) 98 | 99 | Trabajando con clases 100 | Los decoradores, como hemos visto, también se pueden usar con clases, tanto con algún método de la clase como con su propio constructor. Veamos un ejemplo donde hacemos que un método de una clase sea decorado para que devuelva un string en mayúsculas. 101 | 102 | El decorador sería algo así: 103 | 104 | const mayusculas = function( target, name, descriptor ){ 105 | const value = descriptor.value 106 | descriptor.value = function(){ 107 | return value.apply(this, arguments).toUpperCase() 108 | } 109 | return descriptor 110 | } 111 | Devolvemos el objeto descriptor, reescribiéndole solo el atributo value, para que devuelva nuestra función decorada. 112 | 113 | Si tuvieramos una clase, así es como lo usaríamos: 114 | 115 | class Persona{ 116 | constructor( name ){ 117 | this._name = name 118 | } 119 | 120 | @mayusculas 121 | saluda(){ 122 | return `Hola, me llamo ${this._name}` 123 | } 124 | } 125 | const persona = new Persona('carlos') 126 | console.log( `${persona.saluda()}` ) 127 | La salida que obtenemos con esto sería algo así: 128 | 129 | HOLA, ME LLAMO CARLOS 130 | Dando incluso una vuelta de tuerca más, puesto que los decoradores no son más que funciones, también podemos pasarles argumentos a ellos mismos. Esto lo podemos observar en el siguiente ejemplo, donde decoramos el constructor de la clase. 131 | 132 | const apellidos = function( apellidos ){ 133 | return function(Target){ 134 | return class extends Target{ 135 | get apellidos(){ 136 | return apellidos 137 | } 138 | } 139 | } 140 | } 141 | Aquí nuestro decorador es una función que devuelve otra función. Puede sorprenderte que la función que devuelve no tenga los tres argumentos de los casos anteriores, pero eso es debido a que al decorar el constructor de una clase, el decorador solo recibe como úncio argumento el propio constructor. Dicho esto, podríamos usarlo de la siguiente forma: 142 | 143 | @apellidos('Villuendas Zambrana') 144 | class Persona{ 145 | constructor( name ){ 146 | this._name = name 147 | } 148 | 149 | saluda(){ 150 | return `Hola, me llamo ${this._name}` 151 | } 152 | } 153 | 154 | const persona = new Persona('carlos') 155 | console.log( `${persona.apellidos}` ) 156 | Como salidas obtenemos: 157 | 158 | Villuendas Zambrana 159 | Presta mucha atención a este último ejemplo, ya que será la piedra angular sobre la que montaremos el concepto de componentes de orden superior en ReactJS. 160 | 161 | Bonus 162 | Obviamente nada nos impide apilar los decoradores uno sobre otro e ir aplicándolos en serie. Con este nuevo decorador, 163 | 164 | const dots = function( target, name, descriptor ){ 165 | const value = descriptor.value 166 | descriptor.value = function(){ 167 | return value.apply(this, arguments).split('').join('.') 168 | } 169 | return descriptor 170 | } 171 | puedo crear el siguiente stack de decoración sobre un método: 172 | 173 | class Persona{ 174 | constructor( name ){ 175 | this._name = name 176 | } 177 | 178 | @mayusculas 179 | @dots 180 | saluda(){ 181 | return `Hola, me llamo ${this._name}` 182 | } 183 | } 184 | 185 | const persona = new Persona('carlos') 186 | console.log( `${persona.saluda()}` ) 187 | Esto nos daría como salida, 188 | 189 | H.O.L.A.,. .M.E. .L.L.A.M.O. .C.A.R.L.O.S -------------------------------------------------------------------------------- /manuscript/06_Conceptos_de_programacion_orientada_a_objetos_en_javascript.md: -------------------------------------------------------------------------------- 1 | # Conceptos de programación orientada a objetos en JavaScript 2 | 3 | ## Qué es un constructor? 4 | Básicamente un constructor es cualquier función que se utiliza como un constructor. ¿Perogrullada? Estoy seguro, pero es que un constructor no tiene nada de especial hasta que no se utiliza como tal. Esta definición es válida independientemente del lenguaje de programación con el que trabajemos. Veamos un ejemplo de función constructor: 5 | 6 | ```javascript 7 | var Vehicle = function Vehicle() { 8 | console.log('¡Sólo soy una función!'); 9 | // ... 10 | } 11 | 12 | var motorcycle = new Vehicle(); 13 | ``` 14 | 15 | La función Vehicle es una función normal, pero cuando la utilizamos mediante la palabra reservada `new`, además de ejecutar el código del cuerpo de la función, JavaScript hace que pasen cuatro cosas: 16 | 17 | 1. Se crea un nuevo objeto 18 | 2. Establece la propiedad `constructor` del objeto a la función `Vehicle` 19 | 3. Configura el objeto para delegar en `Vehicle.prototype` 20 | 4. Dentro de la función se crea un contexto para el objeto que se está construyendo. 21 | 22 | El resultado de `new Vehicle()` es este nuevo objeto. 23 | 24 | ```javascript 25 | typeof Vehicle; 26 | > 'function' 27 | typeof Vehicle(); 28 | > 'undefined' 29 | typeof new Vehicle(); 30 | > 'object' 31 | ``` 32 | La diferencia entre las tres llamadas es que mientras `Vehicle` es la definición de la función, `Vehicle()` representa el valor retornado por la función y en este caso no retornamos nada, así que es `undefined`. Por último, `new Vehicle()`. Devuelve un nuevo objeto que será una instancia de `Vehicle`. 33 | 34 | ** NOTA: No es aconsejable retornar ningún valor en funciones constructoras, ya que el valor retornado sobreescribirá la instancia del objeto creado** 35 | 36 | ### La propiedad `constructor` 37 | Establecer la propiedad `constructor` en el objeto creado significa dos cosas: 38 | 39 | ```javascript 40 | motorcycle.constructor == Vehicle // true 41 | motorcycle instanceof Vehicle // true 42 | ``` 43 | 44 | El objeto creado tendrá una propiedad `constructor` bastante especial. No aparecerá si enumeramos las propiedades del objeto, pero podemos crearla y asignarla al nuevo objeto, sobreescribiendo la propiedad original: 45 | 46 | ```javascript 47 | motorcycle; // {} 48 | 49 | var Postman = function Postman() {}; 50 | motorcycle.constructor = Postman; 51 | motorcycle; // Postman { constructor: [Function: Postman] } 52 | ``` 53 | 54 | En el fragmento de código anterior hemos creado una nueva función constructora llamada `Postman` (cartero, en inglés). Posteriormente, asignamos a esa función una propiedad constructor de nuestro objeto `motorcycle`. Si evaluamos ahora `motorcycle`, veremos que ahora tiene una propiedad `constructor` que no es más que la función `Postman`. Pero, ¿ha cambiado el tipo de nuestro objeto? 55 | 56 | ```javascript 57 | motorcycle.constructor == Postman // true 58 | motorcycle instanceof Postman // false 59 | motorcycle instanceof Vehicle // true 60 | ``` 61 | 62 | Como puedes comprobar tú mismo, `motorcycle` sigue siendo un vehículo. El constructor subyacente no es algo que podamos modificar tras crear una instancia de un objeto, solo podemos hacerlo en tiempo de construcción del mismo objeto. 63 | 64 | ### Prototipo delegado 65 | En JavaScript una función no es más que un tipo especial de objeto. Y como todos los objetos, una función puede tener propiedades. Las funciones en JavaScript obtienen automáticamente una propiedad llamada `prototype`, la cual es a su vez un objeto vacío. Este objeto recibe un tratamiento especial. 66 | 67 | Cunado un objeto es construido, *hereda todas las propiedades del prototipo de su constructor*. Veamos un ejemplo: 68 | 69 | ```javascript 70 | Vehicle.prototype.wheelCount = 4; 71 | var truck = new Vehicle(); 72 | truck.wheelCount; // 4 73 | ``` 74 | 75 | La instancia del camión (`truck`) ha recogido las propiedades del prototipo de `Vehicle`. 76 | 77 | Ahora es cuando se pone interesante: la nueva instancia no solo copia las propiedades, sino que el objeto está configurado para *delegar* cualquier propiedad que no ha sido explícitamente creada en el prototipo de su constructor. Esto significa que, si cambiamos el prototipo más adelante, veremos los cambios en la instancia: 78 | 79 | ```javascript 80 | Vehicle.prototype.wheelCount = 6; 81 | truck.wheelCount; //6 82 | ``` 83 | 84 | Por supuesto, también podemos sobreescribirla asignándola explícitamente: 85 | 86 | ```javascript 87 | truck.wheelCount = 8; 88 | truck.wheelCount; // 8 89 | ``` 90 | 91 | Obviamente, siempre podremos hacer lo mismo con métodos, ya que un método no es más que una función asignada a una propiedad: 92 | 93 | ```javascript 94 | Vehicle.prototype.go = function go() { return "Vroom!" }; 95 | truck.go(); // Vroom! 96 | ``` 97 | 98 | ### Se crea un contexto para el objeto construido 99 | Dentro de la función constructora, `this` adquiere el valor del objeto construido, creando un contexto específico para dicho objeto: 100 | 101 | ```javascript 102 | var Person = function Person(name){ 103 | this.name = name; 104 | } 105 | 106 | var myself = new Person('Dani'); 107 | 108 | console.log(typeof name); // undefined 109 | console.log(myself.name); // Dani 110 | ``` 111 | 112 | La propiedad `name` solo existe dentro del contexto del objeto `myself`. A diferencia de una llamada a una función no constructora: 113 | 114 | ```javascript 115 | Person('Joan'); 116 | console.log(name); // Joan 117 | ``` 118 | 119 | ## Creando clases en JavaScript (ES5) 120 | Una vez explicados los conceptos anteriores, ya tenemos la capacidad de crear clases en JavaScript. Esta es una manera (no es la única, pero sí una de ellas muy válida): 121 | 122 | ```javascript 123 | //ES5 124 | 125 | // Class definition / constructor 126 | var Vehicle = function Vehicle(color) { 127 | // Initialization 128 | this.color = color; 129 | } 130 | 131 | // Instance methods 132 | Vehicle.prototype = { 133 | go: function go() { 134 | return "Brrooooom!"; 135 | } 136 | } 137 | ``` 138 | 139 | ## Subclases 140 | La flexibilidad de JavaScript también nos permite crear algo muy cercano a lo que sería "herencia" en otros lenguajes de programación orientados a objetos, aunque con sus limitaciones. 141 | 142 | ```javascript 143 | // ES5 144 | var Car = function Car() {}; 145 | Car.prototype = new Vehicle("white"); 146 | Car.prototype.honk = function honk() { return "BEEP!" }; 147 | var car = new Car(); 148 | car.honk(); // "BEEP!" 149 | car.go(); // "Vroom!" 150 | car.color; // "white" 151 | car instanceof Car; // true 152 | car instanceof Vehicle; // true 153 | ``` 154 | El problema con esta aproximación es que el constructor de `Vehicle` solo puede llamarse una vez: para configurar el prototipo de `Car`. Necesitamos asignar el color del coche en ese momento, así que no podemos tener distintos coches de distintos colores. Por eso no llega a ser una herencia pura, en el sentido más estricto del paradigma de orientación a objetos. Algunos *frameworks* de JavaScript han resuelto esto definiendo sus propias implementaciones de clase. 155 | 156 | ## Función de clonado 157 | Si no queremos tener la noción de clases y tan solo queremos un objeto que herede propiedades de otro, pero sea capaz de sobreescribirlas, podemos utilizar una función de clonado. La siguiente sería una posible y sencilla implementación: 158 | 159 | ```javascript 160 | function clone(parent){ 161 | var Clone = function() {}; 162 | Clone.prototype = parent; 163 | return new Clone(); 164 | } 165 | ``` 166 | 167 | Con nuestra función de clonado, podemos hacer nuevas instancias como churros que hereden las propiedades de un objeto original: 168 | 169 | ```javascript 170 | var car = { color: 'white' }; 171 | var ibiza = clone(car); 172 | var leon = clone(car); 173 | var toledo = clone(car); 174 | toledo.color = 'blue'; 175 | 176 | ibiza.color; // white 177 | leon.color; // white 178 | toledo.color; // blue 179 | 180 | car.color = 'black'; 181 | 182 | ibiza.color; // black 183 | leon.color; // black 184 | toledo.color; // blue 185 | ``` -------------------------------------------------------------------------------- /manuscript/04_Desestructuracion_de_arrays_y_objetos.md: -------------------------------------------------------------------------------- 1 | # Desestructuración de arrays y objetos 2 | 3 | ## Desestructuración de Arrays 4 | 5 | ```javascript 6 | const numbers = ["1", "2", "3"]; 7 | 8 | // without destructuring 9 | const one = numbers[0]; 10 | const two = numbers[1]; 11 | const three = numbers[2]; 12 | 13 | // with destructuring 14 | const [uno, dos, tres] = numbers; 15 | 16 | console.log(one, two, three); // 1 2 3 17 | console.log(uno, dos, tres); // 1 2 3 18 | ``` 19 | 20 | Podemos asignar de forma desestructurada sin una declaración en la asignación: 21 | 22 | ```javascript 23 | let ichi, ni, san; 24 | [ichi, ni, san] = ["uno", "dos", "tres"]; 25 | ``` 26 | 27 | Yendo un poco más allá, podemos utilizar funciones para devolver un conjunto de valores y asignarlos de forma desestructurada: 28 | 29 | ```javascript 30 | function users(){ 31 | return ['joan', 'carlos', 'david', 'dani']; 32 | } 33 | ``` 34 | 35 | De este modo podemos devolver cualquier número de valores de forma arbitraria. Hasta aquí ninguna novedad, pero si utilizamos la asignación desestructurada: 36 | 37 | ```javascript 38 | let [joan, carlos, david, dani] = users(); 39 | console.log(joan, carlos, david, dani); // joan carlos david dani 40 | ``` 41 | 42 | La asignación de variables se realiza en orden, el primer valor se asigna a la primera variable, el segundo a la segunda... y así. 43 | 44 | ## Desestructuración de objetos 45 | ```javascript 46 | // ES6 47 | var persona = {nombre: 'Dani', apellidos: 'de la Cruz'}; 48 | var {nombre, apellidos} = persona; 49 | 50 | console.log(nombre); // Dani 51 | console.log(apellidos); // de la Cruz 52 | ``` 53 | 54 | Tal y como puedes ver, podemos crear variables al vuelo a partir de un objeto de Javascript, para luego utilizarlas. Las nuevas variables tendrán por defecto el nombre que tenían las propiedades del objeto. Pero, ¿qué pasa si no nos gusta y queremos cambiarlo? En el ejemplo, no es demasiado correcto utilizar nombres de variables en castellano... 55 | 56 | Bueno, en esta vida casi todo tiene arreglo: 57 | 58 | ```javascript 59 | // ES6 60 | var {nombre: name, apellidos: surname} = persona; 61 | console.log(name); // Dani 62 | console.log(surname); // de la Cruz 63 | ``` 64 | 65 | Este comportamiento es especialmente útil a la hora de pasar parámetros a funciones, como veremos a continuación. 66 | 67 | ```javascript 68 | // ES6 69 | function suma({a, b} = {}) { 70 | return a + b; 71 | }; 72 | 73 | var a = 2; 74 | var b = 3; 75 | console.log(suma({a, b})); // 5 76 | ``` 77 | Como ves, la función `suma` espera un objeto con dos propiedades: `a` y `b`, cuyo valor desconoce. Pero tan solo declarando dos variables del mismo nombre, podemos crear un objeto al vuelo y pasárselo como argumento a la función. El código siguiente es equivalente al anterior, pero con algo más de ceremonia: 78 | 79 | ```javascript 80 | // ES6 81 | console.log(suma({a: a, b: b})); // 5 82 | ``` 83 | Obviamente, nadie utiliza esta segunda forma, ya que ECMAScript asigna automáticamente los valores a propiedades del mismo nombre. 84 | 85 | ## Parámetros por defecto en funciones 86 | 87 | La asignación desestructurada nos facilita muchísimo la vida al trabajar con funciones que reciben parámetros. 88 | 89 | Veamos un ejemplo en ECMAScript 5: 90 | 91 | ```javascript 92 | // ES5 93 | 94 | function drawCircle(options){ 95 | options = options === undefined ? {} : options; 96 | var radius = options.radius === undefined ? 30 : options.radius; 97 | var coords = options.coords === undefined ? { x: 0, y: 0 } : options.coords; 98 | 99 | console.log(radius, coords); 100 | // finally, draw the circle 101 | } 102 | ``` 103 | 104 | ECMAScript 5 nos obligaba a realizar una programación demasiado defensiva en el interior de la función, violando además el Single Responsibility Principle: una función solo debería tener un motivo para cambiar. Y la función `drawCircle` tiene demasiadas responsabilidades: comprobar la integridad de los parámetros proporcionados y dibujar el círculo. 105 | 106 | Pero aquí no terminan los inconvenientes. ¿Qué es options? La firma del método no especifica qué debe ser, si un entero, una cadena de texto o un objeto con propiedades completamente arbitrarias. Y si intentásemos cambiar esto por una firma más explícita como la siguiente tampoco mejoraríamos el problema, ya que estaríamos obligando al consumidor de la función a pasar los argumentos en un orden concreto, complicando la mantenibilidad del código posterior: 107 | 108 | ```javascript 109 | // ES5 110 | function drawCircle(radius, coords) { ... } 111 | ``` 112 | 113 | Para resolver estos problemas, llegua ECMAScript 6 al rescate. Veamos el ejemplo anterior utilizando algunas de las características del nuevo estándar: 114 | 115 | ```javascript 116 | function drawCircle({radius = 30, coords = { x: 0, y: 0}} = {}) { 117 | console.log(radius, coords); 118 | // draw the circle 119 | } 120 | ``` 121 | Como ves, todo el *bootstrapping* de programación defensiva que había que realizar en ES5 solo para que nuestro código no lanzase una excepción, ha desaparecido. O para ser más precisos, se ha camuflado dentro de la firma de la función con un objeto, que por defecto puede estar vacío y cuyas propiedades tienen valores por defecto que tomarán si no se las asigna. 122 | 123 | De este modo, nuestro código no lanzará un error si no asignamos algún valor, la firma del método es mucho más descriptiva y deja de importar el orden de los argumentos a la hora de invocar a `drawCircle`. 124 | 125 | Todos los ejemplos siguientes funcionarían: 126 | 127 | ```javascript 128 | 129 | drawCircle(); // radius: 30, coords.x: 0, coords.y: 0 } 130 | drawCircle({radius: 10}); // radius: 10, coords.x: 0, coords.y: 0 } 131 | drawCircle({coords: {y: 10, x: 30}, radius: 10}); // radius: 10, coords.x: 30, coords.y: 10 } 132 | ``` 133 | 134 | ## Sintaxis del operador de propagación 135 | 136 | El operador de propagación, o *spread operator*, nos permite una aproximación mucho más directa al acceso del contenido dentro de un array o de cualquier objeto. Veamos cómo funciona: 137 | 138 | ```javascript 139 | const values = [1, 2, 3, 4]; 140 | 141 | console.log(values); // [1, 2, 3, 4] 142 | console.log(...values); // 1 2 3 4 143 | ``` 144 | 145 | En la primera impresión por consola, enviamos directamente el array a la función `log`. En cambio, en la segunda, *descomponemos* el array en un conjunto de parámetros que se pasan como argumentos a la función log, así que imprime cada valor directamente. 146 | 147 | Esto nos da mucha flexibilidad a la hora de pasar parámetros a una función, ya que si antes teníamos, por ejemplo: 148 | 149 | ```javascript 150 | // ES5 151 | 152 | var f = function(x, args) { 153 | return x + args.length; 154 | }; 155 | 156 | var parameters = [ "hello", true ]; 157 | console.log(f(3, parameters)); // 5 158 | ``` 159 | 160 | ```javascript 161 | // ES6 162 | 163 | var f = function (x, ...y){ 164 | return x + y.length; 165 | }; 166 | 167 | console.log(f(3, "hello", true)); // 5 168 | ``` 169 | 170 | Como puedes observar, la utilización del *spread operator* nos permite pasar un número arbitrario de parámetros a una función y recogerlos en una variable, sin necesidad de crear un array para ello. 171 | 172 | También podemos hacerlo a la inversa, utilizándolo a la hora de pasar los parámetros, en lugar de en la declaración del método: 173 | 174 | ```javascript 175 | // ES6 176 | 177 | function f(x, y, z) { 178 | return x + y + z; 179 | } 180 | console.log(f(...[1, 2, 3])); // 6 181 | ``` 182 | ## El operador de propagación para propiedades de objetos 183 | 184 | No quería dejar de hablar de una de las funciones más útiles del operador de propagación: la posibilidad de utilizarlo con propiedades de objetos. 185 | 186 | Aunque sea una propuesta para la siguiente versión del estándar (ECMAScript 2016 o ES7), en el momento de escribir estas líneas se encuentra en **Stage 2**, así que es bastante seguro comenzar a utilizarlo desde hoy mismo gracias a la potencia de Babel. 187 | 188 | Como podemos ver en el siguiente ejemplo, el [operador de propagación para propiedades de objetos](https://github.com/sebmarkbage/ecmascript-rest-spread) resulta muy útil para añadir nuevas propiedades a un objeto, o sobreescribirlas al vuelo: 189 | 190 | ```javascript 191 | const worker = { 192 | id: 1337, 193 | name: 'John', 194 | surname: 'Woo', 195 | age: 35, 196 | job: 'UI designer' 197 | }; 198 | 199 | const f = function({worker} = {}){ 200 | return { 201 | ...worker, 202 | fullName: `Mr./Mrs. ${worker.surname}, ${worker.name}`, 203 | age: `${worker.age} years old` 204 | }; 205 | }; 206 | 207 | console.log(f({worker})); 208 | ``` 209 | 210 | El objeto que devuelve la consola tendrá la nueva propiedad `fullName` y también habrá cambiado el tipo de la propiedad `age` a algo más legible. Pero como utilizamos el operador de propagación, también tendrá el resto de propiedades del objeto `worker`. 211 | -------------------------------------------------------------------------------- /manuscript/02_Variables_de_ambito_de_bloque_y_constantes.md: -------------------------------------------------------------------------------- 1 | # Variables de ámbito de bloque: `let` 2 | 3 | ## Diferencia entre contexto y alcance 4 | Cada invocación de función tiene tanto un alcance como un contexto asociados a ella. Fundamentalmente, el alcance es un concepto asociado a funciones mientras que el contexto está asociado a objetos. En otras palabras, el alcance se refiere a la accesibilidad de variables de una función cuando es invocada y es único a cada invocación. En cambio, el contexto es siempre el valor de `this` cuya referencia es siempre el objeto que está ejecutando el código. 5 | 6 | ### Alcance de variables 7 | Las variables pueden ser declaradas con **alcance local** o **alcance global**, lo cual establece su accesibilidad desde diferentes alcances en tiempo de ejecución. Cualquier variable definida como global será accesible en tiempo de ejecución por cualquier alcance, ya que se habrá declarado fuera del cuerpo de una función. 8 | 9 | En cambio, las variables locales existen solamente dentro del cuerpo de una función. El alcance local de una variable solo se define a partir del cuerpo de la función que la contiene, ya que JavaScript hasta ahora no permitía definir un alcance local dentro de una condición `if`, bloques `switch`, o iteraciones `for` y `while`. 10 | 11 | ### Qué es el contexto `this` 12 | El contexto casi siempre viene determinado por la forma en que una función es invocada. Cuando se llama a la función como método de un objeto, `this` se asigna al objeto cuyo método está siendo ejecutado: 13 | 14 | ```javascript 15 | var obj = { 16 | foo: function() { 17 | console.log(this === obj); 18 | } 19 | }; 20 | 21 | obj.foo(); // true 22 | ``` 23 | 24 | El mismo principio se aplica cuando invocamos una función con el operador `new` para crear una instancia del objeto. Cuando se invoca de esta manera, el valor de `this` dentro del alcance de la función se asignará a la nueva instancia creada: 25 | 26 | ```javascript 27 | function foo() { 28 | console.log(this); 29 | } 30 | 31 | foo(); // window (if node, global) 32 | new foo(); // foo 33 | ``` 34 | 35 | ## Hoisting: Lidiando con el contexto en ECMAScript 5 36 | 37 | Tomemos como ejemplo la siguiente función en ES5: 38 | 39 | ```javascript 40 | (function() { 41 | console.log(x); // x no está definida aún. 42 | if(true) { 43 | var x = 'hola mundo'; 44 | } 45 | console.log(x); 46 | // Imprime "hola mundo", porque "var" hace que la variable sea global a la función 47 | })(); 48 | ``` 49 | 50 | Uno de los mayores problemas a los que nos hemos enfrentado siempre los desarrolladores de JavaScript es el conocido como **hoisting** ("elevación"). El término *hoisting*, el cual no está definido dentro del actual ECMAScript, es comúnmente utilizado para describir el particular comportamiento que JavaScript hace de las variables en el interior de las funciones. 51 | 52 | Se entiende como *hoisting* el comportamiento por defecto de cualquier intérprete de JavaScript, que ubica cualquier variable declarada al comienzo de su contexto. 53 | 54 | Dicho funcionamiento ha sido ampliamente discutido por la comunidad de desarrolladores sobre si se trataba de un *bug* o una *feature*. Por citar un ejemplo, **John Resig**, creador de **jQuery**, es un poderoso aliado a la hora de gestionar el alcance nuestras variables. Sin embargo hay otros que pensamos que se trata de una fuente irremediable de inconsistencias en nuestros códigos y errores potenciales difíciles de diagnosticar. 55 | 56 | Por ejemplo, observemos el siguiente código: 57 | 58 | ```javascript 59 | function foo(){ 60 | bar(); 61 | var x = 1; 62 | } 63 | ``` 64 | 65 | Cuando dicho código pase a ser tratado por un intérprete de JavaScript, se convertirá en lo siguiente: 66 | 67 | ```javascript 68 | function foo(){ 69 | var x; 70 | bar(); 71 | x = 1; 72 | } 73 | ``` 74 | 75 | Algo que podría parecer trivial, puede llegar a ocasionar comportamientos no esperados: 76 | 77 | ```javascript 78 | var x = 'Hello World'; // variable global 79 | 80 | (function foo() { 81 | console.log(x); // esperamos el valor global 82 | var x = 'New Value'; // redefinimos la variable en contexto local 83 | console.log(x); // esperamos el nuevo valor local 84 | })(); 85 | ``` 86 | 87 | En el ejemplo anterior, declaramos la variable `x` en el contexto global. Dentro de la función `foo()`, se define un nuevo contexto, local a la función declarada. En la primera sentencia esperamos que la consola imprima `Hello World`, para después re-definir el valor de `x` y volverla a imprimir por consola, esta vez con el nuevo valor: `New Value`. Sin embargo, lo que ocurre es lo siguiente: 88 | 89 | ```terminal 90 | > undefined 91 | > New Value 92 | ``` 93 | 94 | ¿Qué ha ocurrido? Por qué el intérprete imprime un `undefined` en lugar del esperado `Hello World`? 95 | 96 | La respuesta, como te imaginarás, es el **hoisting**. Al crear un nuevo contexto de función, la declaración de la variable se eleva hasta el inicio del nuevo contexto, quedando la función tal que así: 97 | 98 | ```javascript 99 | var x = 'Hello World'; 100 | 101 | (function foo(){ 102 | var x; 103 | console.log( x ); 104 | x = 'New Value'; 105 | console.log( x ); 106 | })(); 107 | ``` 108 | 109 | Como ves, `x` se *define* antes de la primera impresión por consola, sobrescribiendo el valor asignado en contexto global. Sin embargo, no es hasta después del primer `console.log` cuando se le asigna el nuevo valor. 110 | 111 | Esto, como imaginarás, genera todo tipo de inconsistencias en el código de aplicaciones complejas que obligan a pensar dos veces dónde declarar y asignar valores a nuestras variables. 112 | 113 | Por este motivo, se dice que es una buena práctica declarar las variables al principio del contexto, ya que así además incrementamos la legibilidad del código. 114 | 115 | ## Declaración de variables de ámbito local: `let` 116 | 117 | La sentencia `let` declara una variable de alcance local, la cual, opcionalmente, puede ser inicializada con algún valor. 118 | 119 | El alcance de `let` es local al bloque, declaración o expresión donde se está usando. Lo anterior diferencia la expresión `let` de la palabra reservada `var`, la cual define una variable global o local en una función sin importar el ámbito del bloque. 120 | 121 | Veamos algunos ejemplos: 122 | 123 | ```javascript 124 | if(x > y) { 125 | let gamma = 12.7 + y; 126 | i = gamma * x; 127 | } 128 | ``` 129 | 130 | En el ejemplo anterior, `gamma` solo existe dentro del bloque del `if`. 131 | 132 | ```javascript 133 | for(let i = 0; i < students.length; i++){ 134 | console.log(students[i].name); 135 | } 136 | ``` 137 | 138 | Podemos utilizar `let` para que la variable sea local al alcance del bucle `for`. Si en su lugar usáramos `var`, la variable sería visible en toda la función que contiene dicho bucle. 139 | 140 | ```javascript 141 | (function() { 142 | if(true){ 143 | let x = 'hola mundo'; 144 | } 145 | console.log(x); 146 | // Da error, porque "x" ha sido definida dentro del "if" 147 | })(); 148 | ``` 149 | 150 | A diferencia de ECMAScript 5, en ESCMAScript 6 el bloque de una sentencia condicional también actúa como ámbito de bloque. En el ejemplo `console.log(x)` no tiene acceso a `let x = "hola mundo"`. 151 | 152 | En el siguiente ejemplo la consola imprime `Hola Dani`, ya que la variable `x` en el bloque del `if` se mantiene dentro de su ámbito. 153 | 154 | ```javascript 155 | (function() { 156 | let x = 'Hola Dani'; 157 | 158 | if(true) { 159 | let x = 'Hola Joan'; 160 | } 161 | console.log(x); 162 | // Imprime en consola Hola Dani 163 | })(); 164 | ``` 165 | 166 | En el ejemplo anterior, la sentencia `console.log` da error, porque `x` ha sido definida dentro del bloque `if`. 167 | 168 | # Variables de solo lectura: `const` 169 | 170 | Las variables de solo lectura son otra de las novedades de ECMAScript 2015, mediante la introducción de la nueva palabra reservada `const`. Cualquier variable declarada como constante, será de solo lectura y su valor no podrá ser modificado. 171 | 172 | Veamos un ejemplo: 173 | 174 | ```javascript 175 | (function(){ 176 | const HELLO = 'hello world'; 177 | HELLO = 'hola mundo'; 178 | // Dará ERROR, ya que es de sólo lectura 179 | })(); 180 | ``` 181 | 182 | En este ejemplo vemos cómo desde el momento en que declaramos la constante `HELLO`, su valor queda blindado y el intérprete lanzará error al tratar de modificarlo. 183 | 184 | ```javascript 185 | (function() { 186 | const PI; 187 | PI = 3.15; 188 | // Dará ERROR, ya que ha de asignarse un valor en la declaración 189 | })(); 190 | ``` 191 | 192 | Pero, ¿qué pasa cuando la variable no se asigna a un valor, sino a un objeto? Veámoslo con un ejemplo: 193 | 194 | ```javascript 195 | const USER = { 196 | name: 'Daniel', 197 | surname: 'de la Cruz', 198 | age: 32 199 | }; 200 | 201 | USER.name = 'Joan'; // works, as we are modifying a property, but the object remains intact 202 | USER.age = 'treinta y dos'; // modifying a property type also works 203 | // USER = 'Daniel de la Cruz'; // fails, since the const type can't be modifyied 204 | 205 | console.log(USER); 206 | ``` 207 | --------------------------------------------------------------------------------