Featured image of post Guía de Estilo de JavaScript de Airbnb

Guía de Estilo de JavaScript de Airbnb

Guía de Estilo de JavaScript de Airbnb

Photo by Pankaj Patel on Unsplash

https://github.com/jigsawye/javascript

Guía de Estilo de JavaScript de Airbnb()

Un enfoque mayormente razonable para JavaScript.

Downloads Gitter

Otras Guías de Estilo

Traducido de Airbnb JavaScript Style Guide.

Tipos (Types)

  • 1.1 Primitivos (Primitives): Accedes a los tipos primitivos directamente.
  • string
  • number
  • boolean
  • null
  • undefined
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • 1.2 Complejos (Complex): Accedes a los tipos complejos por referencia (by reference).
  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

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

Referencias (References)

¿Por qué? Esto asegura que no puedas reasignar tus referencias, lo que puede llevar a errores y código difícil de comprender.

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

// good
const a = 1;
const b = 2;

¿Por qué? let tiene alcance de bloque (block-scoped), mientras que var tiene alcance de función (function-scoped).

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

// good, use the let.
let count = 1;
if (true) {
    count += 1;
}
  • 2.3 Ten en cuenta que tanto let como const tienen alcance de bloque.
// const y let solo existen dentro del bloque donde se definen.
{
    let a = 1;
    const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

Objetos (Objects)

  • 3.1 Usa la sintaxis literal para la creación 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 Usa nombres de propiedades calculados (computed property names) al crear objetos con nombres de propiedades dinámicos.

¿Por qué? Te permiten definir todas las propiedades del objeto en un solo 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é? Es más corto y descriptivo.

const lukeSkywalker = 'Luke Skywalker';

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

// good
const obj = {
    lukeSkywalker,
};
  • 3.7 Agrupa tus abreviaturas al principio de tu declaración de objeto.

¿Por qué? Es más fácil saber qué propiedades están usando la abreviatura.

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é? En general, lo consideramos subjetivamente más fácil de leer. Mejora el resaltado de sintaxis y también es más fácil de optimizar por muchos motores JS.

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

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

Arreglos (Arrays)

// bad
const items = new Array();

// good
const items = [];
  • 4.2 Usa Array#push en lugar de la asignación directa para agregar elementos a un arreglo.
const someStack = [];

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

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

  • 4.3 Usa la propagación de arreglos (array spreads) ... para copiar arreglos.
// 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 convertir un objeto similar a un arreglo (array-like object) en un arreglo, usa Array#from.
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
  • 4.5 Usa declaraciones de retorno en las devoluciones de llamada de los métodos de arreglo. Está bien omitir el retorno si el cuerpo de la función consiste en una sola declaración siguiendo 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;
});

Desestructuración (Destructuring)

¿Por qué? La desestructuración te evita crear referencias temporales para esas propiedades.

// 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 Usa la desestructuración de objetos para múltiples valores de retorno, no la desestructuración de arreglos.

¿Por qué? Puedes agregar nuevas propiedades o cambiar el orden de las cosas sin romper los sitios de llamada.

// 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);

Cadenas (Strings)

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

// good
const name = 'Capt. Janeway';
  • 6.2 Las cadenas que hacen que la línea supere los 100 caracteres no deben escribirse en varias líneas usando concatenación de cadenas.
  • 6.3 Nota: Si se usa en exceso, la concatenación de cadenas largas puede afectar el rendimiento. jsPerf & Discusión.
// 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é? Las plantillas de cadena te dan una sintaxis legible y concisa con líneas nuevas y funciones de interpolación de cadenas adecuadas.

// 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 uses eval() en una cadena, abre demasiadas vulnerabilidades.

Funciones (Functions)

  • 7.1 Usa declaraciones de función (function declarations) en lugar de expresiones de función (function expressions). jscs: requireFunctionDeclarations

¿Por qué? Las declaraciones de función tienen nombre, por lo que son más fáciles de identificar en los seguimientos de pila (stack traces). Además, las declaraciones de función se elevan (hoisted), mientras que las expresiones de función no. Esta regla hace que sea fácil para Arrow Functions reemplazar completamente las expresiones de función.

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

