Featured image of post Guia de Estilo JavaScript do Airbnb

Guia de Estilo JavaScript do Airbnb

Guia de Estilo JavaScript do Airbnb

Photo by Pankaj Patel on Unsplash

https://github.com/jigsawye/javascript

Guia de Estilo JavaScript do Airbnb()

Uma abordagem na sua maioria razoável para JavaScript.

Downloads Gitter

Outros Guias de Estilo

Traduzido de Airbnb JavaScript Style Guide.

Tipos (Types)

  • 1.1 Primitivos (Primitives): Você acessa tipos primitivos diretamente.
  • string
  • number
  • boolean
  • null
  • undefined
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • 1.2 Complexos (Complex): Você acessa tipos complexos por referência (by reference).
  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Referências (References)

Por quê? Isso garante que você não possa reatribuir suas referências, o que pode levar a bugs e código difícil de compreender.

// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;
  • 2.2 Se você precisar reatribuir referências, use let em vez de var. eslint: no-var jscs: disallowVar

Por quê? let tem escopo de bloco (block-scoped), enquanto var tem escopo de função (function-scoped).

// bad
var count = 1;
if (true) {
    count += 1;
}

// good, use the let.
let count = 1;
if (true) {
    count += 1;
}
  • 2.3 Note que tanto let quanto const têm escopo de bloco.
// const e let existem apenas dentro dos blocos onde são definidos.
{
    let a = 1;
    const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

Objetos (Objects)

  • 3.1 Use a sintaxe literal para a criação de objetos. eslint rules: no-new-object.
// bad
const item = new Object();

// good
const item = {};
// bad
const superman = {
    default: { clark: 'kent' },
    private: true,
};

// good
const superman = {
    defaults: { clark: 'kent' },
    hidden: true,
};
// bad
const superman = {
    class: 'alien',
};

// bad
const superman = {
    klass: 'alien',
};

// good
const superman = {
    type: 'alien',
};

  • 3.4 Use nomes de propriedades computados (computed property names) ao criar objetos com nomes de propriedades dinâmicos.

Por quê? Eles permitem que você defina todas as propriedades do objeto em um só lugar.


function getKey(k) {
    return `a key named ${k}`;
}

// bad
const obj = {
    id: 5,
    name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
    id: 5,
    name: 'San Francisco',
    [getKey('enabled')]: true,
};

// bad
const atom = {
    value: 1,

    addValue: function (value) {
    return atom.value + value;
    },
};

// good
const atom = {
    value: 1,

    addValue(value) {
    return atom.value + value;
    },
};

Por quê? É mais curto e descritivo.

const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
    lukeSkywalker,
};
  • 3.7 Agrupe suas abreviações no início da sua declaração de objeto.

Por quê? É mais fácil saber quais propriedades estão usando a abreviação.

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    lukeSkywalker,
    episodeThree: 3,
    mayTheFourth: 4,
    anakinSkywalker,
};

// good
const obj = {
    lukeSkywalker,
    anakinSkywalker,
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    episodeThree: 3,
    mayTheFourth: 4,
};

Por quê? Em geral, nós consideramos isso subjetivamente mais fácil de ler. Melhora o destaque de sintaxe e também é mais fácil de ser otimizado por muitas engines de JS.

// bad
const bad = {
    'foo': 3,
    'bar': 4,
    'data-blah': 5,
};

// good
const good = {
    foo: 3,
    bar: 4,
    'data-blah': 5,
};

Arrays (Arrays)

// bad
const items = new Array();

// good
const items = [];
  • 4.2 Use Array#push em vez de atribuição direta para adicionar itens a um array.
const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');

  • 4.3 Use spreads de array ... para copiar arrays.
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];
  • 4.4 Para converter um objeto do tipo array (array-like object) para um array, use Array#from.
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
  • 4.5 Use declarações de retorno em callbacks de métodos de array. Tudo bem omitir o retorno se o corpo da função consistir em uma única declaração seguindo 8.2. eslint: array-callback-return
// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map(x => x + 1);

// bad
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = memo.concat(item);
});

// good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = flatten;
    return flatten;
});

// bad
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    } else {
    return false;
    }
});

// good
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    }

    return false;
});

Desestruturação (Destructuring)

Por quê? A desestruturação evita que você crie referências temporárias para essas propriedades.

// bad
function getFullName(user) {
    const firstName = user.firstName;
    const lastName = user.lastName;

    return `${firstName} ${lastName}`;
}

// good
function getFullName(user) {
    const { firstName, lastName } = user;
    return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
    return `${firstName} ${lastName}`;
}
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;
  • 5.3 Use a desestruturação de objetos para múltiplos valores de retorno, não a desestruturação de array.

Por quê? Você pode adicionar novas propriedades ou mudar a ordem das coisas sem quebrar os locais de chamada (call sites).

// bad
function processInput(input) {
    // then a miracle occurs
    return [left, right, top, bottom];
}

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

// good
function processInput(input) {
    // then a miracle occurs
    return { left, right, top, bottom };
}

// the caller selects only the data they need
const { left, right } = processInput(input);

Strings (Strings)

// bad
const name = "Capt. Janeway";

// good
const name = 'Capt. Janeway';
  • 6.2 Strings que fazem a linha ultrapassar 100 caracteres não devem ser escritas em várias linhas usando concatenação de string.
  • 6.3 Nota: Se usado excessivamente, strings longas com concatenação podem impactar a performance. jsPerf & Discussão.
// bad
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

// bad
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';

// good
const errorMessage = 'This is a super long error that was thrown because ' +
    'of Batman. When you stop to think about how Batman had anything to do ' +
    'with this, you would get nowhere fast.';

Por quê? Template strings dão a você uma sintaxe legível e concisa com quebras de linha e recursos de interpolação de string.

// bad
function sayHi(name) {
    return 'How are you, ' + name + '?';
}

// bad
function sayHi(name) {
    return ['How are you, ', name, '?'].join();
}

// bad
function sayHi(name) {
    return `How are you, ${ name }?`;
}

// good
function sayHi(name) {
    return `How are you, ${name}?`;
}
  • 6.5 Nunca use eval() em uma string, isso abre muitas vulnerabilidades.

Funções (Functions)

  • 7.1 Use declarações de função (function declarations) em vez de expressões de função (function expressions). jscs: requireFunctionDeclarations

Por quê? As declarações de função são nomeadas, então são mais fáceis de identificar em stack traces. Além disso, as declarações de função são hoisted, enquanto as expressões de função não são. Essa regra facilita que as Arrow Functions substituam completamente as expressões de função.

// bad
const foo = function () {
};

// good
function foo() {
}

Por quê? Uma IIFE é uma unidade única - envolver a função e sua invocação em parênteses torna isso claro. Note que em um mundo com módulos, você quase não precisa de IIFE.

// immediately-invoked function expression (IIFE)
(function () {
    console.log('Welcome to the Internet. Please follow me.');
}());
  • 7.3 Nunca declare uma função em um bloco não-função (if, while, etc). Atribua a função a uma variável em vez disso. Navegadores permitirão que você faça isso, mas todos interpretam de forma diferente. eslint: no-loop-func

  • 7.4 Nota: ECMA-262 define um bloco como uma lista de declarações. Uma declaração de função não é uma declaração. Leia a nota do ECMA-262 sobre essa questão.

// bad
if (currentUser) {
    function test() {
    console.log('Nope.');
    }
}

// good
let test;
if (currentUser) {
    test = () => {
    console.log('Yup.');
    };
}
  • 7.5 Nunca nomeie um parâmetro arguments. Isso terá precedência sobre o objeto arguments que é dado a cada escopo de função.
// bad
function nope(name, options, arguments) {
    // ...stuff...
}

// good
function yup(name, options, args) {
    // ...stuff...
}

Por quê? ... é explícito sobre quais argumentos você quer puxar. Além disso, argumentos rest são um Array real, e não meramente Array-like como arguments.

// bad
function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
}

// good
function concatenateAll(...args) {
    return args.join('');
}

  • 7.7 Use a sintaxe de parâmetros padrão em vez de modificar os argumentos da função.
// really bad
function handleThings(opts) {
    // No! We shouldn't mutate function arguments.
    // Double bad: if opts is false it will be set to an object which may
    // be what you want but it can cause subtle bugs.
    opts = opts || {};
    // ...
}

// still bad
function handleThings(opts) {
    if (opts === void 0) {
    opts = {};
    }
    // ...
}

// good
function handleThings(opts = {}) {
    // ...
}
  • 7.8 Evite efeitos colaterais com parâmetros padrão.

Por quê? Eles são confusos de entender.

var b = 1;
// bad
function count(a = b++) {
    console.log(a);
}
count();  // 1
count();  // 2
count(3); // 3
count();  // 3
  • 7.9 Sempre coloque os parâmetros padrão por último.
// bad
function handleThings(opts = {}, name) {
    // ...
}

// good
function handleThings(name, opts = {}) {
    // ...
}
  • 7.10 Nunca use o construtor Function para criar uma nova função.

Por quê? Criar uma função dessa maneira avalia uma string de forma similar ao eval(), o que abre vulnerabilidades.

// bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');
  • 7.11 Espaçamento na assinatura de uma função.

Por quê? A consistência é boa, e você não deveria ter que adicionar ou remover espaços ao adicionar ou remover um nome.

// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const x = function () {};
const y = function a() {};

Por quê? Manipular objetos passados como parâmetros pode causar efeitos colaterais indesejados no chamador original.

// bad
function f1(obj) {
    obj.key = 1;
};

// good
function f2(obj) {
    const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};

Por quê? Reatribuir parâmetros pode levar a comportamentos inesperados, especialmente ao acessar o objeto arguments. Isso também pode causar problemas de otimização, especialmente no V8.

```javascript
// bad
function f1(a) {
a = 1;
}

function f2(a) {
if (!a) { a = 1; }
}

// good
function f3(a) {
const b = a || 1;
}

function f4(a = 1) {
}
```

Arrow Functions

Por quê? Cria uma versão da função que executa no contexto de this, que é geralmente o que você quer, e é uma sintaxe mais concisa.

Por que não? Se você tem uma função bastante complexa, você pode mover essa lógica para sua própria declaração de função nomeada.

// bad
[1, 2, 3].map(function (x) {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

Por quê? Açúcar sintático (Syntactic sugar). É mais legível quando múltiplas funções são encadeadas juntas.

Por que não? Se você planeja retornar um objeto.

// bad
[1, 2, 3].map(number => {
    const nextNumber = number + 1;
    `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map(number => `A string containing the ${number}.`);

// good
[1, 2, 3].map((number) => {
    const nextNumber = number + 1;
    return `A string containing the ${nextNumber}.`;
});
  • 8.3 Caso a expressão se estenda por várias linhas, envolva-a em parênteses para melhor legibilidade.

Por quê? Mostra claramente onde a função começa e termina.

// bad
[1, 2, 3].map(number => 'As time went by, the string containing the ' +
    `${number} became much longer. So we needed to break it over multiple ` +
    'lines.'
);

// good
[1, 2, 3].map(number => (
    `As time went by, the string containing the ${number} became much ` +
    'longer. So we needed to break it over multiple lines.'
));

Por quê? Menos desordem visual.

// bad
[1, 2, 3].map((x) => x * x);

// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(number => (
    `A long string with the ${number}. It’s so long that we’ve broken it ` +
    'over multiple lines!'
));

// bad
[1, 2, 3].map(x => {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});
  • 8.5 Evite confundir a sintaxe de arrow function (=>) com operadores de comparação (<=, >=). eslint: no-confusing-arrow
// bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;

// bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;

// good
const itemHeight = item => { return item.height > 256 ? item.largeSize : item.smallSize; }

Construtores (Constructors)

  • 9.1 Sempre use class. Evite manipular prototype diretamente.

Por quê? A sintaxe class é mais concisa e mais fácil de raciocinar.

// bad
function Queue(contents = []) {
    this._queue = [...contents];
}
Queue.prototype.pop = function () {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
}


// good
class Queue {
    constructor(contents = []) {
    this._queue = [...contents];
    }
    pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
    }
}
  • 9.2 Use extends para herança.

Por quê? É uma maneira integrada de herdar a funcionalidade do protótipo sem quebrar instanceof.

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
    Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
    return this._queue[0];
}

// good
class PeekableQueue extends Queue {
    peek() {
    return this._queue[0];
    }
}
  • 9.3 Métodos podem retornar this para ajudar com encadeamento de métodos (method chaining).
// bad
Jedi.prototype.jump = function () {
    this.jumping = true;
    return true;
};

Jedi.prototype.setHeight = function (height) {
    this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
    jump() {
    this.jumping = true;
    return this;
    }

    setHeight(height) {
    this.height = height;
    return this;
    }
}

const luke = new Jedi();

luke.jump()
    .setHeight(20);
  • 9.4 Tudo bem escrever um método toString() personalizado, apenas certifique-se de que funcione corretamente e não cause efeitos colaterais.
class Jedi {
    constructor(options = {}) {
    this.name = options.name || 'no name';
    }

    getName() {
    return this.name;
    }

    toString() {
    return `Jedi - ${this.getName()}`;
    }
}
  • 9.5 Classes têm um construtor padrão se um não for especificado. Um construtor vazio ou um que apenas delega para uma classe pai é desnecessário. no-useless-constructor
// bad
class Jedi {
    constructor() {}

    getName() {
    return this.name;
    }
}

// bad
class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    }
}

// good
class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    this.name = 'Rey';
    }
}

Módulos (Modules)

  • 10.1 Sempre use módulos (import/export) em vez de um sistema de módulos não padrão. Você sempre pode transpilar para o seu sistema de módulos preferido.

Por quê? Módulos são o futuro, vamos começar a usar o futuro agora.

// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • 10.2 Não use importações com curingas (wildcard imports).

Por quê? Isso garante que você tenha um único default export.

// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 10.3 E não exporte diretamente de um import.

Por quê? Embora escrever em uma linha seja conciso, ter uma maneira clara de importar e uma maneira clara de exportar torna as coisas consistentes.

// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

Iteradores e Geradores (Iterators and Generators)

  • 11.1 Não use iteradores. Prefira as funções de ordem superior do JavaScript como map() e reduce() em vez de loops como for-of. eslint: no-iterator

Por quê? Isso impõe nossa regra de imutabilidade. Lidar com funções puras que retornam valores é mais fácil de raciocinar do que efeitos colaterais.

eslint rules: no-iterator.

const numbers = [1, 2, 3, 4, 5];

// bad
let sum = 0;
for (let num of numbers) {
    sum += num;
}

sum === 15;

// good
let sum = 0;
numbers.forEach(num => sum += num);
sum === 15;

// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
  • 11.2 Não use geradores por enquanto.

Por quê? Eles não transpilam bem para ES5 ainda.

Propriedades (Properties)

const luke = {
    jedi: true,
    age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;
  • 12.2 Use a notação de colchetes [] ao acessar propriedades com uma variável.
const luke = {
    jedi: true,
    age: 28,
};

function getProp(prop) {
    return luke[prop];
}

const isJedi = getProp('jedi');

Variáveis (Variables)

  • 13.1 Sempre use const ou let para declarar variáveis. Não fazer isso resultará em variáveis globais. Queremos evitar poluir o namespace global. O Capitão Planeta nos avisou sobre isso.
// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();

Por quê? É mais fácil adicionar novas declarações de variáveis dessa maneira, e você nunca precisa se preocupar em trocar um ; por uma , ou introduzir diferenças apenas de pontuação.

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, notice the error)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
  • 13.3 Agrupe todos os seus consts e depois agrupe todos os seus lets.

Por quê? Isso é útil quando mais tarde você precisar atribuir uma variável dependendo de uma das variáveis atribuídas anteriormente.

// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
  • 13.4 Atribua variáveis onde você precisa delas, mas coloque-as em um lugar razoável.

Por quê? let e const têm escopo de bloco e não de função.

// bad - unnecessary function call
function checkName(hasName) {
    const name = getName();

    if (hasName === 'test') {
    return false;
    }

    if (name === 'test') {
    this.setName('');
    return false;
    }

    return name;
}

// good
function checkName(hasName) {
    if (hasName === 'test') {
    return false;
    }

    const name = getName();

    if (name === 'test') {
    this.setName('');
    return false;
    }

    return name;
}

Hoisting

// we know this wouldn't work (assuming there
// is no notDefined global variable)
function example() {
    console.log(notDefined); // => throws a ReferenceError
}

// creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
    console.log(declaredButNotAssigned); // => undefined
    var declaredButNotAssigned = true;
}

// The interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
    let declaredButNotAssigned;
    console.log(declaredButNotAssigned); // => undefined
    declaredButNotAssigned = true;
}

// using const and let
function example() {
    console.log(declaredButNotAssigned); // => throws a ReferenceError
    console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
    const declaredButNotAssigned = true;
}
  • 14.2 Expressões de função anônimas elevam seu nome de variável, mas não a atribuição da função.
function example() {
    console.log(anonymous); // => undefined

    anonymous(); // => TypeError anonymous is not a function

    var anonymous = function () {
    console.log('anonymous function expression');
    };
}
  • 14.3 Expressões de função nomeadas elevam o nome da variável, não o nome da função ou o corpo da função.
function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    superPower(); // => ReferenceError superPower is not defined

    var named = function superPower() {
    console.log('Flying');
    };
}

// the same is true when the function name
// is the same as the variable name.
function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    var named = function named() {
    console.log('named');
    }
}
  • 14.4 Declarações de função elevam seu nome e o corpo da função.
function example() {
    superPower(); // => Flying

    function superPower() {
    console.log('Flying');
    }
}

Operadores de Comparação e Igualdade (Comparison Operators & Equality)

  • 15.1 Use === e !== em vez de == e !=. eslint: eqeqeq

  • 15.2 Declarações condicionais como if avaliam sua expressão usando coerção com o método abstrato ToBoolean e sempre seguem estas regras simples:

  • Objects avaliam como true
  • Undefined avalia como false
  • Null avalia como false
  • Booleans avaliam como o valor do booleano
  • Numbers avaliam como false se forem +0, -0, ou NaN, caso contrário true
  • Strings avaliam como false se for uma string vazia '', caso contrário true
if ([0] && []) {
    // true
    // an array (even an empty one) is an object, objects will evaluate to true
}
// bad
if (name !== '') {
    // ...stuff...
}

// good
if (name) {
    // ...stuff...
}

// bad
if (collection.length > 0) {
    // ...stuff...
}

// good
if (collection.length) {
    // ...stuff...
}
  • 15.4 Para mais informações, consulte Truth Equality and JavaScript por Angus Croll.
  • 15.5 Use chaves para criar blocos em cláusulas case e default que contêm declarações léxicas (por exemplo, let, const, function, e class).

Por quê? Declarações léxicas são visíveis em todo o bloco switch, mas só são inicializadas quando atribuídas, o que só acontece quando seu case é alcançado. Isso causa problemas quando múltiplas cláusulas case tentam definir a mesma coisa.

eslint rules: no-case-declarations.

// bad
switch (foo) {
    case 1:
    let x = 1;
    break;
    case 2:
    const y = 2;
    break;
    case 3:
    function f() {}
    break;
    default:
    class C {}
}

// good
switch (foo) {
    case 1: {
    let x = 1;
    break;
    }
    case 2: {
    const y = 2;
    break;
    }
    case 3: {
    function f() {}
    break;
    }
    case 4:
    bar();
    break;
    default: {
    class C {}
    }
}
  • 15.7 Ternários não devem ser aninhados e geralmente devem ser expressões de uma linha.

eslint rules: no-nested-ternary.

// bad
const foo = maybe1 > maybe2
    ? "bar"
    : value1 > value2 ? "baz" : null;

// better
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2
    ? 'bar'
    : maybeNull;

// best
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
  • 15.8 Evite declarações ternárias desnecessárias.

eslint rules: no-unneeded-ternary.

// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;

// good
const foo = a || b;
const bar = !!c;
const baz = !c;

Blocos (Blocks)

  • 16.1 Use chaves com todos os blocos de múltiplas linhas.
// bad
if (test)
    return false;

// good
if (test) return false;

// good
if (test) {
    return false;
}

// bad
function foo() { return false; }

// good
function bar() {
    return false;
}
// bad
if (test) {
    thing1();
    thing2();
}
else {
    thing3();
}

// good
if (test) {
    thing1();
    thing2();
} else {
    thing3();
}

Instruções de Controle (Control Statements)

  • 17.1 No caso de sua instrução de controle (if, while etc.) ficar muito longa ou exceder o comprimento máximo da linha, cada condição (agrupada) pode ser colocada em uma nova linha. O operador lógico deve começar a linha.

Por quê? Exigir operadores no início da linha mantém os operadores alinhados e segue um padrão semelhante ao encadeamento de métodos. Isso também melhora a legibilidade, tornando a lógica complexa mais fácil de seguir visualmente.

// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
    thing1();
}

// bad
if (foo === 123 &&
    bar === 'abc') {
    thing1();
}

// bad
if (foo === 123
    && bar === 'abc') {
    thing1();
}

// bad
if (
    foo === 123 &&
    bar === 'abc'
) {
    thing1();
}

// good
if (
    foo === 123
    && bar === 'abc'
) {
    thing1();
}

// good
if (
    (foo === 123 || bar === 'abc')
    && doesItLookGoodWhenItBecomesThatLong()
    && isThisReallyHappening()
) {
    thing1();
}

// good
if (foo === 123 && bar === 'abc') {
    thing1();
}
  • 17.2 Não use operadores de seleção em vez de instruções de controle.
// bad
!isRunning && startRunning();

// good
if (!isRunning) {
    startRunning();
}

Comentários (Comments)

  • 18.1 Use /** ... */ para comentários de múltiplas linhas. Inclua uma descrição, especifique tipos e valores para todos os parâmetros e valores de retorno.
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

    // ...stuff...

    return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

    // ...stuff...

    return element;
}
  • 18.2 Use // para comentários de uma única linha. Coloque comentários de uma linha em uma nova linha acima do assunto do comentário. Coloque uma linha vazia antes do comentário, a menos que esteja na primeira linha de um bloco.
// bad
const active = true;  // is current tab

// good
// is current tab
const active = true;

// bad
function getType() {
    console.log('fetching type...');
    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
}

// good
function getType() {
    console.log('fetching type...');

    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
}

// also good
function getType() {
    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
}
  • 18.3 Prefixar seus comentários com FIXME ou TODO ajuda outros desenvolvedores a entender rapidamente se você está apontando um problema que precisa ser revisitado ou se você está sugerindo uma solução que precisa ser implementada. Estes são diferentes de comentários regulares porque são acionáveis. As ações são FIXME: -- need to figure this out ou TODO: -- need to implement.

  • 18.4 Use // FIXME: para anotar problemas.

class Calculator extends Abacus {
    constructor() {
    super();

    // FIXME: shouldn't use a global here
    total = 0;
    }
}
  • 18.5 Use // TODO: para anotar soluções para problemas.
class Calculator extends Abacus {
    constructor() {
    super();

    // TODO: total should be configurable by an options param
    this.total = 0;
    }
}

Espaços em Branco (Whitespace)

// bad
function foo() {
∙∙∙∙const name;
}

// bad
function bar() {
const name;
}

// good
function baz() {
∙∙const name;
}
// bad
function test(){
    console.log('test');
}

// good
function test() {
    console.log('test');
}

// bad
dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});

// good
dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
// bad
if(isJedi) {
    fight ();
}

// good
if (isJedi) {
    fight();
}

// bad
function fight () {
    console.log ('Swooosh!');
}

// good
function fight() {
    console.log('Swooosh!');
}
// bad
const x=y+5;

// good
const x = y + 5;
  • 19.5 Termine arquivos com uma única quebra de linha.
// bad
(function (global) {
    // ...stuff...
})(this);
// bad
(function (global) {
    // ...stuff...
})(this);

// good
(function (global) {
    // ...stuff...
})(this);
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// bad
$('#items').
    find('.selected').
    highlight().
    end().
    find('.open').
    updateCount();

// good
$('#items')
    .find('.selected')
    .highlight()
    .end()
    .find('.open')
    .updateCount();

// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led')
    .data(data)
    .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
    .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led').data(data);
// bad
if (foo) {
    return bar;
}
return baz;

// good
if (foo) {
    return bar;
}

return baz;

// bad
const obj = {
    foo() {
    },
    bar() {
    },
};
return obj;

// good
const obj = {
    foo() {
    },

    bar() {
    },
};

return obj;

// bad
const arr = [
    function foo() {
    },
    function bar() {
    },
];
return arr;

// good
const arr = [
    function foo() {
    },

    function bar() {
    },
];

return arr;
// bad
function bar() {

    console.log(foo);

}

// also bad
if (baz) {

    console.log(qux);
} else {
    console.log(foo);

}

// good
function bar() {
    console.log(foo);
}

// good
if (baz) {
    console.log(qux);
} else {
    console.log(foo);
}
// bad
function bar( foo ) {
    return foo;
}

// good
function bar(foo) {
    return foo;
}

// bad
if ( foo ) {
    console.log(foo);
}

// good
if (foo) {
    console.log(foo);
}
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);

// good
const foo = [1, 2, 3];
console.log(foo[0]);
// bad
const foo = {clark: 'kent'};

// good
const foo = { clark: 'kent' };

Por quê? Isso garante legibilidade e manutenibilidade.

// bad
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';

// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));

// good
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
    'Whatever wizard constrains a helpful ally. The counterpart ascends!';

// good
$.ajax({
    method: 'POST',
    url: 'https://airbnb.com/',
    data: { name: 'John' },
})
    .done(() => console.log('Congratulations!'))
    .fail(() => console.log('You have failed this city.'));

Vírgulas (Commas)

// bad
const story = [
    once
    , upon
    , aTime
];

// good
const story = [
    once,
    upon,
    aTime,
];

// bad
const hero = {
    firstName: 'Ada'
    , lastName: 'Lovelace'
    , birthYear: 1815
    , superPower: 'computers'
};

// good
const hero = {
    firstName: 'Ada',
    lastName: 'Lovelace',
    birthYear: 1815,
    superPower: 'computers',
};

Por quê? Isso leva a diffs do git mais limpos. Além disso, transpilers como Babel removerão a vírgula final adicional no código transpilado, o que significa que você não precisa se preocupar com o problema da vírgula final em navegadores legados.

// bad - git diff without trailing comma
const hero = {
        firstName: 'Florence',
    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb graph', 'modern nursing']
};

// good - git diff with trailing comma
const hero = {
        firstName: 'Florence',
        lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};

// bad
const hero = {
    firstName: 'Dana',
    lastName: 'Scully'
};

const heroes = [
    'Batman',
    'Superman'
];

// good
const hero = {
    firstName: 'Dana',
    lastName: 'Scully',
};

const heroes = [
    'Batman',
    'Superman',
];

Ponto e Vírgula (Semicolons)

// bad
(function () {
    const name = 'Skywalker'
    return name
})()

// good
(() => {
    const name = 'Skywalker';
    return name;
}());

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(() => {
    const name = 'Skywalker';
    return name;
}());

Leia mais.

Conversão de Tipos e Coerção (Type Casting & Coercion)

  • 22.1 Realize a coerção de tipo no início da instrução.
  • 22.2 Strings:
// => this.reviewScore = 9;

// bad
const totalScore = this.reviewScore + '';

// good
const totalScore = String(this.reviewScore);
  • 22.3 Números: Use Number para o casting de tipo e parseInt sempre com uma base para analisar strings. eslint: radix
const inputValue = '4';

// bad
const val = new Number(inputValue);

// bad
const val = +inputValue;

// bad
const val = inputValue >> 0;

// bad
const val = parseInt(inputValue);

// good
const val = Number(inputValue);

// good
const val = parseInt(inputValue, 10);
  • 22.4 Se por algum motivo você estiver fazendo algo louco e parseInt for seu gargalo e você precisar usar Bitshift por razões de desempenho, deixe um comentário explicando por que e o que você está fazendo.
// good
/**
 * parseInt was the reason my code was slow.
 * Bitshifting the String to coerce it to a
 * Number made it a lot faster.
 */
const val = inputValue >> 0;
  • 22.5 Nota: Tenha cuidado ao usar operações de bitshift. Números são representados como valores de 64 bits, mas as operações de bitshift sempre retornam um inteiro de 32 bits (fonte). O bitshift pode levar a um comportamento inesperado para valores inteiros maiores que 32 bits. Discussão. O maior inteiro com sinal de 32 bits é 2.147.483.647:
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647
const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// good
const hasAge = !!age;

Convenções de Nomenclatura (Naming Conventions)

  • 23.1 Evite nomes de uma letra. Seja descritivo com sua nomenclatura.
// bad
function q() {
    // ...stuff...
}

// good
function query() {
    // ..stuff..
}
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}

// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
// bad
function user(options) {
    this.name = options.name;
}

const bad = new user({
    name: 'nope',
});

// good
class User {
    constructor(options) {
    this.name = options.name;
    }
}

const good = new User({
    name: 'yup',
});
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';

// good
this._firstName = 'Panda';
// bad
function foo() {
    const self = this;
    return function () {
    console.log(self);
    };
}

// bad
function foo() {
    const that = this;
    return function () {
    console.log(that);
    };
}

// good
function foo() {
    return () => {
    console.log(this);
    };
}
  • 23.6 Se seu arquivo exporta uma única classe, o nome do arquivo deve ser exatamente igual ao nome da classe.
// file contents
class CheckBox {
    // ...
}
export default CheckBox;

// in some other file
// bad
import CheckBox from './checkBox';

// bad
import CheckBox from './check_box';

// good
import CheckBox from './CheckBox';
  • 23.7 Use camelCase quando você exportar uma função padrão. O nome do arquivo deve ser idêntico ao nome da função.
function makeStyleGuide() {
}

export default makeStyleGuide;
  • 23.8 Use PascalCase quando você exportar um singleton / biblioteca de funções / objeto vazio.
const AirbnbStyleGuide = {
    es6: {
    }
};

export default AirbnbStyleGuide;

Acessadores (Accessors)

  • 24.1 Funções de acesso para propriedades não são obrigatórias.
  • 24.2 Não use getters/setters JavaScript, pois causam efeitos colaterais inesperados e são difíceis de testar, manter e raciocinar. Em vez disso, se você criar funções de acesso, use getVal() e setVal(‘hello’).
// bad
dragon.age();

// good
dragon.getAge();

// bad
dragon.age(25);

// good
dragon.setAge(25);
  • 24.3 Se a propriedade for um booleano, use isVal() ou hasVal().
// bad
if (!dragon.age()) {
    return false;
}

// good
if (!dragon.hasAge()) {
    return false;
}
  • 24.4 Tudo bem criar funções get() e set(), mas seja consistente.
class Jedi {
    constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
    }

    set(key, val) {
    this[key] = val;
    }

    get(key) {
    return this[key];
    }
}

Eventos (Events)

  • 25.1 Ao anexar payloads de dados a eventos (sejam eventos DOM ou algo mais proprietário como eventos Backbone), passe um hash em vez de um valor bruto. Isso permite que um colaborador subsequente adicione mais dados ao payload do evento sem encontrar e atualizar todos os manipuladores para o evento. Por exemplo, em vez de:
// bad
$(this).trigger('listingUpdated', listing.id);

...

$(this).on('listingUpdated', (e, listingId) => {
    // do something with listingId
});

prefira:

// good
$(this).trigger('listingUpdated', { listingId: listing.id });

...

$(this).on('listingUpdated', (e, data) => {
    // do something with data.listingId
});

jQuery

// bad
const sidebar = $('.sidebar');

// good
const $sidebar = $('.sidebar');

// good
const $sidebarBtn = $('.sidebar-btn');
  • 26.2 Armazene em cache as pesquisas do jQuery.
// bad
function setSidebar() {
    $('.sidebar').hide();

    // ...stuff...

    $('.sidebar').css({
    'background-color': 'pink'
    });
}

// good
function setSidebar() {
    const $sidebar = $('.sidebar');
    $sidebar.hide();

    // ...stuff...

    $sidebar.css({
    'background-color': 'pink'
    });
}
  • 26.3 Para consultas DOM, use Cascading $('.sidebar ul') ou parent > child $('.sidebar > ul'). jsPerf
  • 26.4 Use find com consultas de objeto jQuery com escopo.
// bad
$('ul', '.sidebar').hide();

// bad
$('.sidebar').find('ul').hide();

// good
$('.sidebar ul').hide();

// good
$('.sidebar > ul').hide();

// good
$sidebar.find('ul').hide();

Compatibilidade ECMAScript 5 (ECMAScript 5 Compatibility)

Estilos ECMAScript 6 (ECMAScript 6 Styles)

  • 28.1 Esta é uma coleção de links para os vários recursos do ES6.
  1. Arrow Functions
  2. Classes
  3. Object Shorthand
  4. Object Concise
  5. Computed Object Properties
  6. Template Strings
  7. Destructuring
  8. Default Parameters
  9. Rest
  10. Array Spreads
  11. Let and Const
  12. Iterators and Generators
  13. Modules

Biblioteca Padrão (Standard Library)

A Biblioteca Padrão contém alguns utilitários que estão quebrados, mas continuam lá por motivos de legado.

  • 29.1 Use Number.isNaN em vez de isNaN.

Por quê? isNaN coage valores não numéricos para números, retornando true para qualquer coisa que se converta para NaN. Se este comportamento for desejado, torne-o explícito.

// bad
isNaN('1.2'); // false
isNaN('1.2.3'); // true

// good
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true
  • 29.2 Use Number.isFinite em vez de isFinite.

Por quê? isFinite coage valores não numéricos para números, retornando true para qualquer coisa que se converta para um número finito. Se este comportamento for desejado, torne-o explícito.

// bad
isFinite('2e3'); // true

// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true

Testes (Testing)

function foo() {
    return true;
}
  • 30.2 Não, sério:
  • Não importa qual framework de teste você usa, você deve escrever testes!
  • Tente escrever muitas funções puras pequenas e minimize onde as mutações ocorrem.
  • Tenha cuidado com stubs e mocks - eles podem tornar seus testes mais frágeis.
  • Nós usamos principalmente mocha no Airbnb. tape também é usado ocasionalmente para módulos pequenos e separados.
  • 100% de cobertura de teste é um bom objetivo para se esforçar, mesmo que nem sempre seja prático alcançá-lo.
  • Sempre que você corrigir um bug, escreva um teste de regressão. Um bug corrigido sem um teste de regressão certamente quebrará novamente no futuro.

Desempenho (Performance)

Recursos (Resources)

Aprenda ES6

Leitura Obrigatória

Ferramentas

  • Code Style Linters

Outros Guias de Estilo

Outros Estilos

Leitura Adicional

Livros

Blogs

Podcasts

In the Wild

Esta é uma lista de organizações que estão usando este guia de estilo. Envie-nos um pull request ou uma issue, e nós adicionaremos você à lista.

Tradução (Translation)

Este guia de estilo também está disponível em outras línguas:

O Guia do Guia de Estilo JavaScript (The JavaScript Style Guide Guide)

Converse conosco sobre JavaScript

Contribuidores (Contributors)

Licença (License)

(The MIT License)

Copyright (c) 2014-2016 Airbnb

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Emendas (Amendments)

Nós encorajamos você a fazer um fork deste guia e alterar as regras para se adequar ao guia de estilo da sua equipe. Abaixo, você pode listar algumas emendas ao guia de estilo. Isso permite que você atualize periodicamente seu guia de estilo sem ter que lidar com conflitos de merge (merge conflicts).

Reference

All rights reserved,未經允許不得隨意轉載
Criado com Hugo
Tema Stack desenvolvido por Jimmy