Skip to content

mbiesiad/javascript

 
 

Repository files navigation

Airbnb Przewodnik po stylu JavaScript() {

Najbardziej rozsądne podejście do JavaScript

Uwaga: ten przewodnik zakłada, że używasz Babel, i wymaga użycia babel-preset-airbnb lub odpowiednika. Zakłada również, że instalujesz shims/polyfills w swojej aplikacji za pomocą airbnb-browser-shims lub odpowiednika.

Downloads Downloads Gitter

Ten przewodnik jest dostępny również w innych językach. Zobacz tłumaczenie

Inne przewodniki po stylach

Spis treści

  1. Typy
  2. Referencje
  3. Obiekty
  4. Tablice
  5. Destrukturyzacja
  6. Stringi
  7. Funkcje
  8. Funkcje strzałkowe
  9. Klasy & konstruktory
  10. Moduły
  11. Iteratory i generatory
  12. Właściwości
  13. Zmienne
  14. Hoisting
  15. Operatory porównania i równości
  16. Bloki
  17. Control Statements
  18. Komentarze
  19. Białe znaki
  20. Przecinki
  21. Średniki
  22. Type Casting & Coercion
  23. Konwencje nazewnictwa
  24. Akcesory
  25. Zdarzenia
  26. jQuery
  27. ECMAScript 5 Compatibility
  28. ECMAScript 6+ (ES 2015+) Styles
  29. Standard Library
  30. Testowanie
  31. Wydajność
  32. Materiały
  33. In the Wild
  34. Tłumaczenie
  35. The JavaScript Style Guide Guide
  36. Chatuj z nami na temat JavaScript
  37. Współtwórcy
  38. Licencja
  39. Poprawki

Typy

  • 1.1 Prymitywne: Kiedy uzyskujesz dostęp do typu prymitywnego, pracujesz bezpośrednio na jego wartości.

    • string
    • number
    • boolean
    • null
    • undefined
    • symbol
    • bigint
    const foo = 1;
    let bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
    • Symbole i BigInts nie mogą być wiernie wypełniane, więc nie należy ich używać podczas targetowania przeglądarek / środowisk, które nie obsługują ich natywnie.

  • 1.2 Złożone: Kiedy uzyskujesz dostęp do typu złożonego, pracujesz na referencji do jego wartości.

    • object
    • array
    • function
    const foo = [1, 2];
    const bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9

⬆ powrót do góry

Referencje

  • 2.1 Użyj const dla wszystkich twoich referencji; unikaj używania var. eslint: prefer-const, no-const-assign

    Czemu? To gwarantuje, że nie będziesz mógł ponownie przypisać swoich referencji, co może prowadzić do błędów i trudnego do zrozumienia kodu.

    // złe
    var a = 1;
    var b = 2;
    
    // dobre
    const a = 1;
    const b = 2;

  • 2.2 Jeśli musisz zmienić przypisanie referencji, użyj let zamiast var. eslint: no-var

    Czemu? let ma raczej zasięg blokowy niż funkcjonalny var.

    // złe
    var count = 1;
    if (true) {
      count += 1;
    }
    
    // dobre, użyj let.
    let count = 1;
    if (true) {
      count += 1;
    }

  • 2.3 Zauważ, że oba let i const mają zasięg blokowy.

    // const i let istnieją tylko w blokach, w których są zdefiniowane.
    {
      let a = 1;
      const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError

⬆ powrót do góry

Obiekty

  • 3.1 Użyj literalnej składni do tworzenia obiektów. eslint: no-new-object

    // złe
    const item = new Object();
    
    // dobre
    const item = {};

  • 3.2 Używaj obliczonych nazw właściwości podczas tworzenia obiektów z dynamicznymi nazwami właściwości.

    Czemu? Pozwalają zdefiniować wszystkie właściwości obiektu w jednym miejscu.

    function getKey(k) {
      return `a key named ${k}`;
    }
    
    // złe
    const obj = {
      id: 5,
      name: 'San Francisco',
    };
    obj[getKey('enabled')] = true;
    
    // dobre
    const obj = {
      id: 5,
      name: 'San Francisco',
      [getKey('enabled')]: true,
    };

  • 3.3 Użyj metody obiektowej shorthand. eslint: object-shorthand

    // złe
    const atom = {
      value: 1,
    
      addValue: function (value) {
        return atom.value + value;
      },
    };
    
    // dobre
    const atom = {
      value: 1,
    
      addValue(value) {
        return atom.value + value;
      },
    };

  • 3.4 Użyj wartości właściwości shorthand. eslint: object-shorthand

    Czemu? Jest krótszy i opisowy.

    const lukeSkywalker = 'Luke Skywalker';
    
    // złe
    const obj = {
      lukeSkywalker: lukeSkywalker,
    };
    
    // dobre
    const obj = {
      lukeSkywalker,
    };

  • 3.5 Zgrupuj właściwości shorthand na początku deklaracji obiektu.

    Czemu? Łatwiej jest powiedzieć, które właściwości używają shorthand.

    const anakinSkywalker = 'Anakin Skywalker';
    const lukeSkywalker = 'Luke Skywalker';
    
    // złe
    const obj = {
      episodeOne: 1,
      twoJediWalkIntoACantina: 2,
      lukeSkywalker,
      episodeThree: 3,
      mayTheFourth: 4,
      anakinSkywalker,
    };
    
    // dobre
    const obj = {
      lukeSkywalker,
      anakinSkywalker,
      episodeOne: 1,
      twoJediWalkIntoACantina: 2,
      episodeThree: 3,
      mayTheFourth: 4,
    };

  • 3.6 Podaj tylko właściwości, które są niepoprawnymi identyfikatorami. eslint: quote-props

    Czemu? Ogólnie uważamy, że jest subiektywnie łatwiejszy do odczytania. Poprawia podświetlanie składni, a także jest łatwiej zoptymalizowany przez wiele silników JS.

    // złe
    const bad = {
      'foo': 3,
      'bar': 4,
      'data-blah': 5,
    };
    
    // dobre
    const good = {
      foo: 3,
      bar: 4,
      'data-blah': 5,
    };

  • 3.7 Nie wywołuj metod Object.prototype bezpośrednio, tak jak hasOwnProperty, propertyIsEnumerable, i isPrototypeOf. eslint: no-prototype-builtins

    Czemu? Metody te mogą być zaciemnione przez właściwości danego obiektu - rozważ { hasOwnProperty: false } - lub, obiekt może być obiektem null (Object.create(null)).

    // złe
    console.log(object.hasOwnProperty(key));
    
    // dobre
    console.log(Object.prototype.hasOwnProperty.call(object, key));
    
    // best
    const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
    console.log(has.call(object, key));
    /* or */
    import has from 'has'; // https://www.npmjs.com/package/has
    console.log(has(object, key));

  • 3.8 Preferuj object spread operator nad Object.assign do obiektów shallow-copy. Użyj object rest operator, aby uzyskać nowy obiekt z pominięciem niektórych właściwości.

    // bardzo złe
    const original = { a: 1, b: 2 };
    const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
    delete copy.a; // so does this
    
    // złe
    const original = { a: 1, b: 2 };
    const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
    
    // dobre
    const original = { a: 1, b: 2 };
    const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
    
    const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

⬆ powrót do góry

Tablice

  • 4.1Użyj literalnej składni do tworzenia tablicy. eslint: no-array-constructor

    // złe
    const items = new Array();
    
    // dobre
    const items = [];

  • 4.2 Użyj Array#push zamiast bezpośredniego przypisania, aby dodać elementy do tablicy.

    const someStack = [];
    
    // złe
    someStack[someStack.length] = 'abracadabra';
    
    // dobre
    someStack.push('abracadabra');

  • 4.3 Użyj array spreads ... aby kopiować tablice.

    // złe
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i += 1) {
      itemsCopy[i] = items[i];
    }
    
    // dobre
    const itemsCopy = [...items];

  • 4.4 Aby przekonwertować obiekt iterable object na tablicę, użyj spreads ... zamiast Array.from.

    const foo = document.querySelectorAll('.foo');
    
    // dobre
    const nodes = Array.from(foo);
    
    // najlepsze
    const nodes = [...foo];

  • 4.5 Użyj Array.from do konwersji obiektu podobnego do tablicy na tablicę.

    const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
    
    // złe
    const arr = Array.prototype.slice.call(arrLike);
    
    // dobre
    const arr = Array.from(arrLike);

  • 4.6 Użyj Array.from zamiast spread ... do mapowania przez iterable, ponieważ unika się tworzenia tablicy pośredniej.

    // złe
    const baz = [...foo].map(bar);
    
    // dobre
    const baz = Array.from(foo, bar);

  • 4.7 Używaj instrukcji return w wywołaniach zwrotnych metody tablicowej. Można pominąć return, jeśli treść funkcji składa się z pojedynczej instrukcji zwracającej wyrażenie bez skutków ubocznych, następująco 8.2. eslint: array-callback-return

    // dobre
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
    
    // dobre
    [1, 2, 3].map((x) => x + 1);
    
    // źle - brak zwracanej wartości oznacza, że `acc` staje się niezdefiniowany po pierwszej iteracji
    [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
      const flatten = acc.concat(item);
    });
    
    // dobre
    [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
      const flatten = acc.concat(item);
      return flatten;
    });
    
    // złe
    inbox.filter((msg) => {
      const { subject, author } = msg;
      if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
      } else {
        return false;
      }
    });
    
    // dobre
    inbox.filter((msg) => {
      const { subject, author } = msg;
      if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
      }
    
      return false;
    });

  • 4.8 Użyj podziałów linii po otwarciu i przed zamknięciem nawiasów tablicy, jeśli tablica ma wiele linii

    // złe
    const arr = [
      [0, 1], [2, 3], [4, 5],
    ];
    
    const objectInArray = [{
      id: 1,
    }, {
      id: 2,
    }];
    
    const numberInArray = [
      1, 2,
    ];
    
    // dobre
    const arr = [[0, 1], [2, 3], [4, 5]];
    
    const objectInArray = [
      {
        id: 1,
      },
      {
        id: 2,
      },
    ];
    
    const numberInArray = [
      1,
      2,
    ];

⬆ powrót do góry

Destrukturyzacja

  • 5.1 Użyj destrukturyzacji obiektów podczas uzyskiwania dostępu do wielu właściwości obiektu i korzystania z nich. eslint: prefer-destructuring

    Czemu? Destrukturyzacja pozwala uniknąć tworzenia tymczasowych odniesień do tych właściwości.

    // złe
    function getFullName(user) {
      const firstName = user.firstName;
      const lastName = user.lastName;
    
      return `${firstName} ${lastName}`;
    }
    
    // dobre
    function getFullName(user) {
      const { firstName, lastName } = user;
      return `${firstName} ${lastName}`;
    }
    
    // najlepsze
    function getFullName({ firstName, lastName }) {
      return `${firstName} ${lastName}`;
    }

  • 5.2 Użyj destrukturyzacji tablic. eslint: prefer-destructuring

    const arr = [1, 2, 3, 4];
    
    // złe
    const first = arr[0];
    const second = arr[1];
    
    // dobre
    const [first, second] = arr;

  • 5.3 Użyj niszczenia obiektów dla wielu zwracanych wartości, a nie niszczenia tablic.

    Czemu? Z czasem możesz dodawać nowe właściwości lub zmieniać kolejność rzeczy bez przerywania wywołań stron.

    // złe
    function processInput(input) {
      // then a miracle occurs
      return [left, right, top, bottom];
    }
    
    // wywołujący musi pomyśleć o kolejności danych zwrotnych
    const [left, __, top] = processInput(input);
    
    // dobre
    function processInput(input) {
      // then a miracle occurs
      return { left, right, top, bottom };
    }
    
    // wywołujący wybiera tylko te dane, których potrzebuje
    const { left, top } = processInput(input);

⬆ powrót do góry

Stringi

  • 6.1 Używaj pojedynczych cudzysłowów '' dla stringów. eslint: quotes

    // złe
    const name = "Capt. Janeway";
    
    // źle - literały szablonów powinny zawierać interpolację lub znaki nowej linii
    const name = `Capt. Janeway`;
    
    // dobre
    const name = 'Capt. Janeway';

  • 6.2 Stringi które powodują, że linia przekracza 100 znaków, nie należy pisać w wielu wierszach za pomocą konkatenacji łańcuchów.

    Czemu? Przerwane stringi są ciężkie w pracy i sprawiają, że kod jest mniej przeszukiwalny.

    // złe
    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.';
    
    // złe
    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.';
    
    // dobre
    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.';

  • 6.3 Podczas programowego budowania stringów należy używać ciągów szablonów zamiast konkatenacji. eslint: prefer-template template-curly-spacing

    Czemu? Ciągi szablonów zapewniają czytelną, zwięzłą składnię z odpowiednimi znakami nowej linii i funkcjami interpolacji ciągów.

    // złe
    function sayHi(name) {
      return 'How are you, ' + name + '?';
    }
    
    // złe
    function sayHi(name) {
      return ['How are you, ', name, '?'].join();
    }
    
    // złe
    function sayHi(name) {
      return `How are you, ${ name }?`;
    }
    
    // dobre
    function sayHi(name) {
      return `How are you, ${name}?`;
    }

  • 6.4 Nigdy nie używaj eval() na stringa, otwiera zbyt wiele luk w zabezpieczeniach. eslint: no-eval

  • 6.5 Do not unnecessarily escape characters in strings. eslint: no-useless-escape

    Czemu? Ukośniki odwrotne szkodzą czytelności, dlatego powinny być obecne tylko wtedy, gdy jest to konieczne.

    // złe
    const foo = '\'this\' \i\s \"quoted\"';
    
    // dobre
    const foo = '\'this\' is "quoted"';
    const foo = `my name is '${name}'`;

⬆ powrót do góry

Funkcje

  • 7.1 Użyj nazwanych wyrażeń funkcyjnych zamiast deklaracji funkcji. eslint: func-style

    Czemu? Deklaracje funkcji są hoisted, co oznacza, że łatwo - zbyt łatwo - odwołać się do funkcji, zanim zostanie zdefiniowana w pliku. Utrudnia to czytelność i łatwość konserwacji. Jeśli okaże się, że definicja funkcji jest na tyle duża lub wystarczająco złożona, że zakłóca zrozumienie reszty pliku, być może nadszedł czas, aby wyodrębnić ją do własnego modułu! Nie zapomnij wyraźnie nazwać wyrażenia, niezależnie od tego, czy nazwa jest wywnioskowana ze zmiennej zawierającej (co często ma miejsce w nowoczesnych przeglądarkach lub podczas korzystania z kompilatorów takich jak Babel). Eliminuje to wszelkie założenia dotyczące stosu wywołań błędu. (Discussion)

    // złe
    function foo() {
      // ...
    }
    
    // złe
    const foo = function () {
      // ...
    };
    
    // dobre
    // nazwa leksykalna odróżniona od wywołania (odwołań)
    const short = function longUniqueMoreDescriptiveLexicalFoo() {
      // ...
    };

  • 7.2 Zawiń natychmiast wywołane wyrażenia funkcyjne w nawiasach. eslint: wrap-iife

    Why? An immediately invoked function expression is a single unit - wrapping both it, and its invocation parens, in parens, cleanly expresses this. Note that in a world with modules everywhere, you almost never need an IIFE.

    // natychmiast wywoływane wyrażenie funkcyjne (IIFE)
    (function () {
      console.log('Welcome to the Internet. Please follow me.');
    }());

  • 7.3 Nigdy nie deklaruj funkcji w bloku niefunkcyjnym (if, while, etc). Zamiast tego przypisz funkcję do zmiennej. Przeglądarki pozwolą ci to zrobić, ale wszystkie interpretują to inaczej, co jest złą wiadomością. eslint: no-loop-func

  • 7.4 Uwaga: ECMA-262 definiuje block jako listę instrukcji. Deklaracja funkcji nie jest instrukcją.

    // złe
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // dobre
    let test;
    if (currentUser) {
      test = () => {
        console.log('Yup.');
      };
    }

- 7.5 Nigdy nie nazywaj parametru arguments. Będzie to miało pierwszeństwo przed obiektem arguments, który jest nadawany każdemu zakresowi funkcji.

