ES2015

O ES2015 (anteriormente ES6) é um avanço fantástico para a linguagem JavaScript. Ela traz novos recursos e aprimoramento para padrões que exigiam código boilerplate significativo no ES5. Isso inclui classes, funções de seta e módulos. Neste episódio, abordamos as ferramentas que usamos para aproveitar ao máximo o ES2015 ao criar apps da Web em JavaScript.

Transpilação com Babel

Embora os mecanismos JavaScript estejam progredindo muito na implementação nativa dos recursos do ES2015, há um pegadinha. Para usar o conjunto completo de recursos hoje, você precisará transcompilar seu código de volta para ES5 para que ele possa ser interpretado pelos navegadores atuais. Felizmente, existem ferramentas como o Babel que tornam esse processo muito simples.

Como usar o Babel no processo de compilação

O Babel permite que você pegue o JavaScript escrito usando recursos ES2015 e o transpile para ES5 para que ele funcione em navegadores que não são compatíveis com esses recursos. É possível adicionar o Babel ao seu processo de compilação dessa forma.

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'));
});

O único recurso que o Babel não consegue transcompilar são os Módulos. Os módulos no ES6 permitem criar classes e exportar / importar classes entre arquivos. Para transcompilar módulos, transmita o JavaScript pelo Browserify, que mesclará os arquivos e os transmitirá pelo Babelify, uma versão do Babel que pode processar a saída do 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/'));
});

Vários arquivos JavaScript

O exemplo acima exige que você defina um arquivo específico, o que pode ser um pouco complicado. Matt faz o seguinte no Gulp para pesquisar e transcompilar arquivos que terminam com .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();
});

Linting e verificações de estilo

Há poucas opções ao inspecionar o código em busca de possíveis problemas e conformidade de estilo.

JSHint e JSCS

JSHint e JSCS são as ferramentas mais comuns usadas para a verificação de estilo JavaScript de hoje.

A JSHint destacará todos os possíveis problemas no seu código e destacará todos os padrões geralmente considerados como práticas não recomendadas.

O JSCS verifica o estilo do código, incluindo itens como garantir que apenas tabulações ou espaços sejam usados e que os espaços sejam colocados em locais consistentes.

Para usar JSHint e JSCS no código ES2015, você precisa adicionar "esnext": true aos seus arquivos .jshintrc e .jscsrc

ESLint

O ESLint (link em inglês) é uma ferramenta alternativa de linter e verificador de estilo. Essa ferramenta está ganhando mais vapor e tem alguns recursos interessantes em relação ao JSHint, por exemplo, a capacidade de indicar ambientes para os quais o JavaScript foi escrito e de definir o nível de erro / aviso para problemas específicos.

O ESLint é totalmente personalizável e oferece regras de inspeção personalizadas para você controlar se as opções podem ser desativadas ou reduzidas usando os arquivos de configuração. Além disso, se você estiver usando o React, o ESLint também funcionará com o JSX.

Configurar o ESLint no seu processo de compilação também não é muito 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...
});

A equipe do Babel mantém babel-estlint, uma ferramenta que permite inspecionar qualquer código válido do Babel usando ESLint. Embora o ESLint oferece suporte a analisadores personalizados, algumas das sintaxes com suporte do Babel não são diretamente compatíveis com ESLint. Portanto, essa é outra opção, se você precisa de ainda mais flexibilidade. Isso pode ser configurado personalizando a seção parser do arquivo .eslintrc:

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

Dan Abramov tem um ótimo artigo sobre a configuração do ESLint e do babel-eslint em Lint Like It's 2015. Também aborda como integrar o SublimeLinter-eslint ao fluxo de trabalho para suporte a inspeção ES2015 no Sublime Text.

Qual plataforma deve usar? Experimente e use o que funciona melhor para você.

Destaque de sintaxe ES2015

É claro que a sintaxe do seu código ES2015 deve ser destacada corretamente. Gostamos de usar o babel-sublime, que pode ser instalado pelo Controle de pacotes. Ao configurar, recomendamos que você o defina como padrão para todos os arquivos que precisam ser destacados para trabalhar. Isso inclui o JS, mas também poderá abranger o JSX se você estiver usando o React.

Como documentar o ES2015

Historicamente, usamos o JSDoc para documentar o código JavaScript. Ele tem problemas em aberto (link em inglês) para oferecer suporte ao ES2015 (devido a já ter sido abordado no JSDoc 3). No entanto, há um número crescente de alternativas disponíveis enquanto aguardamos a solução. O ESDoc é uma dessas opções, e Jonathan Creamer fez uma composição recente sobre o assunto que vale a pena ler.

Como criar arquivos Gulp.js com o Babel

Se você estiver usando Gulp no processo de compilação, agora será possível criar Gulpfiles com qualquer sintaxe compatível com o Babel. Fazemos isso no Web Starter Kit (link em inglês), e a configuração é relativamente simples. Usando uma versão recente do Gulp e da CLI do Gulp, basta renomear gulpfile.js como gulpfile.babel.js, e o Gulp vai interpretar e transcompilar seu gulpfile ES2015 usando o Babel automaticamente.

Recursos favoritos do ES2015

Módulos

Os módulos são uma maneira de exportar valores, funções e classes de um arquivo, de modo que você possa importá-los para outro.

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?');
  }
}

Neste site, você encontra ótimos exemplos e explicações sobre os módulos.

Strings de modelo

As strings de modelo permitem substituir uma porta de uma string por uma variável.

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

// => "Yo, Brendan!"

O bom com as strings de modelo é que a substituição é uma execução de JavaScript, o que significa que é possível usar funções ou expressões inline.

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.

Saiba mais nesta postagem do blog útil de Addy (em inglês).

Literais de objetos abreviados

Com os literais de objeto, você não precisa definir a chave e o valor durante a criação de um objeto, caso a variável tenha o mesmo nome que a chave que você quer que o objeto tenha.

Ou seja:

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

Fica assim:

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

Nomes de propriedades computados

Esse recurso no ES2015 permite criar nomes de propriedades dinamicamente em um objeto. Os documentos do Mozilla são uma ótima fonte de informações e têm esse ótimo exemplo (em inglês).

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

Funções da seta gorjeta

As funções de seta de gordura permitem que você escreva funções de forma abreviada em que:

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

Fica assim:

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

Além de ter uma sintaxe mais curta, um ótimo recurso do uso de funções de seta multifacetadas é que o escopo do objeto é o mesmo da instrução de inclusão. Isso significa que você não precisa chamar .bind(this) na sua função nem criar uma var que seja igual a isto.

muitos outros exemplos no MDN.

Repositório de ferramentas ES6 do Addy

O Addy está ocupado mantendo uma lista de ferramentas ES2015. Se as ferramentas acima não são adequadas para você, talvez você esteja usando o Grunt em vez do Gulp, isso pode ter uma resposta para você.

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

O conteúdo acima também inclui links para outras ferramentas do Babel que podem ajudar durante o teste de unidade e além.

Livros para ler

Há dois livros que você pode conferir sem custo financeiro on-line para saber mais sobre a ES2015. Noções básicas sobre ECMAScript 6, escrito por Nicholas C. Zakas e Exploring ES6 escrito pelo Dr. Axel Rauschmayer.

Torre de Babel

Se você quiser aprender sobre os recursos do ES2015 na sua linha de comando, a tower-of-babel oferece uma série de exercícios que podem ser do seu interesse. Todos eles usam o Babel.

Outros recursos em caso de interesse: