Consejos y trucos de ES6 para hacer que su código sea más limpio, más corto y más fácil de leer.

Consejos y trucos de ES6 para hacer que su código sea más limpio, más corto y más fácil de leer.

Literales de plantilla

Los literales de plantilla hacen que trabajar con cadenas sea mucho más fácil que antes. Se inician con una marca inversa y se pueden insertar variables con ${variable}. Compare estas dos líneas de código:

var fName = 'Peter', sName = 'Smith', age = 43, job= 'photographer';var a = 'Hi, I\'m ' + fName + ' ' + sName + ', I\'m ' + age + ' and work as a ' + job + '.';var b = `Hi, I'm ${ fName } ${ sName }, I'm ${ age } and work as a ${ job }.`;

Esto hace la vida mucho más sencilla y el código más fácil de leer. Puede poner cualquier cosa dentro de las llaves: variables, ecuaciones o llamadas a funciones. Los usaré en ejemplos a lo largo de este artículo.

Alcance del bloque de sintaxis

JavaScript siempre ha estado dentro del ámbito de las funciones, por lo que se había vuelto común envolver todo un archivo JavaScript en una expresión de función vacía inmediatamente invocada (IIFE). Esto se hace para aislar todas las variables en el archivo, por lo que no hay conflictos de variables.

Ahora, tenemos alcance de bloque y dos nuevas declaraciones de variables que están vinculadas a un bloque.

Declaración 'Let'

Esto es similar varpero tiene algunas diferencias notables. Debido a que tiene un alcance de bloque, se puede declarar una nueva variable con el mismo nombre sin afectar las variables externas.

var a = 'car' ;{ let a = 5; console.log(a) // 5}console.log(a) // car

Debido a que está vinculado a un alcance de bloque, resuelve esta clásica pregunta de entrevista:

"¿Qué es la salida y cómo conseguiría que funcione como espera?"

for (var i = 1; i  { console.log(i); }, 1000);}

En este caso, genera "5 5 5 5 5" porque la variable i cambia en cada iteración.

Si cambia el varpara let, todo cambia. Ahora, cada ciclo crea un nuevo alcance de bloque con el valor de i vinculado a ese ciclo. Aunque hayas escrito:

{let i = 1; setTimeout(() => { console.log(i) }, 1000)} {let i = 2; setTimeout(() => { console.log(i) }, 1000)} {let i = 3; setTimeout(() => { console.log(i) }, 1000)} {let i = 4; setTimeout(() => { console.log(i) }, 1000)} {let i = 5; setTimeout(() => { console.log(i) }, 1000)} 

Otra diferencia entre vary letes que letno está izado como varestá.