```javascript
// złe
function foo(name, options, arguments) {
  // ...
}

// dobre
function foo(name, options, args) {
  // ...
}
```

  • 7.6 Nigdy nie używaj arguments, zdecyduj się zamiast tego na użycie składni rest syntax .... eslint: prefer-rest-params

    Czemu? ... wyraźnie określa, które argumenty chcesz wyciągnąć. Dodatkowo, argumenty rest są prawdziwym Array, a nie tylko podobne do Array jak arguments.

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

  • 7.7 Użyj domyślnej składni parametrów zamiast mutować argumenty funkcji.

    // naprawdę źle
    function handleThings(opts) {
      // No! We shouldn’t mutate function arguments.
      // Double bad: if opts is falsy it'll be set to an object which may
      // be what you want but it can introduce subtle bugs.
      opts = opts || {};
      // ...
    }
    
    // dalej źle
    function handleThings(opts) {
      if (opts === void 0) {
        opts = {};
      }
      // ...
    }
    
    // dobrze
    function handleThings(opts = {}) {
      // ...
    }

  • 7.8 Unikaj efektów ubocznych przy domyślnych parametrach.

    Czemu? Są mylące.

    var b = 1;
    // złe
    function count(a = b++) {
      console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3

  • 7.9 Zawsze umieszczaj parametry domyślne na końcu.

    // złe
    function handleThings(opts = {}, name) {
      // ...
    }
    
    // dobre
    function handleThings(name, opts = {}) {
      // ...
    }

  • 7.10 Nigdy nie używaj konstruktora funkcji do tworzenia nowej funkcji. eslint: no-new-func

    Czemu? Utworzenie funkcji w ten sposób ocenia ciąg podobny do eval(), który otwiera luki w zabezpieczeniach.

    // złe
    var add = new Function('a', 'b', 'return a + b');
    
    // dalej złe
    var subtract = Function('a', 'b', 'return a - b');

  • 7.11 Odstępy w podpisie funkcji. eslint: space-before-function-paren space-before-blocks

    Czemu? Spójność jest dobra i nie trzeba dodawać ani usuwać spacji podczas dodawania lub usuwania nazwy.

    // złe
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // dobre
    const x = function () {};
    const y = function a() {};

  • 7.12 Nigdy nie mutuj parametrów. eslint: no-param-reassign

    Czemu? Manipulowanie obiektami przekazywanymi jako parametry może powodować niepożądane zmienne skutki uboczne w pierwotnym obiekcie wywołującym.

    // złe
    function f1(obj) {
      obj.key = 1;
    }
    
    // dobre
    function f2(obj) {
      const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
    }

  • 7.13 Nigdy nie przypisuj ponownie parametrów. eslint: no-param-reassign

    Czemu? Ponowne przypisanie parametrów może prowadzić do nieoczekiwanego zachowania, szczególnie podczas uzyskiwania dostępu do obiektu arguments. Może to również powodować problemy z optymalizacją, szczególnie w wersji V8.

    // złe
    function f1(a) {
      a = 1;
      // ...
    }
    
    function f2(a) {
      if (!a) { a = 1; }
      // ...
    }
    
    // dobre
    function f3(a) {
      const b = a || 1;
      // ...
    }
    
    function f4(a = 1) {
      // ...
    }

  • 7.14 Preferuj uzycie operatora spread operator ... aby wywoływać funkcje variadic. eslint: prefer-spread

    Czemu? Jest czyściej, nie musisz podawać kontekstu i nie możesz łatwo składać new z apply.

    // złe
    const x = [1, 2, 3, 4, 5];
    console.log.apply(console, x);
    
    // dobre
    const x = [1, 2, 3, 4, 5];
    console.log(...x);
    
    // dobre
    new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
    
    // dobre
    new Date(...[2016, 8, 5]);

  • 7.15 Funkcje z podpisami wielowierszowymi lub wywołaniami powinny być wcięte, tak jak każda inna lista wielowierszowa w tym przewodniku: każdy element w linii sam, z końcowym przecinkiem na ostatnim elemencie. eslint: function-paren-newline

    // złe
    function foo(bar,
                 baz,
                 quux) {
      // ...
    }
    
    // dobre
    function foo(
      bar,
      baz,
      quux,
    ) {
      // ...
    }
    
    // złe
    console.log(foo,
      bar,
      baz);
    
    // dobre
    console.log(
      foo,
      bar,
      baz,
    );

⬆ powrót do góry

Funkcje strzałkowe

  • 8.1 Gdy musisz użyć funkcji anonimowej (tak jak przy przekazywaniu inline callback), użyj notacji funkcji strzałek. eslint: prefer-arrow-callback, arrow-spacing

    Czemu? Tworzy wersję funkcji, która działa w kontekście this, która jest zwykle tym, czego chcesz, i jest bardziej zwięzłą składnią.

    Dlaczego nie? Jeśli masz dość skomplikowaną funkcję, możesz przenieść tę logikę do własnego nazwanego wyrażenia funkcji.

    // złe
    [1, 2, 3].map(function (x) {
      const y = x + 1;
      return x * y;
    });
    
    // dobre
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });

  • 8.2 Jeśli ciało funkcji składa się z pojedynczej instrukcji zwracającej wyrażeniebez skutków ubocznych, pomiń nawiasy klamrowe i użyj domyślnego return. W przeciwnym razie zachowaj nawiasy klamrowe i użyj instrukcji return. eslint: arrow-parens, arrow-body-style

    Czemu? Czystość kodu. Czyta się dobrze, gdy połączonych jest wiele funkcji.

    // złe
    [1, 2, 3].map((number) => {
      const nextNumber = number + 1;
      `A string containing the ${nextNumber}.`;
    });
    
    // dobre
    [1, 2, 3].map((number) => `A string containing the ${number + 1}.`);
    
    // dobre
    [1, 2, 3].map((number) => {
      const nextNumber = number + 1;
      return `A string containing the ${nextNumber}.`;
    });
    
    // dobre
    [1, 2, 3].map((number, index) => ({
      [index]: number,
    }));
    
    // No implicit return with side effects
    function foo(callback) {
      const val = callback();
      if (val === true) {
        // Do something if callback returns true
      }
    }
    
    let bool = false;
    
    // złe
    foo(() => bool = true);
    
    // dobre
    foo(() => {
      bool = true;
    });

  • 8.3 W przypadku gdy wyrażenie obejmuje wiele wierszy, zawiń je w nawiasach, aby uzyskać lepszą czytelność.

    Czemu? Pokazuje wyraźnie, gdzie funkcja zaczyna się i kończy.

    // złe
    ['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
        httpMagicObjectWithAVeryLongName,
        httpMethod,
      )
    );
    
    // dobre
    ['get', 'post', 'put'].map((httpMethod) => (
      Object.prototype.hasOwnProperty.call(
        httpMagicObjectWithAVeryLongName,
        httpMethod,
      )
    ));

  • 8.4 Zawsze dołączaj nawiasy wokół argumentów dla jasności i spójności. eslint: arrow-parens

    Czemu? Minimalizuje rezygnację z różnic podczas dodawania lub usuwania argumentów.

    // złe
    [1, 2, 3].map(x => x * x);
    
    // dobre
    [1, 2, 3].map((x) => x * x);
    
    // złe
    [1, 2, 3].map(number => (
      `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
    ));
    
    // dobre
    [1, 2, 3].map((number) => (
      `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
    ));
    
    // złe
    [1, 2, 3].map(x => {
      const y = x + 1;
      return x * y;
    });
    
    // dobre
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });

  • 8.5 Unikaj mylącej składni funkcji strzałek (=>) z operatorami porównania (<=, >=). eslint: no-confusing-arrow

    // złe
    const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize;
    
    // złe
    const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
    
    // dobre
    const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
    
    // dobre
    const itemHeight = (item) => {
      const { height, largeSize, smallSize } = item;
      return height <= 256 ? largeSize : smallSize;
    };

  • 8.6 Wymuszaj lokalizację obiektów funkcji strzałek za pomocą niejawnych zwrotów. eslint: implicit-arrow-linebreak

    // złe
    (foo) =>
      bar;
    
    (foo) =>
      (bar);
    
    // dobre
    (foo) => bar;
    (foo) => (bar);
    (foo) => (
       bar
    )

⬆ powrót do góry

Klasy i konstruktory

  • 9.1 Zawsze używaj class. Unikaj bezpośredniego manipulowania prototype.

    Czemu? Składnia class jest bardziej zwięzła i łatwiejsza do uzasadnienia.

    // złe
    function Queue(contents = []) {
      this.queue = [...contents];
    }
    Queue.prototype.pop = function () {
      const value = this.queue[0];
      this.queue.splice(0, 1);
      return value;
    };
    
    // dobre
    class Queue {
      constructor(contents = []) {
        this.queue = [...contents];
      }
      pop() {
        const value = this.queue[0];
        this.queue.splice(0, 1);
        return value;
      }
    }

  • 9.2 Użyj extends do dziedziczenia.

    Czemu? Jest to wbudowany sposób na dziedziczenie prototypowej funkcjonalności bez przerywania instanceof.

    // złe
    const inherits = require('inherits');
    function PeekableQueue(contents) {
      Queue.apply(this, contents);
    }
    inherits(PeekableQueue, Queue);
    PeekableQueue.prototype.peek = function () {
      return this.queue[0];
    };
    
    // dobre
    class PeekableQueue extends Queue {
      peek() {
        return this.queue[0];
      }
    }

  • 9.3 Metody mogą zwracać wartość this, aby pomóc w tworzeniu łańcuchów metod.

    // złe
    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
    
    // dobre
    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 Można napisać niestandardową metodę toString(), upewnij się tylko, że działa ona skutecznie i nie powoduje żadnych skutków ubocznych.

    class Jedi {
      constructor(options = {}) {
        this.name = options.name || 'no name';
      }
    
      getName() {
        return this.name;
      }
    
      toString() {
        return `Jedi - ${this.getName()}`;
      }
    }

  • 9.5 Klasy mają domyślny konstruktor, jeśli nie został określony. Pusta funkcja konstruktora lub taka, która po prostu deleguje do klasy nadrzędnej, nie jest potrzebna. eslint: no-useless-constructor

    // złe
    class Jedi {
      constructor() {}
    
      getName() {
        return this.name;
      }
    }
    
    // złe
    class Rey extends Jedi {
      constructor(...args) {
        super(...args);
      }
    }
    
    // dobre
    class Rey extends Jedi {
      constructor(...args) {
        super(...args);
        this.name = 'Rey';
      }
    }

  • 9.6 Unikaj zduplikowanych członków klasy. eslint: no-dupe-class-members

    Czemu? Zduplikowane deklaracje członków klasy po cichu wolą ostatnią - posiadanie duplikatów jest prawie na pewno błędem.

    // złe
    class Foo {
      bar() { return 1; }
      bar() { return 2; }
    }
    
    // dobre
    class Foo {
      bar() { return 1; }
    }
    
    // dobre
    class Foo {
      bar() { return 2; }
    }

  • 9.7 Metody klas powinny używać this lub zostać przekształcone w metodę statyczną, chyba że zewnętrzna biblioteka lub środowisko wymaga użycia określonych metod niestatycznych. Jako metoda instancji powinna wskazywać, że zachowuje się inaczej w zależności od właściwości receivera. eslint: class-methods-use-this

    // złe
    class Foo {
      bar() {
        console.log('bar');
      }
    }
    
    // dobrze - jest używane
    class Foo {
      bar() {
        console.log(this.bar);
      }
    }
    
    // dobrze - konstruktor jest zwolniony
    class Foo {
      constructor() {
        // ...
      }
    }
    
    // dobrze - metody statyczne nie powinny z tego korzystać
    class Foo {
      static bar() {
        console.log('bar');
      }
    }

⬆ powrót do góry

Moduły

  • 10.1 Zawsze używaj modułów (import/export) przez niestandardowy system modułowy. Zawsze możesz dokonać transpilacji do preferowanego systemu modułów.

    Czemu? Moduły są przyszłością, zacznijmy korzystać z przyszłości już teraz.

    // złe
    const AirbnbStyleGuide = require('./AirbnbStyleGuide');
    module.exports = AirbnbStyleGuide.es6;
    
    // ok
    import AirbnbStyleGuide from './AirbnbStyleGuide';
    export default AirbnbStyleGuide.es6;
    
    // najlepsze
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.2 Nie używaj wildcard imports.

    Czemu? Dzięki temu masz tylko jeden domyślny eksport.

    // złe
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';
    
    // dobre
    import AirbnbStyleGuide from './AirbnbStyleGuide';

  • 10.3 I nie eksportuj bezpośrednio z importu.

    Czemu? Chociaż one-liner jest zwięzły, posiadanie jednego jasnego sposobu importowania i jednego jasnego sposobu eksportowania zapewnia spójność.

    // złe
    // filename es6.js
    export { es6 as default } from './AirbnbStyleGuide';
    
    // dobre
    // filename es6.js
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.4 Importuj tylko ze ścieżki w jednym miejscu. eslint: no-duplicate-imports

    Czemu? Posiadanie wielu wierszy importowanych z tej samej ścieżki może utrudnić utrzymanie kodu.

    // złe
    import foo from 'foo';
    // … some other imports … //
    import { named1, named2 } from 'foo';
    
    // dobre
    import foo, { named1, named2 } from 'foo';
    
    // dobre
    import foo, {
      named1,
      named2,
    } from 'foo';

  • 10.5 Do not export mutable bindings. eslint: import/no-mutable-exports

    Czemu? Ogólnie należy unikać mutacji, ale w szczególności podczas eksportowania mutable bindings. Chociaż ta technika może być potrzebna w niektórych szczególnych przypadkach, na ogół należy eksportować tylko constant references.

    // złe
    let foo = 3;
    export { foo };
    
    // dobre
    const foo = 3;
    export { foo };

  • 10.6 W modułach z pojedynczym eksportem preferuj domyślny eksport niż eksport nazwany. eslint: import/prefer-default-export

    Czemu? Aby 'zachęcić' więcej plików, które kiedykolwiek eksportują tylko jedną rzecz. Jest to lepsze dla czytelności i konserwacji.

    // złe
    export function foo() {}
    
    // dobre
    export default function foo() {}

  • 10.7 Put all imports above non-import statements. eslint: import/first

    Why? Since imports are hoisted, keeping them all at the top prevents surprising behavior.

    // złe
    import foo from 'foo';
    foo.init();
    
    import bar from 'bar';
    
    // dobre
    import foo from 'foo';
    import bar from 'bar';
    
    foo.init();

  • 10.8 Multiline imports should be indented just like multiline array and object literals. eslint: object-curly-newline

    Czemu? Nawiasy klamrowe są zgodne z tymi samymi zasadami wcięcia, co każdy inny blok nawiasów klamrowych w przewodniku stylu, podobnie jak końcowe przecinki.

    // złe
    import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
    
    // dobre
    import {
      longNameA,
      longNameB,
      longNameC,
      longNameD,
      longNameE,
    } from 'path';

  • 10.9 Nie zezwalaj na składnię modułu ładującego Webpack w instrukcjach importu modułu. eslint: import/no-webpack-loader-syntax

    Czemu? Ponieważ podczas importowania używa się składni Webpack, kod łączy się z pakietem modułów. Preferuj użycie składni modułu ładującego w webpack.config.js.

    // złe
    import fooSass from 'css!sass!foo.scss';
    import barCss from 'style!css!bar.css';
    
    // dobre
    import fooSass from 'foo.scss';
    import barCss from 'bar.css';

  • 10.10 Nie dołączaj rozszerzeń nazw JavaScript eslint: import/extensions

    Czemu? Dołączanie rozszerzeń hamuje refaktoryzację i niewłaściwe kodowanie szczegółów implementacji modułu importowanego do każdego konsumenta.

    // złe
    import foo from './foo.js';
    import bar from './bar.jsx';
    import baz from './baz/index.jsx';
    
    // dobre
    import foo from './foo';
    import bar from './bar';
    import baz from './baz';

⬆ powrót do góry

Iteratory i generatory

  • 11.1 Nie używaj iteratorów. Wybieraj funkcje wyższego rzędu JavaScript zamiast takich jak pętle for-in lub for-of. eslint: no-iterator no-restricted-syntax

    Czemu? To wymusza naszą niezmienną zasadę. Radzenie sobie z czystymi funkcjami zwracającymi wartości jest łatwiejsze do uzasadnienia niż skutki uboczne.

    Użyj map() / every() / filter() / find() / findIndex() / reduce() / some() / ... do iterowania po tablicach, i Object.keys() / Object.values() / Object.entries() do tworzenia tablic, dzięki czemu można iterować po obiektach.

    const numbers = [1, 2, 3, 4, 5];
    
    // złe
    let sum = 0;
    for (let num of numbers) {
      sum += num;
    }
    sum === 15;
    
    // dobre
    let sum = 0;
    numbers.forEach((num) => {
      sum += num;
    });
    sum === 15;
    
    // najlepiej (użyj functional force)
    const sum = numbers.reduce((total, num) => total + num, 0);
    sum === 15;
    
    // złe
    const increasedByOne = [];
    for (let i = 0; i < numbers.length; i++) {
      increasedByOne.push(numbers[i] + 1);
    }
    
    // dobre
    const increasedByOne = [];
    numbers.forEach((num) => {
      increasedByOne.push(num + 1);
    });
    
    // najlepiej (utrzymanie funkcjonalności)
    const increasedByOne = numbers.map((num) => num + 1);

  • 11.2 Na razie nie używaj generatorów.

    Czemu? Nie przenoszą się dobrze na ES5.

  • 11.3 Jeśli musisz użyć generatorów lub zignorujesz naszą radę, upewnij się, że podpis funkcji jest odpowiednio rozmieszczony. eslint: generator-star-spacing

    Czemu? function oraz * są częścią tego samego koncepcyjnego słowa kluczowego - * nie jest modyfikatorem dla function, function* jest unikalnym construct, innym niż function.

    // złe
    function * foo() {
      // ...
    }
    
    // złe
    const bar = function * () {
      // ...
    };
    
    // złe
    const baz = function *() {
      // ...
    };
    
    // złe
    const quux = function*() {
      // ...
    };
    
    // złe
    function*foo() {
      // ...
    }
    
    // złe
    function *foo() {
      // ...
    }
    
    // bardzo złe
    function
    *
    foo() {
      // ...
    }
    
    // bardzo złe
    const wat = function
    *
    () {
      // ...
    };
    
    // dobre
    function* foo() {
      // ...
    }
    
    // dobre
    const foo = function* () {
      // ...
    };

⬆ powrót do góry

