Las 10 características principales de ES6 que todo desarrollador JavaScript ocupado debe conocer

Hace poco fui a la conferencia HTML5 Dev en San Francisco. La mitad de las charlas a las que asistí fueron sobre ES6 o, como ahora se llama oficialmente, ECMAScript2015. Sin embargo, prefiero el ES6 más sucinto.

Este ensayo le dará una introducción rápida a ES6. Si no sabe qué es ES6, es una nueva implementación de JavaScript. Si es un ingeniero de software de JavaScript ocupado (¿y quién no lo es?), siga leyendo para conocer las 10 mejores funciones de la nueva generación del lenguaje de programación más popular:JavaScript.

Esta es la lista de las 10 mejores funciones de ES6 para un ingeniero de software ocupado (sin ningún orden en particular):

  1. Parámetros predeterminados en ES6
  2. Literales de plantilla en ES6
  3. Cadenas multilínea en ES6
  4. Desestructuración de una tarea en ES6
  5. Literales de objetos mejorados en ES6
  6. Funciones de flecha en ES6
  7. Promesas en ES6
  8. Construcciones de ámbito de bloque Let y Const
  9. Clases en ES6
  10. Módulos en ES6

Descargo de responsabilidad:la lista es muy parcial y subjetiva. De ninguna manera se pretendía disminuir la utilidad de otras características de ES6, que no llegaron a la lista simplemente porque tuve que limitar el número a 10.

Primero, un poco de historia porque los que no conocen la historia no pueden hacerlo. Esta es una breve línea de tiempo de JavaScript:

  1. 1995:JavaScript nace como LiveScript
  2. 1997:se establece el estándar ECMAScript
  3. 1999:sale ES3 y IE5 está de moda
  4. 2000–2005:XMLHttpRequest, también conocido como AJAX, gana popularidad en aplicaciones como Outlook Web Access (2000) y Oddpost (2002), Gmail (2004) y Google Maps (2005).
  5. 2009:sale ES5 (esto es lo que la mayoría de nosotros usamos ahora) con forEach , Object.keys , Object.create (especialmente para Douglas Crockford) y JSON estándar
  6. 2015:sale ES6/ECMAScript2015; tiene principalmente azúcar sintáctico, porque las personas no pudieron ponerse de acuerdo en algo más innovador (¿ES7?)

Suficiente con la historia, pasemos al negocio de la codificación.

1. Parámetros predeterminados en ES6

Recuerde que teníamos que hacer estas declaraciones para definir los parámetros predeterminados:

var link = function (height, color, url) {
    var height = height || 50
    var color = color || 'red'
    var url = url || 'http://azat.co'
    ...
}

Estaban bien hasta que el valor era 0 y debido a que 0 es falso en JavaScript, por defecto sería el valor codificado en lugar de convertirse en el valor mismo. Por supuesto, quién necesita 0 como valor (#sarcasmfont), así que simplemente ignoramos esta falla y usamos la lógica O de todos modos... ¡No más! En ES6, podemos poner los valores por defecto justo en la firma de las funciones:

var link = function(height = 50, color = 'red', url = 'http://azat.co') {
  ...
}

Por cierto, ¡esta sintaxis es similar a Ruby!

2. Literales de plantilla en ES6

Los literales de plantilla o la interpolación en otros idiomas es una forma de generar variables en la cadena. Así que en ES5 tuvimos que romper la cadena así:

var name = 'Your name is ' + first + ' ' + last + '.'
var url = 'http://localhost:3000/api/messages/' + id

Afortunadamente, en ES6 podemos usar una nueva sintaxis ${NAME} dentro de la cadena marcada al revés:

var name = `Your name is ${first} ${last}.`
var url = `http://localhost:3000/api/messages/${id}`

3. Cadenas multilínea en ES6

Otro azúcar sintáctico delicioso es la cadena de varias líneas. En ES5, tuvimos que usar uno de estos enfoques:

var roadPoem = 'Then took the other, as just as fair,\n\t'
    + 'And having perhaps the better claim\n\t'
    + 'Because it was grassy and wanted wear,\n\t'
    + 'Though as for that the passing there\n\t'
    + 'Had worn them really about the same,\n\t'

var fourAgreements = 'You have the right to be you.\n\
    You can only be you when you do your best.'

Mientras esté en ES6, simplemente utilice los acentos graves:

var roadPoem = `Then took the other, as just as fair,
    And having perhaps the better claim
    Because it was grassy and wanted wear,
    Though as for that the passing there
    Had worn them really about the same,`

var fourAgreements = `You have the right to be you.
    You can only be you when you do your best.`

4. Asignación de desestructuración en ES6

La desestructuración puede ser un concepto más difícil de entender, porque está ocurriendo algo de magia... digamos que tiene asignaciones simples donde las teclas house y mouse son variables house y mouse :

[Nota al margen]

Leer publicaciones de blog es bueno, pero ver cursos en video es aún mejor porque son más atractivos.

Muchos desarrolladores se quejaron de la falta de material de video de calidad asequible en Node. Es una distracción ver videos de YouTube y una locura pagar $ 500 por un curso de video de Node.

Visite Node University, que tiene cursos de video GRATUITOS en Node:node.university.

[Fin de la nota al margen]

var data = $('body').data(), // data has properties house and mouse
  house = data.house,
  mouse = data.mouse

Otros ejemplos de asignaciones de desestructuración (de Node.js):

var jsonMiddleware = require('body-parser').json

var body = req.body, // body has username and password
  username = body.username,
  password = body.password  

En ES6, podemos reemplazar el código ES5 anterior con estas declaraciones:

var {house, mouse} = $('body').data() // we'll get house and mouse variables

var {json: jsonMiddleware} = require('body-parser')

var {username, password} = req.body

Esto también funciona con arreglos. ¡Loco!

var [col1, col2]  = $('.column'),
  [line1, line2, line3, , line5] = file.split('\n')

Puede llevar algo de tiempo acostumbrarse a la sintaxis de asignación de desestructuración, pero es un dulce recubrimiento de azúcar.

5. Literales de objetos mejorados en ES6

¡Lo que puedes hacer con objetos literales ahora es alucinante! Pasamos de una versión glorificada de JSON en ES5 a algo muy parecido a las clases en ES6.

Aquí hay un literal de objeto ES5 típico con algunos métodos y atributos/propiedades:

var serviceBase = {port: 3000, url: 'azat.co'},
    getAccounts = function(){return [1,2,3]}

var accountServiceES5 = {
  port: serviceBase.port,
  url: serviceBase.url,
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf())
  },
  getUrl: function() {return "http://" + this.url + ':' + this.port},
  valueOf_1_2_3: getAccounts()
}

Si queremos ser elegantes, podemos heredar de serviceBase convirtiéndolo en el prototipo con el Object.create método:

var accountServiceES5ObjectCreate = Object.create(serviceBase)
var accountServiceES5ObjectCreate = {
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf())
  },
  getUrl: function() {return "http://" + this.url + ':' + this.port},
  valueOf_1_2_3: getAccounts()
}

Lo sé, accountServiceES5ObjectCreate y accountServiceES5 NO son totalmente idénticos, porque un objeto (accountServiceES5 ) tendrá las propiedades en el __proto__ objeto como se muestra a continuación:

Literales de objetos mejorados en ES6

Pero por el bien del ejemplo, los consideraremos similares. Entonces, en el objeto literal ES6, hay abreviaturas para la asignación getAccounts: getAccounts, se convierte en solo getAccounts, . Además, configuramos el prototipo allí mismo en el __proto__`` property which makes sense (not 'proto ’` aunque:

var serviceBase = {port: 3000, url: 'azat.co'},
    getAccounts = function(){return [1,2,3]}
var accountService = {
    __proto__: serviceBase,
    getAccounts,

Además, podemos invocar super y tener claves dinámicas (valueOf_1_2_3 ):

    toString() {
     return JSON.stringify((super.valueOf()))
    },
    getUrl() {return "http://" + this.url + ':' + this.port},
    [ 'valueOf_' + getAccounts().join('_') ]: getAccounts()
};
console.log(accountService)

Literales de objetos mejorados en ES6 II

¡Esta es una gran mejora para los buenos objetos literales antiguos!

6. Funciones de flecha en ES6

Esta es probablemente una característica que más esperé. Me encanta CoffeeScript por sus flechas gordas. Ahora los tenemos en ES6. Las flechas gordas son asombrosas porque harían que tu this comportarse correctamente, es decir, this tendrá el mismo valor que en el contexto de la función, no mutará. La mutación generalmente ocurre cada vez que crea un cierre.

El uso de funciones de flechas en ES6 nos permite dejar de usar that = this o self = this o _this = this o .bind(this) . Por ejemplo, este código en ES5 es feo:

var _this = this
$('.btn').click(function(event){
  _this.sendData()
})

Este es el código ES6 sin _this = this :

$('.btn').click((event) =>{
  this.sendData()
})

Lamentablemente, el comité de ES6 decidió que tener flechas delgadas es demasiado bueno para nosotros y nos dejó con un viejo y detallado function en cambio. (La flecha delgada en CoffeeScript funciona como function regular en ES5 y ES6).

Aquí hay otro ejemplo en el que usamos call para pasar el contexto al logUpperCase() función en ES5:

var logUpperCase = function() {
  var _this = this

  this.string = this.string.toUpperCase()
  return function () {
    return console.log(_this.string)
  }
}

logUpperCase.call({ string: 'es6 rocks' })()

Mientras que en ES6, no necesitamos perder el tiempo con _this :

var logUpperCase = function() {
  this.string = this.string.toUpperCase()
  return () => console.log(this.string)
}

logUpperCase.call({ string: 'es6 rocks' })()

Tenga en cuenta que puede mezclar y combinar el viejo function con => en ES6 como mejor le parezca. Y cuando se usa una función de flecha con una declaración de una línea, se convierte en una expresión, es decir,. implícitamente devolverá el resultado de esa declaración única. Si tiene más de una línea, deberá usar return explícitamente.

Este código ES5 está creando una matriz desde el messages matriz:

var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9']
var messages = ids.map(function (value) {
  return "ID is " + value // explicit return
})

Se convertirá en esto en ES6:

var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9']
var messages = ids.map(value => `ID is ${value}`) // implicit return

¿Te das cuenta de que usé las plantillas de cadena? Otra función de CoffeeScript... ¡Me encantan!

El paréntesis () son opcionales para parámetros individuales en una firma de función de flecha. Los necesita cuando usa más de un parámetro.

En ES5 el código tiene function con retorno explícito:

var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9'];
var messages = ids.map(function (value, index, list) {
  return 'ID of ' + index + ' element is ' + value + ' ' // explicit return
})

Y una versión más elocuente del código en ES6 con paréntesis alrededor de parámetros y retorno implícito:

var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9']
var messages = ids.map((value, index, list) => `ID of ${index} element is ${value} `) // implicit return

7. Promesas en ES6

Las promesas han sido un tema controvertido. Hubo muchas implementaciones prometedoras con una sintaxis ligeramente diferente. q, bluebird, deferred.js, vote, avow, jquery deferred, por nombrar solo algunos. Otros dijeron que no necesitamos promesas y que solo podemos usar async, generadores, devoluciones de llamadas, etc. Con mucho gusto, hay un Promise estándar implementación en ES6 ahora!

Consideremos un ejemplo bastante trivial de una ejecución asíncrona retrasada con setTimeout() :

setTimeout(function(){
  console.log('Yay!')
}, 1000)

Podemos volver a escribir el código en ES6 con Promise:

var wait1000 =  new Promise(function(resolve, reject) {
  setTimeout(resolve, 1000)
}).then(function() {
  console.log('Yay!')
})

O con funciones de flecha ES6:

var wait1000 =  new Promise((resolve, reject)=> {
  setTimeout(resolve, 1000)
}).then(()=> {
  console.log('Yay!')
})

Hasta ahora, hemos aumentado la cantidad de líneas de código de tres a cinco sin ningún beneficio evidente. Así es. El beneficio vendrá si tenemos más lógica anidada dentro del setTimeout() devolución de llamada:

setTimeout(function(){
  console.log('Yay!')
  setTimeout(function(){
    console.log('Wheeyee!')
  }, 1000)
}, 1000)

Se puede reescribir con promesas de ES6:

var wait1000 =  ()=> new Promise((resolve, reject)=> {setTimeout(resolve, 1000)})

wait1000()
  .then(function() {
    console.log('Yay!')
    return wait1000()
  })
  .then(function() {
    console.log('Wheeyee!')
  })

¿Aún no está convencido de que Promises sea mejor que las devoluciones de llamada regulares? A mí tampoco. Creo que una vez que se te ocurrió la idea de las devoluciones de llamada y las entendiste, no hay necesidad de una complejidad adicional de promesas.

Sin embargo, ES6 tiene Promesas para aquellos de ustedes que las aman. Las promesas también tienen una devolución de llamada de fallar y atrapar todo, lo cual es una buena característica. Eche un vistazo a esta publicación para obtener más información sobre las promesas:Introducción a ES6 Promises .

8. Construcciones con ámbito de bloque Let y Const

Es posible que ya hayas visto el sonido extraño let en código ES6. Recuerdo la primera vez que estuve en Londres, estaba confundido por todos esos carteles de SE ALQUILA. El ES6 let no tiene nada que ver con el alquiler. Esta no es una característica de recubrimiento de azúcar. Es más complicado. let es un nuevo var lo que permite ampliar el alcance de la variable a los bloques. Definimos bloques por las llaves. En ES5, los bloques no hicieron NADA a las vars:

function calculateTotalAmount (vip) {
  var amount = 0
  if (vip) {
    var amount = 1
  }
  { // more crazy blocks!
    var amount = 100
    {
      var amount = 1000
      }
  }  
  return amount
}

console.log(calculateTotalAmount(true))

El resultado será 1000. ¡Guau! Ese es un error realmente malo. En ES6, usamos let para restringir el alcance a los bloques. Vars tienen un alcance de función.

function calculateTotalAmount (vip) {
  var amount = 0 // probably should also be let, but you can mix var and let
  if (vip) {
    let amount = 1 // first amount is still 0
  } 
  { // more crazy blocks!
    let amount = 100 // first amount is still 0
    {
      let amount = 1000 // first amount is still 0
      }
  }  
  return amount
}

console.log(calculateTotalAmount(true))

El valor es 0, porque el if bloque también tiene let . Si no tuviera nada (amount=1 ), entonces la expresión habría sido 1.

Cuando se trata de const , las cosas son más fáciles; es solo un inmutable, y también tiene un alcance de bloque como let . Solo para demostrar, aquí hay un montón de constantes y todas están bien porque pertenecen a diferentes bloques:

function calculateTotalAmount (vip) {
  const amount = 0  
  if (vip) {
    const amount = 1 
  } 
  { // more crazy blocks!
    const amount = 100 
    {
      const amount = 1000
      }
  }  
  return amount
}

console.log(calculateTotalAmount(true))

En mi humilde opinión, let y const complicar demasiado el lenguaje. Sin ellos teníamos un solo comportamiento, ahora hay múltiples escenarios a considerar.;-(

9. Clases en ES6

Si te encanta la programación orientada a objetos (POO), entonces te encantará esta función. Hace que escribir clases y heredar de ellas sea tan fácil como hacer clic en Me gusta en un comentario en Facebook.

La creación y el uso de clases en ES5 fue un fastidio, porque no había una palabra clave class (estaba reservado pero no hizo nada). Además de eso, muchos patrones de herencia como pseudoclásico, clásico, funcional simplemente se sumaron a la confusión, echando gasolina al fuego de las guerras religiosas de JavaScript.

No le mostraré cómo escribir una clase (sí, sí, hay clases, los objetos se heredan de los objetos) en ES5, porque hay muchos sabores. Echemos un vistazo al ejemplo de ES6 de inmediato. Puedo decirles que la clase ES6 usará prototipos, no el enfoque de fábrica de funciones. Tenemos una clase baseModel en el que podemos definir un constructor y un getName() método:

class baseModel {
  constructor(options = {}, data = []) { // class constructor
    this.name = 'Base'
    this.url = 'http://azat.co/api'
    this.data = data
    this.options = options
  }

    getName() { // class method
      console.log(`Class name: ${this.name}`)
    }
}

Tenga en cuenta que estoy usando valores de parámetros predeterminados para opciones y datos. Además, los nombres de los métodos no necesitan tener la palabra function o los dos puntos (: ) más. La otra gran diferencia es que no puedes asignar propiedades this.NAME de la misma manera que los métodos, es decir, no puedes decir name al mismo nivel de sangría que un método. Para establecer el valor de una propiedad, simplemente asigne un valor en el constructor.

El AccountModel hereda de baseModel con class NAME extends PARENT_NAME :

class AccountModel extends baseModel {
  constructor(options, data) {

Para llamar al constructor principal, invoque sin esfuerzo super() con parámetros:

    super({private: true}, ['32113123123', '524214691']) //call the parent method with super
     this.name = 'Account Model'
     this.url +='/accounts/'
   }

Si quieres ser realmente elegante, puedes configurar un getter como este y accountsData será una propiedad:

 get accountsData() { //calculated attribute getter
    // ... make XHR
    return this.data
  }
}

Entonces, ¿cómo usas realmente este abracadabra? Es tan fácil como engañar a un niño de tres años para que piense que Santa Claus es real:

let accounts = new AccountModel(5)
accounts.getName()
console.log('Data is %s', accounts.accountsData)

En caso de que te lo estés preguntando, el resultado es:

Class name: Account Model
Data is %s 32113123123,524214691

10. Módulos en ES6

Como puede saber ahora, no había compatibilidad con módulos nativos en JavaScript antes de ES6. A la gente se le ocurrió AMD, RequireJS, CommonJS y otras soluciones. Ahora hay módulos con import y export operandos.

En ES5 usarías <script> etiquetas con IIFE, o alguna biblioteca como AMD, mientras que en ES6 puedes exponer tu clase con export . Soy un tipo de Node.js, así que usaré CommonJS, que también es una sintaxis de Node.js. Es sencillo usar CommonJS en el navegador con el paquete Browserify. Digamos que tenemos port variable y getAccounts método en ES5 module.js :

module.exports = {
  port: 3000,
  getAccounts: function() {
    ...
  }
}

En ES5 main.js , require('module') esa dependencia:

var service = require('module.js')
console.log(service.port) // 3000

En ES6, usaríamos export y import . Por ejemplo, esta es nuestra biblioteca en ES6 module.js archivo:

export var port = 3000
export function getAccounts(url) {
  ...
}

En el archivo del importador ES6 main.js , usamos import {name} from 'my-module' sintaxis. Por ejemplo,

import {port, getAccounts} from 'module'
console.log(port) // 3000

O podemos importar todo como una variable service en main.js :

import * as service from 'module'
console.log(service.port) // 3000

Personalmente, encuentro confusos los módulos ES6. Sí, son más elocuentes, pero los módulos de Node.js no cambiarán pronto. Es mejor tener solo un estilo para el navegador y el servidor JavaScript, por lo que me quedaré con el estilo CommonJS/Node.js por ahora.

La compatibilidad con los módulos ES6 en los navegadores no llegará pronto (al momento de escribir este artículo), por lo que necesitará algo como jspm para usar los módulos ES6.

Para obtener más información y ejemplos sobre los módulos ES6, consulte este texto. No importa qué, ¡escriba JavaScript modular!

Cómo usar ES6 hoy (Babel)

ES6 está finalizado, pero no es totalmente compatible con todos los navegadores (por ejemplo, compatibilidad con ES6 Firefox). Para usar ES6 hoy, obtenga un compilador como Babel. Puede ejecutarlo como una herramienta independiente o usarlo con su sistema de compilación. Hay complementos de Babel para Grunt, Gulp y Webpack.

Cómo usar ES6 hoy (Babel)

Aquí hay un ejemplo de Gulp. Instale el complemento:

$ npm install --save-dev gulp-babel

En gulpfile.js , define una tarea build eso toma src/app.js y lo compila en el build carpeta:

var gulp = require('gulp'),
  babel = require('gulp-babel')

gulp.task('build', function () {
  return gulp.src('src/app.js')
    .pipe(babel())
    .pipe(gulp.dest('build'))
})

Node.js y ES6

Para Node.js, puede compilar sus archivos Node.js con una herramienta de compilación o usar un módulo de Babel independiente babel-core . Para instalarlo,

$ npm install --save-dev babel-core

Luego, en Node.js, llamas a esta función:

require("babel-core").transform(es5Code, options)

Resumen de las cosas de ES6

Hay muchas otras características notables de ES6 que probablemente no usará (al menos no de inmediato). Sin ningún orden en particular:

  1. Nuevos métodos Math, Number, String, Array y Object
  2. Tipos de números binarios y octales
  3. Distribución de descanso predeterminada
  4. For of comprensiones (¡hola de nuevo, poderoso CoffeeScript!)
  5. Símbolos
  6. Llamadas de cola
  7. Generadores
  8. Nuevas estructuras de datos como Map y Set

Para los que tienen un gran rendimiento y no pueden dejar de aprender sobre ES6, como algunas personas que no pueden parar después de la primera papa frita (¡solo una más!), aquí está la lista de lecturas adicionales:

  1. Hoja de trucos de ES6 (PDF GRATIS)
  2. Comprensión de ECMAScript 6 por Nicolas Zakas libro
  3. Explorando ES6 por el Dr. Axel Rauschmayer
  4. ES6 en la Universidad Node
  5. ES7 y ES8 en Node University