// good
function foo() {
}

¿Por qué? Una IIFE es una unidad única: envolver la función y su invocación entre paréntesis lo hace claro. Ten en cuenta que en un mundo con módulos, casi no necesitas IIFE.

// immediately-invoked function expression (IIFE)
(function () {
    console.log('Welcome to the Internet. Please follow me.');
}());
  • 7.3 Nunca declares una función en un bloque que no sea de función (if, while, etc). Asigna la función a una variable en su lugar. Los navegadores te permitirán hacerlo, pero todos lo interpretan de manera diferente. eslint: no-loop-func

  • 7.4 Nota: ECMA-262 define un bloque como una lista de declaraciones. Una declaración de función no es una declaración. Lee la nota de ECMA-262 sobre este problema.

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

// good
let test;
if (currentUser) {
    test = () => {
    console.log('Yup.');
    };
}
  • 7.5 Nunca nombres un parámetro arguments. Esto tendrá prioridad sobre el objeto arguments que se le da a cada alcance de función.
// bad
function nope(name, options, arguments) {
    // ...stuff...
}

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

¿Por qué? ... es explícito sobre qué argumentos quieres extraer. Además, los argumentos rest son un Array real, y no simplemente 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 Usa la sintaxis de parámetros predeterminados en lugar de mutar los argumentos de la función.
// 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 Evita los efectos secundarios con parámetros predeterminados.

¿Por qué? Son 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 Pon siempre los parámetros por defecto en último lugar.
// bad
function handleThings(opts = {}, name) {
    // ...
}

// good
function handleThings(name, opts = {}) {
    // ...
}
  • 7.10 Nunca uses el constructor de Función para crear una nueva función.

¿Por qué? Crear una función de esta manera evalúa una cadena de forma similar a eval(), lo 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 Espaciado en la firma de una función.

¿Por qué? La consistencia es buena, y no deberías tener que agregar o quitar espacio al agregar o quitar un nombre.

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

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

¿Por qué? Manipular objetos pasados como parámetros puede causar efectos secundarios no deseados en el llamador 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é? Reasignar parámetros puede llevar a un comportamiento inesperado, especialmente al acceder al objeto arguments. También puede causar problemas de optimización, especialmente en 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) {
}
```

Funciones de Flecha (Arrow Functions)

¿Por qué? Crea una versión de la función que se ejecuta en el contexto de this, que es generalmente lo que quieres, y es una sintaxis más concisa.

¿Por qué no? Si tienes una función bastante compleja, podrías mover esa lógica a su propia declaración de función nombrada.

// 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é? Azúcar sintáctico (Syntactic sugar). Es más legible cuando múltiples funciones se encadenan juntas.

¿Por qué no? Si planeas devolver un 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 En caso de que la expresión se extienda a lo largo de varias líneas, envuélvela entre paréntesis para una mejor legibilidad.

¿Por qué? Muestra claramente dónde empieza y termina la función.

// 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 desorden 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 Evita confundir la sintaxis de función de flecha (=>) con operadores de comparación (<=, >=). 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; }

Constructores (Constructors)

  • 9.1 Usa siempre class. Evita manipular prototype directamente.

¿Por qué? La sintaxis class es más concisa y más fácil de razonar.

// 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 Usa extends para la herencia.

¿Por qué? Es una forma integrada de heredar la funcionalidad del prototipo sin romper 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 Los métodos pueden devolver this para ayudar con el encadenamiento 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 Está bien escribir un método toString() personalizado, solo asegúrate de que funcione correctamente y no cause efectos secundarios.
class Jedi {
    constructor(options = {}) {
    this.name = options.name || 'no name';
    }

    getName() {
    return this.name;
    }

    toString() {
    return `Jedi - ${this.getName()}`;
    }
}
  • 9.5 Las clases tienen un constructor predeterminado si no se especifica uno. Un constructor vacío o uno que simplemente delega a una clase padre es innecesario. 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 Utiliza siempre módulos (import/export) en lugar de un sistema de módulos no estándar. Siempre puedes transpirar a tu sistema de módulos preferido.

¿Por qué? Los módulos son el futuro, empecemos a usar el futuro ahora.

// 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 No uses imports con comodines (wildcard imports).

¿Por qué? Esto asegura que tengas un solo default export.

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

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 10.3 Y no exportes directamente desde un import.

¿Por qué? Aunque escribirlo en una línea es conciso, tener una forma clara de importar y una forma clara de exportar hace que las cosas sean consistentes.

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

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

Iteradores y Generadores (Iterators and Generators)

  • 11.1 No uses iteradores. Prefiere las funciones de orden superior de JavaScript como map() y reduce() en lugar de bucles como for-of. eslint: no-iterator

¿Por qué? Esto hace cumplir nuestra regla de inmutabilidad. Tratar con funciones puras que devuelven valores es más fácil de razonar que los efectos secundarios.

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 No uses generadores por ahora.

¿Por qué? No se transpilan bien a ES5 todavía.

Propiedades (Properties)

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

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

// good
const isJedi = luke.jedi;
  • 12.2 Usa la notación de corchetes [] al acceder a propiedades con una variable.
const luke = {
    jedi: true,
    age: 28,
};

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

const isJedi = getProp('jedi');

Variables (Variables)

  • 13.1 Siempre usa const o let para declarar variables. No hacerlo resultará en variables globales. Queremos evitar contaminar el espacio de nombres global (global namespace). El Capitán Planeta nos advirtió sobre eso.
// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();

¿Por qué? Es más fácil agregar nuevas declaraciones de variables de esta manera, y nunca tienes que preocuparte por cambiar un ; por una , o introducir diferencias solo de puntuación.

// 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 Agrupa todos tus const y luego agrupa todos tus let.

¿Por qué? Esto es útil cuando más tarde necesites asignar una variable dependiendo de una de las variables asignadas previamente.

// 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 Asigna variables donde las necesites, pero colócalas en un lugar razonable.

¿Por qué? let y const tienen alcance de bloque y no de función.

// 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 Las expresiones de función anónimas elevan su nombre de variable, pero no la asignación de la función.
function example() {
    console.log(anonymous); // => undefined

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

    var anonymous = function () {
    console.log('anonymous function expression');
    };
}
  • 14.3 Las expresiones de función con nombre elevan el nombre de la variable, no el nombre de la función ni el cuerpo de la función.
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 Las declaraciones de función elevan su nombre y el cuerpo de la función.
function example() {
    superPower(); // => Flying

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

Operadores de Comparación e Igualdad (Comparison Operators & Equality)

  • 15.1 Usa === y !== en lugar de == y !=. eslint: eqeqeq

  • 15.2 Las declaraciones condicionales como if evalúan su expresión usando coerción con el método abstracto ToBoolean y siempre siguen estas reglas simples:

  • Objects se evalúan como true
  • Undefined se evalúa como false
  • Null se evalúa como false
  • Booleans se evalúan como el valor del booleano
  • Numbers se evalúan como false si son +0, -0, o NaN, de lo contrario true
  • Strings se evalúan como false si es una cadena vacía '', de lo contrario 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 más información, consulta Truth Equality and JavaScript por Angus Croll.
  • 15.5 Usa llaves para crear bloques en las cláusulas case y default que contienen declaraciones léxicas (por ejemplo, let, const, function y class).

¿Por qué? Las declaraciones léxicas son visibles en todo el bloque switch pero solo se inicializan cuando se asignan, lo que solo sucede cuando se alcanza su case. Esto causa problemas cuando múltiples cláusulas case intentan definir lo mismo.

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 Los ternarios no deben anidarse y generalmente deben ser expresiones de una sola línea.

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 Evita las declaraciones ternarias innecesarias.

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;

Bloques (Blocks)

  • 16.1 Usa llaves con todos los bloques de varias líneas.
// 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();
}

Sentencias de Control (Control Statements)

  • 17.1 En caso de que su declaración de control (if, while etc.) se vuelva demasiado larga o exceda la longitud máxima de línea, cada condición (agrupada) podría ponerse en una nueva línea. El operador lógico debe comenzar la línea.

¿Por qué? Requerir operadores al comienzo de la línea mantiene los operadores alineados y sigue un patrón similar al encadenamiento de métodos. Esto también mejora la legibilidad al hacer que la lógica compleja sea más 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 No uses operadores de selección en lugar de declaraciones de control.
// bad
!isRunning && startRunning();

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

Comentarios (Comments)

  • 18.1 Usa /** ... */ para comentarios de varias líneas. Incluye una descripción, especifica tipos y valores para todos los parámetros y 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 Usa // para comentarios de una sola línea. Coloca los comentarios de una sola línea en una nueva línea sobre el sujeto del comentario. Pon una línea vacía antes del comentario a menos que esté en la primera línea de un bloque.
// 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 Prefijar tus comentarios con FIXME o TODO ayuda a otros desarrolladores a entender rápidamente si estás señalando un problema que necesita ser revisado o si estás sugiriendo una solución que necesita ser implementada. Estos son diferentes de los comentarios regulares porque son accionables. Las acciones son FIXME: -- need to figure this out o TODO: -- need to implement.

  • 18.4 Usa // FIXME: para anotar problemas.

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

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

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

Espacios en blanco (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 Termina los archivos con un solo carácter de nueva línea.
// 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é? Esto asegura la legibilidad y el mantenimiento.

// 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.'));

Comas (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é? Esto lleva a diferencias de git (git diffs) más limpias. Además, los transpiladores como Babel eliminarán la coma final adicional en el código transpilado, lo que significa que no tienes que preocuparte por el problema de la coma final en los navegadores heredados.

// 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',
];

Punto y coma (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;
}());

Leer más.

Conversión de Tipos y Coerción (Type Casting & Coercion)

  • 22.1 Realiza la coerción de tipos al principio de la sentencia.
  • 22.2 Cadenas (Strings):
// => this.reviewScore = 9;

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

// good
const totalScore = String(this.reviewScore);
  • 22.3 Números (Numbers): Usa Number para la conversión de tipos y parseInt siempre con una base para analizar cadenas. 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 Si por alguna razón estás haciendo algo salvaje y parseInt es tu cuello de botella y necesitas usar Bitshift por razones de rendimiento, deja un comentario explicando por qué y qué estás haciendo.
// 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: Ten cuidado al usar operaciones de bitshift. Los números se representan como valores de 64 bits, pero las operaciones de bitshift siempre devuelven un entero de 32 bits (fuente). El bitshift puede llevar a un comportamiento inesperado para valores enteros mayores de 32 bits. Discusión. El entero con signo de 32 bits más grande es 2,147,483,647:
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647
  • 22.6 Booleanos (Booleans):
const age = 0;

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

// good
const hasAge = Boolean(age);

// good
const hasAge = !!age;

Convenciones de Nomenclatura (Naming Conventions)

  • 23.1 Evita nombres de una sola letra. Sé descriptivo con tu 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 Si tu archivo exporta una sola clase, el nombre de tu archivo debe coincidir exactamente con el nombre de tu clase.
// 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 Usa camelCase cuando exportes por defecto una función. El nombre de tu archivo debe ser idéntico al nombre de tu función.
function makeStyleGuide() {
}

export default makeStyleGuide;
  • 23.8 Usa PascalCase cuando exportes un singleton / biblioteca de funciones / objeto simple.
const AirbnbStyleGuide = {
    es6: {
    }
};

export default AirbnbStyleGuide;

Accesores (Accessors)

  • 24.1 Las funciones de acceso para propiedades no son obligatorias.
  • 24.2 No uses getters/setters de JavaScript ya que causan efectos secundarios inesperados y son difíciles de probar, mantener y razonar. En su lugar, si creas funciones de acceso, usa getVal() y setVal(‘hello’).
// bad
dragon.age();

// good
dragon.getAge();

// bad
dragon.age(25);

// good
dragon.setAge(25);
  • 24.3 Si la propiedad es un booleano, usa isVal() o hasVal().
// bad
if (!dragon.age()) {
    return false;
}

// good
if (!dragon.hasAge()) {
    return false;
}
  • 24.4 Está bien crear funciones get() y set(), pero sé 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 Al adjuntar cargas de datos (payloads) a eventos (ya sea eventos DOM o algo más propietario como eventos de Backbone), pasa un hash en lugar de un valor sin procesar. Esto permite que un colaborador posterior agregue más datos a la carga útil del evento sin encontrar y actualizar cada controlador para el evento. Por ejemplo, en lugar de:
// bad
$(this).trigger('listingUpdated', listing.id);

...

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

prefiere:

// 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 Caché de búsquedas jQuery (jQuery lookups).
// 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, usa encadenamiento $('.sidebar ul') o padre > hijo $('.sidebar > ul'). jsPerf
  • 26.4 Usa find con consultas de objeto jQuery con alcance.
// bad
$('ul', '.sidebar').hide();

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

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

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

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

Compatibilidad con ECMAScript 5 (ECMAScript 5 Compatibility)

Estilos de ECMAScript 6 (ECMAScript 6 Styles)

  • 28.1 Esta es una colección de enlaces a las diversas características de 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 Estándar (Standard Library)

La Biblioteca Estándar contiene algunas utilidades que están rotas pero se mantienen por razones heredadas.

  • 29.1 Usa Number.isNaN en lugar de isNaN.

¿Por qué? isNaN convierte los valores no numéricos en números, devolviendo verdadero para cualquier cosa que se convierta a NaN. Si este comportamiento es deseado, hazlo 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 Usa Number.isFinite en lugar de isFinite.

¿Por qué? isFinite convierte los valores no numéricos en números, devolviendo verdadero para cualquier cosa que se convierta a un número finito. Si este comportamiento es deseado, hazlo explícito.

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

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

Pruebas (Testing)

function foo() {
    return true;
}
  • 30.2 No, en serio:
  • ¡No importa qué marco de prueba uses, deberías estar escribiendo pruebas!
  • Trata de escribir muchas funciones puras pequeñas y minimiza dónde ocurren las mutaciones.
  • Ten cuidado con los stubs y mocks: pueden hacer que tus pruebas sean más frágiles.
  • Usamos principalmente mocha en Airbnb. tape también se usa ocasionalmente para módulos pequeños y aislados.
  • El 100% de cobertura de prueba es un buen objetivo por el que luchar, incluso si no siempre es práctico alcanzarlo.
  • Cada vez que arregles un error, escribe una prueba de regresión. Un error arreglado sin una prueba de regresión es casi seguro que se romperá de nuevo en el futuro.

Rendimiento (Performance)

Recursos (Resources)

Aprender ES6

Lectura Obligatoria

Herramientas

  • Code Style Linters

Otras Guías de Estilo

Otros Estilos

Lectura Adicional

Libros

Blogs

Podcasts

En el mundo real (In the Wild)

Esta es una lista de organizaciones que utilizan esta guía de estilo. Envíanos un pull request o un issue y te añadiremos a la lista.

Traducción (Translation)

Esta guía de estilo también está disponible en otros idiomas:

Guía de Guías de Estilo de JavaScript (The JavaScript Style Guide Guide)

Chatea con nosotros sobre JavaScript

Colaboradores (Contributors)

Licencia (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.

Enmiendas (Amendments)

Te animamos a bifurcar esta guía y cambiar las reglas para que se ajusten a la guía de estilo de tu equipo. A continuación, puedes enumerar algunas enmiendas a la guía de estilo. Esto te permite actualizar periódicamente tu guía de estilo sin tener que lidiar con conflictos de fusión (merge conflicts).

Reference

All rights reserved,未經允許不得隨意轉載
Creado con Hugo
Tema Stack diseñado por Jimmy