Właściwości

  • 12.1 Używaj notacji kropkowej podczas uzyskiwania dostępu do właściwości. eslint: dot-notation

    const luke = {
      jedi: true,
      age: 28,
    };
    
    // złe
    const isJedi = luke['jedi'];
    
    // dobre
    const isJedi = luke.jedi;

  • 12.2 Użyj notacji nawiasowej [] podczas uzyskiwania dostępu do właściwości za pomocą zmiennej.

    const luke = {
      jedi: true,
      age: 28,
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    const isJedi = getProp('jedi');

  • 12.3 Użyj operatora potęgowania ** przy obliczaniu wykładników. eslint: no-restricted-properties.

    // złe
    const binary = Math.pow(2, 10);
    
    // dobre
    const binary = 2 ** 10;

⬆ powrót do góry

Zmienne

  • 13.1 Zawsze używaj const lub let aby deklarować zmienne. Nieprzestrzeganie tego spowoduje globalne zmienne. Chcemy uniknąć zanieczyszczenia globalnej przestrzeni nazw. Kapitan Planeta nas przed tym ostrzegł. eslint: no-undef prefer-const

    // złe
    superPower = new SuperPower();
    
    // dobre
    const superPower = new SuperPower();

  • 13.2 Użyj jednej deklaracji const lub let na zmienną lub przypisanie. eslint: one-var

    Czemu? W ten sposób łatwiej jest dodawać nowe deklaracje zmiennych i nigdy nie musisz się martwić o zamianę ; na , lub wprowadzenie różnic tylko interpunkcyjnych. Możesz również przejrzeć każdą deklarację za pomocą debuggera, zamiast przeskakiwać wszystkie z nich jednocześnie.

    // złe
    const items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
    // złe
    // (porównaj z powyższym i spróbuj wykryć błąd)
    const items = getItems(),
        goSportsTeam = true;
        dragonball = 'z';
    
    // dobre
    const items = getItems();
    const goSportsTeam = true;
    const dragonball = 'z';

  • 13.3 Zgrupuj wszystkie swoje consty, a następnie zgrupuj wszystkie twoje lety.

    Czemu? Jest to pomocne, gdy później może być konieczne przypisanie zmiennej w zależności od jednej z wcześniej przypisanych zmiennych.

    // złe
    let i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // złe
    let i;
    const items = getItems();
    let dragonball;
    const goSportsTeam = true;
    let len;
    
    // dobre
    const goSportsTeam = true;
    const items = getItems();
    let dragonball;
    let i;
    let length;

  • 13.4 Przypisz zmienne tam, gdzie ich potrzebujesz, ale umieść je w rozsądnym miejscu.

    Czemu? let oraz const mają zasięg blokowy, a nie zasięg funkcjonalny.

    // złe - niepotrzebne wywołanie funkcji
    function checkName(hasName) {
      const name = getName();
    
      if (hasName === 'test') {
        return false;
      }
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }
    
    // dobre
    function checkName(hasName) {
      if (hasName === 'test') {
        return false;
      }
    
      const name = getName();
    
      if (name === 'test') {
        this.setName('');
        return false;
      }
    
      return name;
    }

  • 13.5 Nie łącz łańcuchów przypisań zmiennych. eslint: no-multi-assign

    Czemu? Łańcuchowe przypisania zmiennych tworzy niejawne zmienne globalne.

    // złe
    (function example() {
      // JavaScript interprets this as
      // let a = ( b = ( c = 1 ) );
      // The let keyword only applies to variable a; variables b and c become
      // global variables.
      let a = b = c = 1;
    }());
    
    console.log(a); // throws ReferenceError
    console.log(b); // 1
    console.log(c); // 1
    
    // dobre
    (function example() {
      let a = 1;
      let b = a;
      let c = a;
    }());
    
    console.log(a); // throws ReferenceError
    console.log(b); // throws ReferenceError
    console.log(c); // throws ReferenceError
    
    // the same applies for `const`

  • 13.6 Unikaj stosowania jednoargumentowych inkrementacji i dekrementacji (++, --). eslint no-plusplus

    Czemu? Zgodnie z dokumentacją eslint, jednostkowe instrukcje inkrementacji i dekrementacji podlegają automatycznemu wstawianiu średników i mogą powodować ciche błędy przy zwiększaniu lub zmniejszaniu wartości w aplikacji. Bardziej wyraziste jest również mutowanie wartości za pomocą instrukcji takich jak num + = 1 zamiast num ++ lub num++. Nie zezwalanie na jednostkowe instrukcje inkrementacji i dekrementacji zapobiega również przypadkowemu zwiększaniu / dekrementowaniu wartości, co może również powodować nieoczekiwane zachowanie w programach.

    // złe
    
    const array = [1, 2, 3];
    let num = 1;
    num++;
    --num;
    
    let sum = 0;
    let truthyCount = 0;
    for (let i = 0; i < array.length; i++) {
      let value = array[i];
      sum += value;
      if (value) {
        truthyCount++;
      }
    }
    
    // dobre
    
    const array = [1, 2, 3];
    let num = 1;
    num += 1;
    num -= 1;
    
    const sum = array.reduce((a, b) => a + b, 0);
    const truthyCount = array.filter(Boolean).length;

  • 13.7 Unikaj łamania linii przed lub po = w przypisaniu. If your assignment violates max-len, surround the value in parens. eslint operator-linebreak.

    Czemu? Linie podziału wokół = mogą zaciemnić wartość przypisania.

    // złe
    const foo =
      superLongLongLongLongLongLongLongLongFunctionName();
    
    // złe
    const foo
      = 'superLongLongLongLongLongLongLongLongString';
    
    // dobre
    const foo = (
      superLongLongLongLongLongLongLongLongFunctionName()
    );
    
    // dobre
    const foo = 'superLongLongLongLongLongLongLongLongString';

  • 13.8 Nie zezwalaj na nieużywane zmienne. eslint: no-unused-vars

    Czemu? Zmienne, które są zadeklarowane i nieużywane nigdzie w kodzie, najprawdopodobniej są błędem z powodu niepełnej refaktoryzacji. Takie zmienne zajmują miejsce w kodzie i mogą prowadzić do zamieszania u czytelników.

    // złe
    
    var some_unused_var = 42;
    
    // Zmienne tylko do zapisu nie są uważane za używane.
    var y = 10;
    y = 5;
    
    // Odczyt samej modyfikacji nie jest uważany za wykorzystany.
    var z = 0;
    z = z + 1;
    
    // Nieużywane argumenty funkcji.
    function getX(x, y) {
        return x;
    }
    
    // dobre
    
    function getXPlusY(x, y) {
      return x + y;
    }
    
    var x = 1;
    var y = a + 2;
    
    alert(getXPlusY(x, y));
    
    // 'type' jest ignorowany, nawet jeśli nie jest używany, ponieważ ma rodzeństwo właściwości rest.
    // Jest to forma wyodrębnienia obiektu, który pomija określone klucze.
    var { type, ...coords } = data;
    // 'coords' jest teraz obiektem 'data' bez swojej właściwości 'type'.

⬆ powrót do góry

Hoisting

  • 14.1 var declarations get hoisted to the top of their closest enclosing function scope, their assignment does not. const and let declarations are blessed with a new concept called Temporal Dead Zones (TDZ). It’s important to know why typeof is no longer safe.

    // wiemy, że to nie zadziałałoby (zakładając, że
    // nie jest zmienną globalną notDefined)
    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;
    }
    
    // używanie const oraz let
    function example() {
      console.log(declaredButNotAssigned); // => throws a ReferenceError
      console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
      const declaredButNotAssigned = true;
    }

  • 14.2 Anonimowe wyrażenia funkcyjne hoistują swoją nazwę zmiennej, ale nie przypisanie funkcji.

    function example() {
      console.log(anonymous); // => undefined
    
      anonymous(); // => TypeError anonymous is not a function
    
      var anonymous = function () {
        console.log('anonymous function expression');
      };
    }

  • 14.3 Nazwane wyrażenia funkcyjne hoistują nazwę zmiennej, a nie nazwę funkcji lub body funkcji.

    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');
      };
    }
    
    // to samo dotyczy nazwy funkcji
    // jest taka sama jak nazwa zmiennej.
    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
    
      var named = function named() {
        console.log('named');
      };
    }

  • 14.4 Function declarations hoist their name and the function body.

    function example() {
      superPower(); // => Flying
    
      function superPower() {
        console.log('Flying');
      }
    }
  • Aby uzyskać więcej informacji, zobacz JavaScript Scoping & Hoisting od Ben Cherry.

⬆ powrót do góry

Operatory porównania i równości

  • 15.1 Użyj === i !== zamiast == oraz !=. eslint: eqeqeq

  • 15.2 Instrukcje warunkowe, takie jak instrukcja if evaluate their expression using coercion with the ToBoolean abstract method and always follow these simple rules:

    • Objects określa na true
    • Undefined określa na false
    • Null określa na false
    • Booleans określa na the value of the boolean
    • Numbers określa na false jeśli +0, -0, or NaN, w innym przypadku true
    • Strings określa na false jeśli pusty string '', w innym przypadku true
    if ([0] && []) {
      // true
      // tablica (nawet pusta) jest obiektem, obiekty będą oceniać jako prawdziwe
    }

  • 15.3 Używaj skrótów dla logicznych wartości, ale wyraźne dla porównania stringów i liczb.

    // złe
    if (isValid === true) {
      // ...
    }
    
    // dobre
    if (isValid) {
      // ...
    }
    
    // złe
    if (name) {
      // ...
    }
    
    // dobre
    if (name !== '') {
      // ...
    }
    
    // złe
    if (collection.length) {
      // ...
    }
    
    // dobre
    if (collection.length > 0) {
      // ...
    }

  • 15.5 Użyj nawiasów klamrowych, aby utworzyć bloki w klauzulach case oraz default zawierające deklaracje leksykalne (np. let, const, function, czy class). eslint: no-case-declarations

    Czemu? Deklaracje leksykalne są widoczne w całym bloku switch, ale są inicjalizowane tylko wtedy, gdy są przypisane, co dzieje się tylko po osiągnięciu jego case. Powoduje to problemy, gdy wiele klauzul case próbuje zdefiniować to samo.

    // złe
    switch (foo) {
      case 1:
        let x = 1;
        break;
      case 2:
        const y = 2;
        break;
      case 3:
        function f() {
          // ...
        }
        break;
      default:
        class C {}
    }
    
    // dobre
    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.6 Ternary nie powinny być zagnieżdżone i zazwyczaj są wyrażeniami jednowierszowymi. eslint: no-nested-ternary

    // złe
    const foo = maybe1 > maybe2
      ? "bar"
      : value1 > value2 ? "baz" : null;
    
    // podzielone na 2 oddzielne wyrażenia trójskładnikowe
    const maybeNull = value1 > value2 ? 'baz' : null;
    
    // lepiej
    const foo = maybe1 > maybe2
      ? 'bar'
      : maybeNull;
    
    // najlepiej
    const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

  • 15.7 Unikaj niepotrzebnych wyrażeń potrójnych. eslint: no-unneeded-ternary

    // złe
    const foo = a ? a : b;
    const bar = c ? true : false;
    const baz = c ? false : true;
    
    // dobre
    const foo = a || b;
    const bar = !!c;
    const baz = !c;

  • 15.8 Mieszając operatory, umieść je w nawiasach. Jedynym wyjątkiem są standardowe operatory arytmetyczne: +, -, i ** ponieważ ich pierwszeństwo jest szeroko rozumiane. Zalecamy otoczyć / i * w w nawiasach, ponieważ ich pierwszeństwo może być dwuznaczne, gdy są mieszane. eslint: no-mixed-operators

    Czemu? Poprawia to czytelność i wyjaśnia zamiar programisty.

    // złe
    const foo = a && b < 0 || c > 0 || d + 1 === 0;
    
    // złe
    const bar = a ** b - 5 % d;
    
    // złe
    // one may be confused into thinking (a || b) && c
    if (a || b && c) {
      return d;
    }
    
    // złe
    const bar = a + b / c * d;
    
    // dobre
    const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
    
    // dobre
    const bar = a ** b - (5 % d);
    
    // dobre
    if (a || (b && c)) {
      return d;
    }
    
    // dobre
    const bar = a + (b / c) * d;

⬆ powrót do góry

Bloki

  • 16.1 Używaj nawiasów klamrowych ze wszystkimi wielowierszowymi blokami. eslint: nonblock-statement-body-position

    // złe
    if (test)
      return false;
    
    // dobre
    if (test) return false;
    
    // dobre
    if (test) {
      return false;
    }
    
    // złe
    function foo() { return false; }
    
    // dobre
    function bar() {
      return false;
    }

  • 16.2 Jeśli używasz wielowierszowe bloki z if i else, umieść else w tej samej linii co nawias zamykający bloku if. eslint: brace-style

    // złe
    if (test) {
      thing1();
      thing2();
    }
    else {
      thing3();
    }
    
    // dobre
    if (test) {
      thing1();
      thing2();
    } else {
      thing3();
    }

  • 16.3 Jeśli blok if zawsze wykonuje instrukcję return, kolejny blok else jest niepotrzebny. return w bloku else if po bloku if, który zawiera return, można podzielić na wiele bloków if. eslint: no-else-return

    // złe
    function foo() {
      if (x) {
        return x;
      } else {
        return y;
      }
    }
    
    // złe
    function cats() {
      if (x) {
        return x;
      } else if (y) {
        return y;
      }
    }
    
    // złe
    function dogs() {
      if (x) {
        return x;
      } else {
        if (y) {
          return y;
        }
      }
    }
    
    // dobre
    function foo() {
      if (x) {
        return x;
      }
    
      return y;
    }
    
    // dobre
    function cats() {
      if (x) {
        return x;
      }
    
      if (y) {
        return y;
      }
    }
    
    // dobre
    function dogs(x) {
      if (x) {
        if (z) {
          return y;
        }
      } else {
        return z;
      }
    }

⬆ powrót do góry

Control Statements

  • 17.1 In case your control statement (if, while etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line.

    Why? Requiring operators at the beginning of the line keeps the operators aligned and follows a pattern similar to method chaining. This also improves readability by making it easier to visually follow complex logic.

    // złe
    if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
      thing1();
    }
    
    // złe
    if (foo === 123 &&
      bar === 'abc') {
      thing1();
    }
    
    // złe
    if (foo === 123
      && bar === 'abc') {
      thing1();
    }
    
    // złe
    if (
      foo === 123 &&
      bar === 'abc'
    ) {
      thing1();
    }
    
    // dobre
    if (
      foo === 123
      && bar === 'abc'
    ) {
      thing1();
    }
    
    // dobre
    if (
      (foo === 123 || bar === 'abc')
      && doesItLookGoodWhenItBecomesThatLong()
      && isThisReallyHappening()
    ) {
      thing1();
    }
    
    // dobre
    if (foo === 123 && bar === 'abc') {
      thing1();
    }

  • 17.2 Nie używaj operatorów wyboru zamiast instrukcji sterujących.

    // złe
    !isRunning && startRunning();
    
    // dobre
    if (!isRunning) {
      startRunning();
    }

⬆ powrót do góry

Komentarze

  • 18.1 Użyj /** ... */ dla wielowierszowych komentarzy.

    // złe
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // dobre
    /**
     * make() returns a new element
     * based on the passed-in tag name
     */
    function make(tag) {
    
      // ...
    
      return element;
    }

  • 18.2 Użyj // dla komentarzy w pojedynczej linii. Umieść komentarze w jednym wierszu w nowym wierszu nad tematem komentarza. Umieść pusty wiersz przed komentarzem, chyba że znajduje się on w pierwszym wierszu bloku.

    // złe
    const active = true;  // is current tab
    
    // dobre
    // is current tab
    const active = true;
    
    // złe
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      const type = this.type || 'no type';
    
      return type;
    }
    
    // dobre
    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 Rozpocznij wszystkie komentarze spacją, aby ułatwić czytanie. eslint: spaced-comment

    // złe
    // is current tab
    const active = true;
    
    // dobre
    // is current tab
    const active = true;
    
    // złe
    /**
     *make() returns a new element
     *based on the passed-in tag name
     */
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // dobre
    /**
     * make() returns a new element
     * based on the passed-in tag name
     */
    function make(tag) {
    
      // ...
    
      return element;
    }

  • 18.4 Poprzedź swoje komentarze za pomocą FIXME lub TODO co pomaga innym programistom w szybkim zrozumieniu, czy wskazujesz problem, który należy ponownie zgłosić, lub sugerujesz rozwiązanie problemu, który należy wdrożyć. Różnią się one od zwykłych komentarzy, ponieważ można je wykonać. Działania są FIXME: -- need to figure this out lub TODO: -- need to implement.

  • 18.5 Użyj // FIXME: aby opisywać problemy.

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // FIXME: shouldn’t use a global here
        total = 0;
      }
    }

  • 18.6 Użyj // TODO: aby opisywać rozwiązania problemów.

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // TODO: total should be configurable by an options param
        this.total = 0;
      }
    }

⬆ powrót do góry

Białe znaki

  • 19.1 Użyj miękkich tabulatorów (spacji) ustawionych na 2 spacje. eslint: indent

    // złe
    function foo() {
    ∙∙∙∙let name;
    }
    
    // złe
    function bar() {
    ∙let name;
    }
    
    // dobre
    function baz() {
    ∙∙let name;
    }

  • 19.2 Umieść 1 spację przed nawiasem rozpoczynającym. eslint: space-before-blocks

    // złe
    function test(){
      console.log('test');
    }
    
    // dobre
    function test() {
      console.log('test');
    }
    
    // złe
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog',
    });
    
    // dobre
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog',
    });

  • 19.3 Umieść 1 spację przed nawiasem otwierającym w instrukcjach sterujących (if, while etc.). Nie umieszczaj spacji między listą argumentów a nazwą funkcji w wywołaniach funkcji i deklaracjach. eslint: keyword-spacing

    // złe
    if(isJedi) {
      fight ();
    }
    
    // dobre
    if (isJedi) {
      fight();
    }
    
    // złe
    function fight () {
      console.log ('Swooosh!');
    }
    
    // dobre
    function fight() {
      console.log('Swooosh!');
    }

  • 19.4 Set off operators with spaces. eslint: space-infix-ops

    // złe
    const x=y+5;
    
    // dobre
    const x = y + 5;

  • 19.5 Zakończ pliki pojedynczym znakiem nowej linii. eslint: eol-last

    // złe
    import { es6 } from './AirbnbStyleGuide';
      // ...
    export default es6;
    // złe
    import { es6 } from './AirbnbStyleGuide';
      // ...
    export default es6;
    
    // dobre
    import { es6 } from './AirbnbStyleGuide';
      // ...
    export default es6;

  • 19.6 Use indentation when making long method chains (more than 2 method chains). Use a leading dot, which emphasizes that the line is a method call, not a new statement. eslint: newline-per-chained-call no-whitespace-before-property

    // złe
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // złe
    $('#items').
      find('.selected').
        highlight().
        end().
      find('.open').
        updateCount();
    
    // dobre
    $('#items')
      .find('.selected')
        .highlight()
        .end()
      .find('.open')
        .updateCount();
    
    // złe
    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);
    
    // dobre
    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);
    
    // dobre
    const leds = stage.selectAll('.led').data(data);

  • 19.7 Pozostaw puste wiersze po blokach i przed następną instrukcją.

    // złe
    if (foo) {
      return bar;
    }
    return baz;
    
    // dobre
    if (foo) {
      return bar;
    }
    
    return baz;
    
    // złe
    const obj = {
      foo() {
      },
      bar() {
      },
    };
    return obj;
    
    // dobre
    const obj = {
      foo() {
      },
    
      bar() {
      },
    };
    
    return obj;
    
    // złe
    const arr = [
      function foo() {
      },
      function bar() {
      },
    ];
    return arr;
    
    // dobre
    const arr = [
      function foo() {
      },
    
      function bar() {
      },
    ];
    
    return arr;

  • 19.8 Nie wypełniaj bloków pustymi liniami. eslint: padded-blocks

    // złe
    function bar() {
    
      console.log(foo);
    
    }
    
    // złe
    if (baz) {
    
      console.log(qux);
    } else {
      console.log(foo);
    
    }
    
    // złe
    class Foo {
    
      constructor(bar) {
        this.bar = bar;
      }
    }
    
    // dobre
    function bar() {
      console.log(foo);
    }
    
    // dobre
    if (baz) {
      console.log(qux);
    } else {
      console.log(foo);
    }

  • 19.9 Nie używaj wielu pustych linii do uzupełnienia kodu. eslint: no-multiple-empty-lines

    // złe
    class Person {
      constructor(fullName, email, birthday) {
        this.fullName = fullName;
    
    
        this.email = email;
    
    
        this.setAge(birthday);
      }
    
    
      setAge(birthday) {
        const today = new Date();
    
    
        const age = this.getAge(today, birthday);
    
    
        this.age = age;
      }
    
    
      getAge(today, birthday) {
        // ..
      }
    }
    
    // dobre
    class Person {
      constructor(fullName, email, birthday) {
        this.fullName = fullName;
        this.email = email;
        this.setAge(birthday);
      }
    
      setAge(birthday) {
        const today = new Date();
        const age = getAge(today, birthday);
        this.age = age;
      }
    
      getAge(today, birthday) {
        // ..
      }
    }

  • 19.10 Nie dodawaj spacji w nawiasach. eslint: space-in-parens

    // złe
    function bar( foo ) {
      return foo;
    }
    
    // dobre
    function bar(foo) {
      return foo;
    }
    
    // złe
    if ( foo ) {
      console.log(foo);
    }
    
    // dobre
    if (foo) {
      console.log(foo);
    }

  • 19.11 Nie dodawaj spacji w nawiasach. eslint: array-bracket-spacing

    // złe
    const foo = [ 1, 2, 3 ];
    console.log(foo[ 0 ]);
    
    // dobre
    const foo = [1, 2, 3];
    console.log(foo[0]);

  • 19.12 Dodaj spacje w nawiasach klamrowych. eslint: object-curly-spacing

    // złe
    const foo = {clark: 'kent'};
    
    // dobre
    const foo = { clark: 'kent' };

  • 19.13 Unikaj linii kodu dłuższych niż 100 znaków (w tym białych znaków). Uwaga: na powyżej, długie stringi są zwolnione z tej zasady i nie powinny być dzielone. eslint: max-len

    Czemu? Zapewnia to czytelność i łatwość konserwacji.

    // złe
    const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
    
    // złe
    $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
    
    // dobre
    const foo = jsonData
      && jsonData.foo
      && jsonData.foo.bar
      && jsonData.foo.bar.baz
      && jsonData.foo.bar.baz.quux
      && jsonData.foo.bar.baz.quux.xyzzy;
    
    // dobre
    $.ajax({
      method: 'POST',
      url: 'https://airbnb.com/',
      data: { name: 'John' },
    })
      .done(() => console.log('Congratulations!'))
      .fail(() => console.log('You have failed this city.'));

  • 19.14 Require consistent spacing inside an open block token and the next token on the same line. This rule also enforces consistent spacing inside a close block token and previous token on the same line. eslint: block-spacing

    // złe
    function foo() {return true;}
    if (foo) { bar = 0;}
    
    // dobre
    function foo() { return true; }
    if (foo) { bar = 0; }

  • 19.15 Unikaj spacji przed przecinkami i wymagaj spacji po przecinkach. eslint: comma-spacing

    // złe
    var foo = 1,bar = 2;
    var arr = [1 , 2];
    
    // dobre
    var foo = 1, bar = 2;
    var arr = [1, 2];

  • 19.16 Enforce spacing inside of computed property brackets. eslint: computed-property-spacing

    // złe
    obj[foo ]
    obj[ 'foo']
    var x = {[ b ]: a}
    obj[foo[ bar ]]
    
    // dobre
    obj[foo]
    obj['foo']
    var x = { [b]: a }
    obj[foo[bar]]

  • 19.17 Unikaj spacji między funkcjami i ich wywołaniami. eslint: func-call-spacing

    // złe
    func ();
    
    func
    ();
    
    // dobre
    func();

  • 19.18Wymuszaj odstępy między kluczami a wartościami we właściwościach literału obiektu. eslint: key-spacing

    // złe
    var obj = { foo : 42 };
    var obj2 = { foo:42 };
    
    // dobre
    var obj = { foo: 42 };

  • 19.20 Unikaj wielu pustych linii, zezwalaj tylko na jedną nową linię na końcu plików i unikaj nowej linii na początku plików. eslint: no-multiple-empty-lines

    // złe - wiele pustych linii
    var x = 1;
    
    
    var y = 2;
    
    // złe - 2+ nowe linie na końcu pliku
    var x = 1;
    var y = 2;
    
    
    // złe - 1+ nowa linia(e) na początku pliku
    
    var x = 1;
    var y = 2;
    
    // dobre
    var x = 1;
    var y = 2;

