ES2015

ES2015 (formalmente ES6) es un gran avance para el lenguaje JavaScript. Ofrece nuevas funciones y expansión de sintaxis para los patrones que requerían código estándar significativo en ES5. Esto incluye clases, funciones de flecha y módulos. En este episodio, analizaremos las herramientas que usamos para aprovechar al máximo ES2015 cuando compilamos apps web con JavaScript.

Transpilación con Babel

Si bien los motores de JavaScript están haciendo grandes progresos en la implementación de las funciones de ES2015 de forma nativa, hay una trampa. Para usar el conjunto completo de funciones en este momento, deberás volver a transpilar tu código a ES5 para que los navegadores actuales lo interpreten. Afortunadamente, existen herramientas como Babel que facilitan este proceso.

Usa Babel en tu proceso de compilación

Babel te permite tomar código JavaScript escrito con funciones ES2015 y transpilarlo de nuevo a ES5 para que pueda funcionar en navegadores que actualmente no admiten estas funciones. Puedes agregar Babel a tu proceso de compilación de esta manera.

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

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

El único atributo que Babel no puede transpilar es Módulos. Los módulos en ES6 te permiten crear clases y exportar o importar clases entre archivos. Para transpilar módulos, pasa el código JavaScript por Browserify, que combinará los archivos y, luego, lo pasará por Babelify (una versión de Babel que puede controlar el resultado de Browserify).

var babelify = require('babelify');
var source = require('vinyl-source-stream');
var browserify = require('browserify');

gulp.task('babelify', function() {
  browserify({ entries: './src.js', debug: true })
    .transform(babelify)
    .bundle()
    .pipe(source('bundle.js'))
    .pipe(gulp.dest('./dist/js/'));
});

Varios archivos JavaScript

El ejemplo anterior requiere que definas un archivo específico, que puede convertirse en una carga. Matt hace lo siguiente en Gulp para buscar y transpilar archivos que terminan en .es6.js.

var config = {
  src: 'src/scripts',
  dest: 'dist/scripts'
};
var es6FileGlob = '/**/*.es6.js';

var gulp = require('gulp');
var plugins = require('gulp-load-plugins')();
var glob = require('glob');
var path = require('path');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');

// Takes an array of bundles to run through browserify and babelify
function transpileES6Modules(browserifyFileEntries) {
  browserifyFileEntries.forEach(function(fileEntry) {
    var browserifyBundle = browserify({
        entries: [fileEntry.srcPath]
      })
      .transform(babelify);

    var finalStream = browserifyBundle.bundle()
      .on('log', plugins.util.log.bind(plugins.util, 'Browserify Log'))
      .on('error', plugins.util.log.bind(plugins.util, 'Browserify Error'))
      .pipe(source(fileEntry.outputFilename));

    return finalStream.pipe(gulp.dest(fileEntry.dest));
  });
}

// This takes a source path and finds all files ending
// with .es6.js and creates the bundles to run through browserify
// and babelify
function handleES6Scripts(srcPath) {
  var browserifyFileEntries = [];

  var es6Filepaths = glob.sync(srcPath + es6FileGlob);
  es6Filepaths.forEach(function(filepath) {
    var filename = path.basename(filepath);
    var directoryOfFile = path.dirname(filepath);
    var relativeDirectory = path.relative(
      srcPath,
      directoryOfFile);

    // Create an object and add to the browserify bundle array
    browserifyFileEntries.push({
      srcPath: './' + filepath,
      outputFilename: filename,
      dest: path.join(config.dest, relativeDirectory)
    });
  });

  transpileES6Modules(browserifyFileEntries);
}

gulp.task('scripts:es6', ['scripts:lint'], function(cb) {
  handleES6Scripts(config.src);

  cb();
});

Comprobaciones de lint y estilo

Existen algunas opciones para analizar tu código con lint para detectar posibles problemas y ajustes de estilo.

JSHint y JSCS

JSHint y JSCS son las herramientas más comunes que se usan para la verificación de estilo actual de JavaScript.

JSHint destacará todos los posibles problemas en tu código y mencionará los patrones que generalmente se consideran una práctica no recomendada.

JSCS analizará el estilo de tu código, lo que incluye acciones como asegurarse de que solo se usen pestañas o espacios, y que los espacios se coloquen en lugares coherentes.

Para usar JSHint y JSCS en el código ES2015, debes agregar "esnext": true a tus archivos .jshintrc y .jscsrc.

ESLint

ESLint es un linter y un verificador de estilo alternativos. Esta herramienta genera mucha transmisión y tiene algunas funciones interesantes sobre JSHint, como poder indicar los entornos para los que está escrito JavaScript y establecer el nivel de error / advertencia para problemas específicos.

ESLint es completamente personalizable y proporciona reglas de análisis con lint personalizadas en las que tú controlas si las opciones se pueden desactivar o atenuar mediante sus archivos de configuración. Además, si usas React, ESLint también funciona con JSX.

Configurar ESLint en tu proceso de compilación tampoco es demasiado difícil.

var gulp = require('gulp'),
    eslint = require('gulp-eslint');

gulp.task('lint', function () {
    return gulp.src(['js/**/*.js'])
        // eslint() attaches the lint output to the eslint property
        // of the file object so it can be used by other modules.
        .pipe(eslint())
        // eslint.format() outputs the lint results to the console.
        // Alternatively use eslint.formatEach() (see Docs).
        .pipe(eslint.format())
        // To have the process exit with an error code (1) on
        // lint error, return the stream and pipe to failOnError last.
        .pipe(eslint.failOnError());
});

gulp.task('default', ['lint'], function () {
    // This will only run if the lint task is successful...
});