{ console.log(a); // undefined console.log(b); // ReferenceError var a = 'car'; let b = 5;}

Debido a su alcance más estricto y comportamiento más predecible, algunas personas han dicho que debería usar en letlugar de var, excepto cuando necesite específicamente la elevación o el alcance más flexible de la vardeclaración.

Const

Si antes deseaba declarar una variable constante en JavaScript, era una convención nombrar la variable en mayúsculas. Sin embargo, esto no protegería la variable, solo les permitiría a otros desarrolladores saber que era una constante y que no debería cambiarse.

Ahora tenemos la constdeclaración.

{ const c = "tree"; console.log(c); // tree c = 46; // TypeError! }

constno hace que la variable sea inmutable, simplemente bloquea su asignación. Si tiene una asignación compleja (objeto o matriz), el valor aún se puede modificar.

{ const d = [1, 2, 3, 4]; const dave = { name: 'David Jones', age: 32}; d.push(5); dave.job = "salesman"; console.log(d); // [1, 2, 3, 4, 5] console.log(dave); // { age: 32, job: "salesman", name: 'David Jones'}}

Problema con las funciones de alcance de bloques

Las declaraciones de funciones ahora se especifican para estar vinculadas al ámbito del bloque.

{ bar(); // works function bar() { /* do something */ }}bar(); // doesn't work

El problema surge cuando declaras una función dentro de una ifdeclaración.

Considera esto:

if ( something) { function baz() { console.log('I passed') }} else { function baz() { console.log('I didn\'t pass') } } baz();

Antes de ES6, ambas declaraciones de función se habrían elevado y el resultado habría sido 'I didn\'t pass'sin importar cuál somethingfuera.

Ahora tenemos 'ReferenceError', como bazsiempre está limitado por el alcance del bloque.

Propagar

ES6 presenta al ...operador, que se conoce como el "operador de propagación". Tiene dos usos principales: extender una matriz u objeto en una nueva matriz u objeto y unir varios parámetros en una matriz.

El primer caso de uso es el que probablemente encontrará más, así que lo veremos primero.

let a = [3, 4, 5];let b = [1, 2, ...a, 6];console.log(b); // [1, 2, 3, 4, 5, 6]

Esto puede ser muy útil para pasar un conjunto de variables a una función desde una matriz.

function foo(a, b, c) { console.log(`a=${a}, b=${b}, c=${c}`)} let data = [5, 15, 2];foo( ...data); // a=5, b=15, c=2

Un objeto también se puede extender, ingresando cada uno de los pares clave-valor en el nuevo objeto. (La extensión de objetos se encuentra en la etapa 4 de la propuesta y estará oficialmente en ES2018. Solo es compatible con Chrome 60 o posterior, Firefox 55 o posterior y Node 6.4.0 o posterior)

let car = { type: 'vehicle ', wheels: 4};let fordGt = { make: 'Ford', ...car, model: 'GT'};console.log(fordGt); // {make: 'Ford', model: 'GT', type: 'vehicle', wheels: 4}

Otra característica del operador de propagación es que crea una nueva matriz u objeto. El siguiente ejemplo crea una nueva matriz para b, pero csolo se refiere a la misma matriz.

let a = [1, 2, 3];let b = [ ...a ];let c = a;b.push(4);console.log(a); // [1, 2, 3]console.log(b); // [1, 2, 3, 4] referencing different arraysc.push(5);console.log(a); // [1, 2, 3, 5] console.log(c); // [1, 2, 3, 5] referencing the same array

The second use case is gathering variables together into an array. This is very useful for when you don’t know how many variables are being passed to a function.

function foo(...args) { console.log(args); } foo( 'car', 54, 'tree'); // [ 'car', 54, 'tree' ] 

Default Parameters

Functions can now be defined with default parameters. Missing or undefined values are initialized with the default value. Just be careful — because null and false values are coerced to 0.

function foo( a = 5, b = 10) { console.log( a + b);} foo(); // 15foo( 7, 12 ); // 19foo( undefined, 8 ); // 13foo( 8 ); // 18foo( null ); // 10 as null is coerced to 0

The default values can be more than just values — they can also be expressions or functions.

function foo( a ) { return a * 4; }function bar( x = 2, y = x + 4, z = foo(x)) { console.log([ x, y, z ]);}bar(); // [ 2, 6, 8 ]bar( 1, 2, 3 ); //[ 1, 2, 3 ] bar( 10, undefined, 3 ); // [ 10, 14, 3 ]

Destructuring

Destructuring is the process of taking apart the array or object on the left hand side of the equal sign. The array or object can come from a variable, function, or equation.

let [ a, b, c ] = [ 6, 2, 9];console.log(`a=${a}, b=${b}, c=${c}`); //a=6, b=2, c=9
function foo() { return ['car', 'dog', 6 ]; } let [ x, y, z ] = foo();console.log(`x=${x}, y=${y}, z=${z}`); // x=car, y=dog, z=6

With object destructuring, the keys of the object can be listed inside curly braces to extract that key-value pair.

function bar() { return {a: 1, b: 2, c: 3}; }let { a, c } = bar();console.log(a); // 1console.log(c); // 3console.log(b); // undefined

Sometimes, you want to extract the values but assign them to a new variable. This is done using a 'key: variable' pairing on the left of the equals sign.

function baz() { return { x: 'car', y: 'London', z: { name: 'John', age: 21} }; }let { x: vehicle, y: city, z: { name: driver } } = baz();
console.log( `I'm going to ${city} with ${driver} in their ${vehicle}.`); // I'm going to London with John in their car. 

Another thing that object destructuring allows is assigning a value to multiple variables.

let { x: first, x: second } = { x: 4 };console.log( first, second ); // 4, 4

Object Literals and Concise Parameters

When you are creating an object literal from variables, ES6 allows you to omit the key if it is the same as the variable name.

let a = 4, b = 7;let c = { a: a, b: b };let concise = { a, b };console.log(c, concise) // {a: 4, b: 7}, {a: 4, b: 7}

This can also be used in combination with destructuring to make your code much simpler and cleaner.

function foo() { return { name: 'Anna', age: 56, job: { company: 'Tesco', title: 'Manager' } };} 
// pre ES6let a = foo(), name = a.name, age = a.age, company = a.job.company;
// ES6 destructuring and concise parameters let { name, age, job: {company}} = foo();

It can also be used to destructure objects passed into functions. Method 1 and 2 are how you would have done it before ES6, and method 3 uses destructuring and concise parameters.

let person = { name: 'Anna', age: 56, job: { company: 'Tesco', title: 'Manager' }};
// method 1function old1( person) { var yearOfBirth = 2018 - person.age; console.log( `${ person.name } works at ${ person.job.company } and was born in ${ yearOfBirth }.`);}
// method 2function old1( person) { var age = person.age, yearOfBirth = 2018 - age, name = person.name, company = person.job.company; console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`);} 
// method 3function es6({ age, name, job: {company}}) { var yearOfBirth = 2018 - age; console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`);} 

Using ES6, we can extract the age, name and company without extra variable declaration.

Dynamic Property Names

ES6 adds the ability to create or add properties with dynamically assigned keys.

let city= 'sheffield_';let a = { [ city + 'population' ]: 350000};a[ city + 'county' ] = 'South Yorkshire';console.log(a); // {sheffield_population: 350000, sheffield_county: 'South Yorkshire' }

Arrow Functions

Arrow functions have two main aspects: their structure and their this binding.

They can have a much simpler structure than traditional functions because they don't need the function key word, and they automatically return whatever is after the arrow.

var foo = function( a, b ) { return a * b;} 
let bar = ( a, b ) => a * b;

If the function requires more than a simple calculation, curly braces can be used and the function returns whatever is returned from the block scope.

let baz = ( c, d ) => { let length = c.length + d.toString().length; let e = c.join(', '); return `${e} and there is a total length of ${length}`;}

One of the most useful places for arrow functions is in array functions like .map, .forEach or .sort.

let arr = [ 5, 6, 7, 8, 'a' ];let b = arr.map( item => item + 3 );console.log(b); // [ 8, 9, 10, 11, 'a3' ]

As well as having a shorter syntax, it also fixes the issues that often arose around the this binding behaviour. The fix with pre-ES6 functions was to store the this reference, often as a self variable.

var clickController = { doSomething: function (..) { var self = this; btn.addEventListener( 'click', function() { self.doSomething(..) }, false ); } };

This had to be done because the this binding is dynamic. This means that the this inside the event listener and the this inside the doSomething do not refer to the same thing.

Inside arrow functions, the this binding is lexical, not dynamic. This was the main design feature of the arrow function.

Whilst lexical this binding can be great, sometimes that's not what is wanted.

let a = { oneThing: ( a ) => { let b = a * 2; this.otherThing(b); }, otherThing: ( b ) => {....} };
a.oneThing(6);

When we use a.oneThing(6), the this.otherThing( b ) reference fails as this doesn't point to the a object, but to the surrounding scope. If you are rewriting legacy code using ES6 syntax, this is something to watch out for.

for … of Loops

ES6 adds a way to iterate over each of the values in an array. This is different from the existing for ... in loop that loops over the key/index.

let a = ['a', 'b', 'c', 'd' ];// ES6 for ( var val of a ) { console.log( val );} // "a" "b" "c" "d"// pre-ES6 for ( var idx in a ) { console.log( idx );} // 0 1 2 3

Using the new for … of loop saves adding a let val = a[idx] inside each loop.

Arrays, strings, generators and collections are all iterable in standard JavaScript. Plain objects can't normally be iterated over, unless you have defined an iterator for it.

Number Literals

ES5 code handled decimal and hexadecimal number formats well, but octal form wasn't specified. In fact, it was actively disallowed in strict mode.

ES6 ha agregado un nuevo formato, agregando una odespués de la inicial 0para declarar el número en un octal. También han agregado un formato binario.

Number( 29 ) // 29Number( 035 ) // 35 in old octal form. Number( 0o35 ) // 29 in new octal form Number( 0x1d ) // 29 in hexadecimal Number( 0b11101 ) // 29 in binary form

Y mucho más…

Hay mucho, mucho más que ES6 nos ofrece para hacer nuestro código más limpio, más corto, más fácil de leer y más robusto. Mi objetivo es escribir una continuación de este artículo que cubra los bits menos conocidos de ES6.

Si no puede esperar tanto, lea el libro You Don't Know JS de Kyle Simpson en ES6, o visite este pequeño y brillante sitio web.

¿Quieres convertirte en desarrollador y conseguir tu primer trabajo de software? Descargue los 7 pasos para convertirse en desarrollador y obtener su primer trabajo.

SIGUIENTE -> Cómo asegurar el trabajo de sus sueños. Domina el proceso de entrevista

Si le gustó esto y lo encontró útil, ¡demuestre su apoyo aplaudiendo y suscríbase para obtener más artículos como este!