⬆ powrót do góry

Przecinki

  • 20.1 Leading commas: Nope. eslint: comma-style

    // złe
    const story = [
        once
      , upon
      , aTime
    ];
    
    // dobre
    const story = [
      once,
      upon,
      aTime,
    ];
    
    // złe
    const hero = {
        firstName: 'Ada'
      , lastName: 'Lovelace'
      , birthYear: 1815
      , superPower: 'computers'
    };
    
    // dobre
    const hero = {
      firstName: 'Ada',
      lastName: 'Lovelace',
      birthYear: 1815,
      superPower: 'computers',
    };

  • 20.2 Additional trailing comma: Yup. eslint: comma-dangle

    Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the trailing comma problem in legacy browsers.

    // złe - git diff without trailing comma
    const hero = {
         firstName: 'Florence',
    -    lastName: 'Nightingale'
    +    lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing']
    };
    
    // dobre - git diff with trailing comma
    const hero = {
         firstName: 'Florence',
         lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing'],
    };
    // złe
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully'
    };
    
    const heroes = [
      'Batman',
      'Superman'
    ];
    
    // dobre
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully',
    };
    
    const heroes = [
      'Batman',
      'Superman',
    ];
    
    // złe
    function createHero(
      firstName,
      lastName,
      inventorOf
    ) {
      // does nothing
    }
    
    // dobre
    function createHero(
      firstName,
      lastName,
      inventorOf,
    ) {
      // does nothing
    }
    
    // dobre (note that a comma must not appear after a "rest" element)
    function createHero(
      firstName,
      lastName,
      inventorOf,
      ...heroArgs
    ) {
      // does nothing
    }
    
    // złe
    createHero(
      firstName,
      lastName,
      inventorOf
    );
    
    // dobre
    createHero(
      firstName,
      lastName,
      inventorOf,
    );
    
    // dobre (note that a comma must not appear after a "rest" element)
    createHero(
      firstName,
      lastName,
      inventorOf,
      ...heroArgs
    );

⬆ powrót do góry

Średniki

  • 21.1 Yup. eslint: semi

    Czemu? Gdy JavaScript napotyka podział wiersza bez średnika, używa zestawu reguł o nazwie Automatic Semicolon Insertion aby ustalić, czy powinien on traktować podział wiersza jako koniec instrukcji i (jak sama nazwa wskazuje) umieścić średnik w kodzie przed podziałem wiersza, jeśli tak uważa. ASI zawiera jednak kilka ekscentrycznych zachowań, a twój kod się zepsuje, jeśli JavaScript źle interpretuje podział linii. Reguły te staną się bardziej skomplikowane, gdy nowe funkcje staną się częścią JavaScript. Jawne zakończenie instrukcji i konfiguracja linijki w celu wychwycenia brakujących średników pomoże ci uniknąć problemów.

    // złe - raises exception
    const luke = {}
    const leia = {}
    [luke, leia].forEach((jedi) => jedi.father = 'vader')
    
    // złe - raises exception
    const reaction = "No! That’s impossible!"
    (async function meanwhileOnTheFalcon() {
      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
      // ...
    }())
    
    // złe - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
    function foo() {
      return
        'search your feelings, you know it to be foo'
    }
    
    // dobre
    const luke = {};
    const leia = {};
    [luke, leia].forEach((jedi) => {
      jedi.father = 'vader';
    });
    
    // dobre
    const reaction = "No! That’s impossible!";
    (async function meanwhileOnTheFalcon() {
      // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
      // ...
    }());
    
    // dobre
    function foo() {
      return 'search your feelings, you know it to be foo';
    }

    Read more.

⬆ powrót do góry