El equipo de Babel mantiene babel-estlint, una herramienta que te permite analizar con lint cualquier código de Babel válido mediante ESLint. Si bien ESLint admite analizadores personalizados, parte de la sintaxis que Babel admite no es directamente compatible con ESLint, por lo que es otra opción si necesitas aún más flexibilidad. Para configurarlo, personaliza la sección parser del archivo .eslintrc:

{
  "parser": "babel-eslint",
  "rules": {
    "strict": 0
  }
}

Dan Abramov tiene un excelente comentario sobre la configuración de ESLint y Babel-eslint en Lint Like It's. También se explica cómo integrar SublimeLinter-eslint en el flujo de trabajo para la compatibilidad con el análisis con lint ES2015 en Sublime Text.

¿Qué plataforma deberías usar? Pruébalas y elige la que funcione mejor para ti.

Resaltado de sintaxis ES2015

Por supuesto, querrás que tu código ES2015 se destaque correctamente la sintaxis. Nos gusta usar babel-sublime, que se puede instalar desde el Control de paquetes. Durante la configuración, te recomendamos que te asegures de establecerlo como predeterminado para todos los archivos en los que necesites destacar para trabajar. Esto incluye JS, pero también puede abarcar JSX si usas React.

Documentación de ES2015

Históricamente, hemos confiado en JSDoc bastante para documentar nuestro código JavaScript. Lamentablemente, tiene problemas abiertos para admitir ES2015 (debido a los temas que se abordaron en JSDoc 3). Sin embargo, hay una cantidad cada vez mayor de alternativas disponibles mientras esperamos que se actualice. ESDoc es una de esas opciones, y Jonathan Creamer tiene una descripción reciente que vale la pena leer.

Crea archivos Gulp.js con Babel

Si usas Gulp para el proceso de compilación, ahora se puede crear Gulpfiles con cualquier sintaxis compatible con Babel. Esto se hace en Web Starter Kit, y su configuración es relativamente trivial. Con una versión reciente de Gulp y la CLI de Gulp, solo cambia el nombre de gulpfile.js a gulpfile.babel.js, y Gulp interpretará y transpilará el archivo gulpfile ES2015 automáticamente con Babel.

Funciones favoritas de ES2015

Módulos

Los módulos son una forma de exportar valores, funciones y clases desde un archivo para que puedas importarlos a otro.

export function exampleFunction() {
  console.log('I\'m an example. #TrueStory');
}



import { exampleFunction } from './example-function';
import BaseController from './base-controller';

export default class ExampleController extends BaseController {
  constructor() {
    super();

    exampleFunction();
  }

  doSomething() {
    console.log('What should I do? Change the DOM? Print a dancing shark to the console?');
  }
}

En este sitio, encontrarás excelentes ejemplos y explicaciones de los módulos.

Strings de plantillas

Las cadenas de plantilla te permiten reemplazar el puerto de una cadena por una variable.

// Simple string substitution
var name = "Brendan";
console.log('Yo, ${name}!');

// => "Yo, Brendan!"

Lo bueno de las strings de plantillas es que la sustitución es una ejecución de JavaScript, lo que significa que puedes usar funciones o expresiones intercaladas.

var a = 10;
var b = 10;
console.log('a+b = ${a+b}.');
//=> a+b = 20.

function fn() { return "I am a result. Rarr"; }
console.log('foo ${fn()} bar');
//=> foo I am a result. Rarr bar.

Puedes obtener más información en esta útil entrada de blog de Addy.

Literales de objetos abreviados

Los literales de objeto te permiten evitar tener que definir la clave y el valor cuando creas un objeto si la variable tiene el mismo nombre que la clave que quieres que tenga el objeto.

Esto significa lo siguiente:

function createObject(name, data) {
  return { name: name, data: data };
}

Se convierte en esto:

function createObject(name, data) {
  return { name, data };
}

Nombres de propiedad calculados

Esta función en ES2015 te permite crear nombres de propiedades de forma dinámica en un objeto. Los documentos de Mozilla son una gran fuente de información y contienen este excelente ejemplo.

var a = {
  ["foo" + ++i]: i,
  ["foo" + ++i]: i,
  ["foo" + ++i]: i
};

console.log(a.foo1); // 1
console.log(a.foo2); // 2
console.log(a.foo3); // 3

Funciones de la flecha gruesa

Las funciones de flechas de grasa te permiten escribir funciones abreviadas donde esto:

button.addEventListener('click', function(event) {
  console.log('The button has received a click', event);
});

Se convierte en esto:

button.addEventListener('click', (event) => {
  console.log('The button has received a click', event);
});

Además de tener una sintaxis más corta, una gran característica de usar las funciones de flechas grandes es que el alcance del objeto es el mismo que la sentencia que la contiene. Esto significa que no necesitas llamar a .bind(this) en tu función ni crear una variable que = esto.

Hay muchos más ejemplos en MDN.

Repositorio de herramientas ES6 de Addy

Addy se ha ocupado de mantener una lista de herramientas ES2015 y, si las herramientas anteriores no son adecuadas para ti, quizás estás usando Grunt en lugar de Gulp, entonces es posible que esto tenga la respuesta.

https://github.com/addyosmani/es6-tools

Lo anterior también incluye vínculos a herramientas adicionales de Babel que pueden ayudarte durante las pruebas de unidades y más allá.

Libros para leer

Hay dos libros que puedes consultar de forma gratuita en línea para obtener más información sobre ES2015. Para comprender ECMAScript 6, escrito por Nicholas C. Zakas y Exploring ES6 del Dr. Axel Rauschmayer.

Torre de Babel

Si te interesa aprender los atributos ES2015 en tu línea de comandos, tower-of-babel ofrece una serie de ejercicios que pueden ser de interés. Todos ellos usan Babel.

Otros recursos en caso de interés: