|
| 1 | + |
| 2 | + |
| 3 | +# Rambling Javascript #2: Fat arrow functions |
| 4 | + |
| 5 | +Continuamos nuestro camino indagando sobre diversos aspectos de JavaScript. En este caso vamos a comentar sobre uno de los añadidos más relevantes que se hicieron con _ES6_ (ó _EcmaScript 2015_). Estamos hablando de las _Fat arrow functions_. Con su uso conseguiremos un código más legible y compacto. |
| 6 | +Sin embargo, es conveniente hacer hincapié en algunos puntos que pueden pasarse de largo a la hora de trabajar con ellas. Comenzamos. |
| 7 | + |
| 8 | +## Sintaxis |
| 9 | + |
| 10 | +Tomaremos como base un ejemplo básico de función escrita en _ES5_. En este caso la función ```add``` tiene como argumentos dos números y nos devuelve la suma. |
| 11 | + |
| 12 | +```javascript |
| 13 | +const add = function(a,b) { |
| 14 | + return a + b; |
| 15 | +} |
| 16 | +``` |
| 17 | + |
| 18 | +Código totalmente correcto, pero quizás un pelín verboso para lo que realmente hace, que es la suma de dos números. De ahí partimos con las _fat arrow function_. En un primer momento podemos sustituir la palabra reservada ```function``` por el símbolo siguiente ```=>```, conocido como _fat arrow_, quedando la expresión de la siguiente forma: |
| 19 | + |
| 20 | +```javascript |
| 21 | +const add = (a,b) => { |
| 22 | + return a + b; |
| 23 | +} |
| 24 | +``` |
| 25 | +Hemos reducido algunos caracteres, pero la mejora tampoco es demasiado sustancial. Sin embargo, podemos seguir trabajando en la expresión. El siguiente aspecto en el que podemos dar cuenta, es que nuestra función consta de una única expresión. En casos como éste, podemos igualmente quitar la palabra reservada ```return``` e, incluso, quitar las llaves delimitadora de la función. Con ello, la palabra reservada ```return``` quedaría implícita en la única expresión de la que constaría nuestra función. Por lo tanto: |
| 26 | + |
| 27 | +```javascript |
| 28 | +const add = (a,b) => a + b; |
| 29 | +``` |
| 30 | + |
| 31 | +Creo que se intuye que extendiendo su uso conseguiremos un código más claro y limpio. |
| 32 | + |
| 33 | +El siguiente ejemplo también nos permite presentar un nuevo detalle relativo a la sintaxis: |
| 34 | + |
| 35 | + |
| 36 | + |
| 37 | +Efectivamente, también hay puntos que aclarar sobre la sintaxis relativo a los argumentos: |
| 38 | +* En funciones sin argumentos, o con dos o más, es necesario rodearlos con paréntesis: |
| 39 | + |
| 40 | +```javascript |
| 41 | +const sayHelloWorld = () => 'Hello World'; |
| 42 | + |
| 43 | +const sayHelloWorldTwo = (place, hour) => `Hello ${place} at ${hour}` |
| 44 | +``` |
| 45 | + |
| 46 | +* Sin embargo, cuando tengamos un sólo argumento, los paréntesis son opcionales, tal y como hemos visto en el ejemplo previo. |
| 47 | + |
| 48 | +## Uso en casos reales |
| 49 | + |
| 50 | +Todo lo que se ha comentado está bien, pero realmente, y con los ejemplos vistos, tampoco parece un cambio demasiado relevante. |
| 51 | +Sin embargo, cuando nos vamos a casos un poco más realistas, es donde empezamos a observar su valía. El ejemplo más claro en el que es muy útil su uso es en los predicados que utilizamos en las funciones para tratar con colecciones. |
| 52 | +Mostramos el típico ejemplo de un array de números, y queremos extraer aquellos cuyo valor sea mayor o igual que 5: |
| 53 | + |
| 54 | +```javascript |
| 55 | +const numbers = [10,2,3,4,5,8] |
| 56 | + |
| 57 | +const result = numbers.filter(function(number){ |
| 58 | + return number >= 5; |
| 59 | +}); |
| 60 | +``` |
| 61 | + |
| 62 | +En este caso, utilizando _fat arrow functions_, nuestro ejemplo quedaría de la siguiente forma: |
| 63 | + |
| 64 | +```javascript |
| 65 | +const numbers = [10,2,3,4,5,8] |
| 66 | + |
| 67 | +const result = numbers.filter(number => number >= 5); |
| 68 | +``` |
| 69 | + |
| 70 | +La mejora queda clara en cuanto a legibilidad. Imaginemos su uso en callbacks, donde ayudaran, sino a reducir, sí a mejorar, el temido [callback hell](http://callbackhell.com/) en javascript. |
| 71 | + |
| 72 | +## Comportamiento de ```this``` |
| 73 | + |
| 74 | +Todo lo comentado tiene su valor, pero donde realmente está el factor diferenciador de las fat arrow function respecto al empleo de las funciones tradicionales es en el cambio de comportamiento que ha sufrido ```this```. Cualquier persona que haya trabajado con un código JavaScript relativamente complejo, saliendo de los ejemplos básicos, habrá obtenido comportamientos no esperables de ```this```. ¿Cuántos hemos hecho uso de alguno de los siguientes _workarounds_?: |
| 75 | +* Guardarnos el contexto en una variable para poder utilizarlo en el callback. Todos hemos hecho alguna vez: |
| 76 | + |
| 77 | +```javascript |
| 78 | +function test(){ |
| 79 | + var self = this; |
| 80 | + callOtherFunction(function(callbackResponse){ |
| 81 | + console.log(this); |
| 82 | + console.log(self); |
| 83 | + }) |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +* Utilizar el ```bind(this)```, a la hora de definir una función. |
| 88 | + |
| 89 | +Con el uso de las _fat arrow functions_ esto ya no será necesario, ya que, por defecto, ```this``` se va a referir al contexto donde la función haya sido declarada. Lo vemos con el ejemplo siguiente: |
| 90 | + |
| 91 | +```javascript |
| 92 | +const testObject = { |
| 93 | + numbers: [10,20,30,40,50], |
| 94 | + title: 'Double of', |
| 95 | + double: function(){ |
| 96 | + return this.numbers.map(function(number){ |
| 97 | + return `${this.title} ${number} is ${2*number}`; |
| 98 | + }) |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +console.log(testObject.double()); |
| 103 | + |
| 104 | +``` |
| 105 | + |
| 106 | +Si ejecutamos el ejemplo obtenemos lo siguiente: |
| 107 | + |
| 108 | +```javascript |
| 109 | +["undefined 10 is 20", "undefined 20 is 40", "undefined 30 is 60", "undefined 40 is 80", "undefined 50 is 100"] |
| 110 | +``` |
| 111 | + |
| 112 | +¿Qué está ocurriendo? Pues lo que todos sabíamos de antemano, el ```this``` dentro de la función que se ejecuta en el _map_ no es el que cabría esperarse (el objeto _testObject_, sino que es el contexto global, donde, lógicamente, no está definido _title_). Esto lo resolveríamos en _ES5_ con una de las formas siguientes: |
| 113 | + |
| 114 | +```javascript |
| 115 | +const testObject = { |
| 116 | + numbers: [10,20,30,40,50], |
| 117 | + title: 'Double of', |
| 118 | + double: function(){ |
| 119 | + const self = this; |
| 120 | + return this.numbers.map(function(number){ |
| 121 | + return `${self.title} ${number} is ${2*number}`; |
| 122 | + }) |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +console.log(testObject.double()); |
| 127 | +``` |
| 128 | + |
| 129 | +```javascript |
| 130 | +const testObject = { |
| 131 | + numbers: [10,20,30,40,50], |
| 132 | + title: 'Double of', |
| 133 | + double: function(){ |
| 134 | + return this.numbers.map(function(number){ |
| 135 | + return `${this.title} ${number} is ${2*number}`; |
| 136 | + }.bind(this)); |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +console.log(testObject.double()); |
| 141 | +``` |
| 142 | + |
| 143 | +Mientras que, usando _fat arrow function_, el ejemplo quedaría: |
| 144 | + |
| 145 | +```javascript |
| 146 | +const testObject = { |
| 147 | + numbers: [10,20,30,40,50], |
| 148 | + title: 'Double of', |
| 149 | + double: function() { |
| 150 | + return this.numbers.map(number => |
| 151 | + `${this.title} ${number} is ${2*number}`); |
| 152 | + } |
| 153 | +}; |
| 154 | + |
| 155 | +console.log(testObject.double()); |
| 156 | +``` |
| 157 | + |
| 158 | +En todos los casos el resultado sería el esperado: |
| 159 | + |
| 160 | +```javascript |
| 161 | +["Double of 10 is 20", "Double of 20 is 40", "Double of 30 is 60", "Double of 40 is 80", "Double of 50 is 100"] |
| 162 | +``` |
| 163 | + |
| 164 | + |
| 165 | +### Conclusiones |
| 166 | +En este caso no hay mucho que comentar: |
| 167 | +* Sintaxis más sencilla. |
| 168 | +* Código más legible. |
| 169 | +* Facilidad a la hora de escribir callbacks o predicados. |
| 170 | +* Quita problemas al usar ```this``` |
| 171 | + |
| 172 | +Todo son ventajas. |
| 173 | + |
| 174 | +En la siguiente entrega hablaremos de los operadores rest / spread y de los valores por defecto que podemos indicar a nuestras funciones. |
| 175 | + |
| 176 | +## Links |
| 177 | +* [Practica ES6](http://jsbin.com/?console,output) |
0 commit comments