Type Casting & Coercion

  • 22.1 Perform type coercion at the beginning of the statement.

  • 22.2 Strings: eslint: no-new-wrappers

    // => this.reviewScore = 9;
    
    // złe
    const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
    
    // złe
    const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
    
    // złe
    const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string
    
    // dobre
    const totalScore = String(this.reviewScore);

  • 22.3 Numbers: Use Number for type casting and parseInt always with a radix for parsing strings. eslint: radix no-new-wrappers

    const inputValue = '4';
    
    // złe
    const val = new Number(inputValue);
    
    // złe
    const val = +inputValue;
    
    // złe
    const val = inputValue >> 0;
    
    // złe
    const val = parseInt(inputValue);
    
    // dobre
    const val = Number(inputValue);
    
    // dobre
    const val = parseInt(inputValue, 10);

  • 22.4 If for whatever reason you are doing something wild and parseInt is your bottleneck and need to use Bitshift for performance reasons, leave a comment explaining why and what you’re doing.

    // dobre
    /**
     * 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 Note: Be careful when using bitshift operations. Numbers are represented as 64-bit values, but bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Discussion. Largest signed 32-bit Int is 2,147,483,647:

    2147483647 >> 0; // => 2147483647
    2147483648 >> 0; // => -2147483648
    2147483649 >> 0; // => -2147483647

  • 22.6 Booleans: eslint: no-new-wrappers

    const age = 0;
    
    // złe
    const hasAge = new Boolean(age);
    
    // dobre
    const hasAge = Boolean(age);
    
    // best
    const hasAge = !!age;

⬆ powrót do góry

Konwencje nazewnictwa

  • 23.1 Unikaj nazw jednoliterowych. Podaj swoje nazwy w sposób opisowy. eslint: id-length

    // złe
    function q() {
      // ...
    }
    
    // dobre
    function query() {
      // ...
    }

  • 23.2 Użyj camelCase podczas nadawania nazw dla obiektów, funkcji, i instancji. eslint: camelcase

    // złe
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // dobre
    const thisIsMyObject = {};
    function thisIsMyFunction() {}

  • 23.3 Użyj PascalCase tylko podczas nazywania konstruktorów lub klas. eslint: new-cap

    // złe
    function user(options) {
      this.name = options.name;
    }
    
    const bad = new user({
      name: 'nope',
    });
    
    // dobre
    class User {
      constructor(options) {
        this.name = options.name;
      }
    }
    
    const good = new User({
      name: 'yup',
    });

  • 23.4 Do not use trailing or leading underscores. eslint: no-underscore-dangle

    Why? JavaScript does not have the concept of privacy in terms of properties or methods. Although a leading underscore is a common convention to mean “private”, in fact, these properties are fully public, and as such, are part of your public API contract. This convention might lead developers to wrongly think that a change won’t count as breaking, or that tests aren’t needed. tl;dr: if you want something to be “private”, it must not be observably present.

    // złe
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // dobre
    this.firstName = 'Panda';
    
    // dobre, in environments where WeakMaps are available
    // see https://kangax.github.io/compat-table/es6/#test-WeakMap
    const firstNames = new WeakMap();
    firstNames.set(this, 'Panda');

  • 23.5 Don’t save references to this. Use arrow functions or Function#bind.

    // złe
    function foo() {
      const self = this;
      return function () {
        console.log(self);
      };
    }
    
    // złe
    function foo() {
      const that = this;
      return function () {
        console.log(that);
      };
    }
    
    // dobre
    function foo() {
      return () => {
        console.log(this);
      };
    }

  • 23.6 A base filename should exactly match the name of its default export.

    // file 1 contents
    class CheckBox {
      // ...
    }
    export default CheckBox;
    
    // file 2 contents
    export default function fortyTwo() { return 42; }
    
    // file 3 contents
    export default function insideDirectory() {}
    
    // in some other file
    // złe
    import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename
    import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export
    import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export
    
    // złe
    import CheckBox from './check_box'; // PascalCase import/export, snake_case filename
    import forty_two from './forty_two'; // snake_case import/filename, camelCase export
    import inside_directory from './inside_directory'; // snake_case import, camelCase export
    import index from './inside_directory/index'; // requiring the index file explicitly
    import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly
    
    // dobre
    import CheckBox from './CheckBox'; // PascalCase export/import/filename
    import fortyTwo from './fortyTwo'; // camelCase export/import/filename
    import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index"
    // ^ supports both insideDirectory.js and insideDirectory/index.js

  • 23.7 Use camelCase when you export-default a function. Your filename should be identical to your function’s name.

    function makeStyleGuide() {
      // ...
    }
    
    export default makeStyleGuide;

  • 23.8 Use PascalCase when you export a constructor / class / singleton / function library / bare object.

    const AirbnbStyleGuide = {
      es6: {
      },
    };
    
    export default AirbnbStyleGuide;

  • 23.9 Acronyms and initialisms should always be all uppercased, or all lowercased.

    Why? Names are for readability, not to appease a computer algorithm.

    // złe
    import SmsContainer from './containers/SmsContainer';
    
    // złe
    const HttpRequests = [
      // ...
    ];
    
    // dobre
    import SMSContainer from './containers/SMSContainer';
    
    // dobre
    const HTTPRequests = [
      // ...
    ];
    
    // also good
    const httpRequests = [
      // ...
    ];
    
    // best
    import TextMessageContainer from './containers/TextMessageContainer';
    
    // best
    const requests = [
      // ...
    ];

  • 23.10 You may optionally uppercase a constant only if it (1) is exported, (2) is a const (it can not be reassigned), and (3) the programmer can trust it (and its nested properties) to never change.

    Why? This is an additional tool to assist in situations where the programmer would be unsure if a variable might ever change. UPPERCASE_VARIABLES are letting the programmer know that they can trust the variable (and its properties) not to change.

    • What about all const variables? - This is unnecessary, so uppercasing should not be used for constants within a file. It should be used for exported constants however.
    • What about exported objects? - Uppercase at the top level of export (e.g. EXPORTED_OBJECT.key) and maintain that all nested properties do not change.
    // złe
    const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file';
    
    // złe
    export const THING_TO_BE_CHANGED = 'should obviously not be uppercased';
    
    // złe
    export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables';
    
    // ---
    
    // allowed but does not supply semantic value
    export const apiKey = 'SOMEKEY';
    
    // better in most cases
    export const API_KEY = 'SOMEKEY';
    
    // ---
    
    // złe - unnecessarily uppercases key while adding no semantic value
    export const MAPPING = {
      KEY: 'value'
    };
    
    // dobre
    export const MAPPING = {
      key: 'value'
    };

⬆ powrót do góry

Akcesory

  • 24.1 Accessor functions for properties are not required.

  • 24.2 Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use getVal() and setVal('hello').

    // złe
    class Dragon {
      get age() {
        // ...
      }
    
      set age(value) {
        // ...
      }
    }
    
    // dobre
    class Dragon {
      getAge() {
        // ...
      }
    
      setAge(value) {
        // ...
      }
    }

  • 24.3 If the property/method is a boolean, use isVal() or hasVal().

    // złe
    if (!dragon.age()) {
      return false;
    }
    
    // dobre
    if (!dragon.hasAge()) {
      return false;
    }

  • 24.4 It’s okay to create get() and set() functions, but be consistent.

    class Jedi {
      constructor(options = {}) {
        const lightsaber = options.lightsaber || 'blue';
        this.set('lightsaber', lightsaber);
      }
    
      set(key, val) {
        this[key] = val;
      }
    
      get(key) {
        return this[key];
      }
    }

⬆ powrót do góry

Zdarzenia

  • 25.1 When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass an object literal (also known as a "hash") instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of:

    // złe
    $(this).trigger('listingUpdated', listing.id);
    
    // ...
    
    $(this).on('listingUpdated', (e, listingID) => {
      // do something with listingID
    });

    prefer:

    // dobre
    $(this).trigger('listingUpdated', { listingID: listing.id });
    
    // ...
    
    $(this).on('listingUpdated', (e, data) => {
      // do something with data.listingID
    });

⬆ powrót do góry

jQuery

  • 26.1 Prefix jQuery object variables with a $.

    // złe
    const sidebar = $('.sidebar');
    
    // dobre
    const $sidebar = $('.sidebar');
    
    // dobre
    const $sidebarBtn = $('.sidebar-btn');

  • 26.2 Cache jQuery lookups.

    // złe
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...
    
      $('.sidebar').css({
        'background-color': 'pink',
      });
    }
    
    // dobre
    function setSidebar() {
      const $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...
    
      $sidebar.css({
        'background-color': 'pink',
      });
    }

  • 26.3 For DOM queries use Cascading $('.sidebar ul') or parent > child $('.sidebar > ul'). jsPerf

  • 26.4 Use find with scoped jQuery object queries.

    // złe
    $('ul', '.sidebar').hide();
    
    // złe
    $('.sidebar').find('ul').hide();
    
    // dobre
    $('.sidebar ul').hide();
    
    // dobre
    $('.sidebar > ul').hide();
    
    // dobre
    $sidebar.find('ul').hide();

⬆ powrót do góry

ECMAScript 5 Compatibility

⬆ powrót do góry

ECMAScript 6+ (ES 2015+) Styles

  • 28.1 This is a collection of links to the various ES6+ features.
  1. Funkcje strzałkowe
  2. Klasy
  3. Object Shorthand
  4. Object Concise
  5. Object Computed Properties
  6. Template Strings
  7. Destrukturyzacja
  8. Default Parameters
  9. Rest
  10. Array Spreads
  11. Let and Const
  12. Exponentiation Operator
  13. Iteratory i generatory
  14. Moduły

  • 28.2 Do not use TC39 proposals that have not reached stage 3.

    Why? They are not finalized, and they are subject to change or to be withdrawn entirely. We want to use JavaScript, and proposals are not JavaScript yet.

⬆ powrót do góry

Standard Library

The Standard Library contains utilities that are functionally broken but remain for legacy reasons.

  • 29.1 Use Number.isNaN instead of global isNaN. eslint: no-restricted-globals

    Why? The global isNaN coerces non-numbers to numbers, returning true for anything that coerces to NaN. If this behavior is desired, make it explicit.

    // złe
    isNaN('1.2'); // false
    isNaN('1.2.3'); // true
    
    // dobre
    Number.isNaN('1.2.3'); // false
    Number.isNaN(Number('1.2.3')); // true

  • 29.2 Use Number.isFinite instead of global isFinite. eslint: no-restricted-globals

    Why? The global isFinite coerces non-numbers to numbers, returning true for anything that coerces to a finite number. If this behavior is desired, make it explicit.

    // złe
    isFinite('2e3'); // true
    
    // dobre
    Number.isFinite('2e3'); // false
    Number.isFinite(parseInt('2e3', 10)); // true

⬆ powrót do góry

Testowanie

  • 30.1 Yup.

    function foo() {
      return true;
    }

  • 30.2 No, but seriously:
    • Whichever testing framework you use, you should be writing tests!
    • Strive to write many small pure functions, and minimize where mutations occur.
    • Be cautious about stubs and mocks - they can make your tests more brittle.
    • We primarily use mocha and jest at Airbnb. tape is also used occasionally for small, separate modules.
    • 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it.
    • Whenever you fix a bug, write a regression test. A bug fixed without a regression test is almost certainly going to break again in the future.

⬆ powrót do góry

Wydajność

⬆ powrót do góry

Materiały

Nauka ES6+

Przeczytaj

Narzędzia

Inne przewodniki po stylach

Inne style

Further Reading

Książki

Blogi

Podcasty

⬆ powrót do góry

In the Wild

To jest lista organizacji korzystających z tego przewodnika stylu. Stwórz PR, a my dodamy cię do listy.

⬆ powrót do góry

Tłumaczenie

Ten przewodnik po stylach jest również dostępny w innych językach:

The JavaScript Style Guide Guide

Chatuj z nami na temat JavaScript

Współtwórcy

Licencja

(The MIT License)

Copyright (c) 2012 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.

⬆ powrót do góry

Poprawki

Zachęcamy do forka tego przewodnika i zmiany zasad w celu dopasowania do przewodnika po stylu dla twojego zespołu. Poniżej możesz wymienić kilka poprawek do przewodnika po stylach. Umożliwia to okresową aktualizację przewodnika po stylu bez konieczności rozwiązywania konfliktów mergowania.

};

About

JavaScript Style Guide

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%