Skip to content

lequanvn89/javascriptstyleguide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 

Repository files navigation

JavaScript style guide (ES6)

Пишем JavaScript красиво.

За основу взята статья "Airbnb - JavaScript style guide". Убрано лишнее, нужные моменты переосмысленны и переведены, добавлено некоторые мои видения.

Оглавление

  1. Переменные
  2. Объекты
  3. Массивы
  4. Деструктурирование
  5. Строки
  6. Функции
  7. Стрелочные функции
  8. Импорты
  9. Операторы равенства и идентичности
  10. Блоки кода
  11. Комментарии
  12. Табы и пробелы
  13. Запятые
  14. Точки с запятой
  15. Соглашение об именовании

Переменные

  • 1.1 В ES6 не принято использовать var. Используйте const если не хотите чтобы значение переменной или ссылка на значение менялось. Во всех остальных случаях используйте let.

    eslint: prefer-const, no-const-assign no-var jscs: disallowVar

    Используя const можно предотвратить перезаписи простых типов данных (string, number, boolean, null, undefined) или перезаписи ссылки на сложные типы данных (array, object, function). Улучшает понимание кода другими программистами, какие переменные можно менять, а какие нет.

    Отказываясь от var получаем везде переменные с блочной областью видимости.

    // bad
    var a = 1;
    var b = 2;
    var count = 0;
    
    if (true) {
        count += 1;
    }
    
    // good
    const a = 1;
    const b = 2;
    let count = 0;
    
    if (true) {
        count += 1;
    }
  • 1.2 Блочная область видимости const и let.

    // const and let only exist in the blocks they are defined in.
    {
        let a = 1;
        const b = 1;
    }
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError
  • 1.3 Всегда используйте const или let для объявления переменных. Иначе переменная будет объявлена глобально, а это не есть хорошо.

    // bad
    superPower = new SuperPower();
    
    // good
    const superPower = new SuperPower();

⬆ к оглавлению

Объекты

  • 2.1 Для создания объекта используйте литерал объекта {}. Так проще и читабельней.

    eslint: no-new-object

    // bad
    const item = new Object();
    
    // good
    const item = {};
  • 2.2 Используйте сокращенный синтаксис для определения методов объекта.

    eslint: object-shorthand jscs: requireEnhancedObjectLiterals

    // bad
    const item = {
        value: 1,
        addValue: function (value) {
            return this.value + value;
        },
    };
    
    // good
    const item = {
        value: 1,
        addValue(value) {
            return this.value + value;
        },
    };
  • 2.3 Используйте сокращенный синтаксис для значений свойств.

    eslint: object-shorthand jscs: requireEnhancedObjectLiterals

    const name = 'Awesome item';
    
    // bad
    const item = {
        name: name,
    };
    
    // good
    const item = {
        name,
    };

    Группируйте сокращенные свойства в начале объекта.

    const name = 'Awesome item';
    const link = 'https://www.abc.com';
    
    // bad
    const item = {
        index: 1,
        name,
        type: 'new',
        link
    };
    
    // good
    const item = {
        name,
        link,
        index: 1,
        type: 'new'
    };
  • 2.4 Имена свойств не нужно обрамлять в кавычки. Обрамляйте только синтактически неправильные имена (но такие имена лучше не использовать =) ).

    eslint: quote-props jscs: disallowQuotedKeysInObjects

    // bad
    const bad = {
        'foo': 3,
        'bar': 4,
        'foo-bar': 5,
    };
    
    // good
    const good = {
        foo: 3,
        bar: 4,
        'foo-bar': 5,
    };
  • 2.5 Используйте точечную нотацию для доступа к свойствам объекта.

    eslint: dot-notation jscs: requireDotNotation

    const item = {
        foo: true,
        bar: 28,
    };
    
    // bad
    const hasFoo = item['foo'];
    
    // good
    const hasFoo = item.foo;
  • 2.6 Используйте скобочную нотацию [], когда вы обращаетесь к свойству через имя хранимое в переменной.

    const item = {
        foo: true,
        bar: 28,
    };
    
    function getProp(prop) {
        return item[prop];
    }
    
    const hasFoo = getProp('foo');

⬆ к оглавлению

Массивы

  • 3.1 Для создания массива используйте литерал массива []. Так проще и читабельней.

    eslint: no-array-constructor

    // bad
    const items = new Array();
    
    // good
    const items = [];
  • 3.2 Используйте оператор расширения ... для копирование массивов.

    // bad
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i++) {
        itemsCopy[i] = items[i];
    }
    
    // good
    const itemsCopy = [...items];

Деструктурирование

  • 4.1 Используйте деструктуризацию объекта в параметрах функции.

    jscs: requireObjectDestructuring

    Это позволит обойтись без временных переменных.

    // bad
    function getFullName(user) {
        const firstName = user.firstName;
        const lastName = user.lastName;
    
        return `${firstName} ${lastName}`;
    }
    
    // good
    function getFullName({firstName, lastName}) {
        return `${firstName} ${lastName}`;
    }
  • 4.2 Используйте деструктуризацию массивов.

    jscs: requireArrayDestructuring

    const arr = [1, 2, 3, 4, 5, 6, 7];
    
    // bad
    const first = arr[0];      // 1
    const second = arr[1];     // 2
    const fourth = arr[3];     // 4
    const rest = arr.slice(4); // 5, 6, 7
    
    // good
    const [first, second, , fourth, ...rest] = arr;
    
    // bad
    function sum(arr) {
        let foo = arr[0];
        let bar = arr[1];
        let foobar = foo + bar;
        return foobar;
    }
    sum([1, 2]);
    
    // good
    function sum([foo, bar]) {
        let foobar = foo + bar;
        return foobar;
    }
    sum([1, 2]);
  • 4.3 Множество значений возвращайте в виде объекта, чтобы потом использовать деструктуризацию объекта. Это удобней чем возвращать массив.

    Со временем можно добавлять и возвращать новые переменные не боясь поломать места вызова и использования функции.

    // bad
    function processInput(input) {
        // calculating...
        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) {
        // calculating...
        return {left, right, top, bottom};
    }
    
    // the caller selects only the data they need
    const {left, right} = processInput(input);

⬆ к оглавлению

Строки

  • 5.1 Используйте одинарные кавычки '' для строк.

    eslint: quotes jscs: validateQuoteMarks

    В текстах внутри строки часто встречаются ", обрамляя строки в '' упрощаем себе жизнь.

    // bad
    const name = "James Bond. James.";
    const hello = "Say \"Hello world\"";
    
    // good
    const name = 'James. James Bond.';
    const hello = 'Say "Hello world"';
  • 5.2 Используйте конкатенацию для переноса длинных строк.

    Крутые IDE переносят длинные строки именно таким способом.

    // 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.';
  • 5.3 Используйте литералы шаблонов ``, вместо конкатенации, когда программно строите строки.

    eslint: prefer-template jscs: requireTemplateStrings

    Шаблоны более читаемы, имеют лаконичный синтаксис, могут быть многострочными и могут интерполироваться. Что нам еще нужно?

    // bad
    function sayHi(name) {
        return 'How are you, ' + name + '?';
    }
    
    // bad
    function sayHi(name) {
        return ['How are you, ', name, '?'].join();
    }
    
    // good
    function sayHi(name) {
        return `How are you, ${name}?`;
    }
    
    // bad
    const errorMessage = 'This is a super long error that was thrown because \n' +
        'of Batman. When you stop to think about how Batman had anything to do \n' +
        '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.`;

⬆ к оглавлению

Функции

  • 6.1 Старайтесь объявлять именованные функции вместо анонимных (функционального литерала).

    jscs: requireFunctionDeclarations

    Функциональный литерал создает анонимную функцию, ее сложнее идентифицировать в стеке вызовов в отличие от именованной функции. К тому же все тело именованной фунции поднимается при объявлении, а в случае функционального литерала, поднимается только ссылка на функцию. Следовательно именованную функцию можно использовать даже если в коде она идет ниже чем место вызова.

    // bad
    const foo = function () {
    };
    foo();
    
    // good
    function foo() {
    }
    foo();
    
    // still work
    foo();
    function foo() {
    }
  • 6.2 Пишите чистые функции — не мутируйте аргументы, используйте значения аргументов по умолчанию, с ES6 это стало возможно.

    // really bad
    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 || {};
        // ...
    }
    
    // still bad
    function handleThings(opts) {
        // if opts is undefined, then opts = {}
        if (opts === void 0) {
            opts = {};
        }
        // ...
    }
    
    // good
    function handleThings(opts = {}) {
        // ...
    }
  • 6.3 Значении по умолчанию ставьте в конце списка.

    // bad
    function handleThings(opts = {}, name) {
        // ...
    }
    
    // good
    function handleThings(name, opts = {}) {
        // ...
    }
  • 6.4 Только будьте осторожны с сайд-эффектами.

    var b = 1;
    // very bad
    function count(a = b++) {
        console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3
  • 6.5 Пробелы в вызовах функции.

    Единообразность это хорошо, не нужно добавлять или удалять пробел, когда будете добавлять или удалять имя функции.

    // bad
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // good
    const x = function () {};
    const y = function a() {};

⬆ к оглавлению

Стрелочные функции

  • 7.1 Когда нужно использовать функциональный литерал (например, передача анонимной функции как параметр другой функции), то используйте стрелочные функции.

    eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions

    Создается функция, которая будет выполняться в контексте текущего this, а это то, что нам нужно в большинстве случаях.

    Но если у вас очень сложная функция, то можете вынести эту логику и объявить отдельную функцию.

    // 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;
    });
  • 7.2 Разный синтаксис стрелочной функции.

    7.2.1 Однострочная функция — используется неявный return.

    Если у функции всего один аргумент, то можно опустить фигурные скобки. Но для единообразия рекомендуется всегда ставить фигурные скобки вокруг аргументов.

    // not recommended
    [1, 2, 3].map(number => `A string containing the ${number}.`);
    
    // one argument
    [1, 2, 3].map((number) => `A string containing the ${number}.`);
    
    // to return object include parentheses around it
    [1, 2, 3].map((number, index) => ({num: number, index: index}));
    
    // no argument
    [1, 2, 3].map(() => 'No argument');

    7.2.2 Если выражение не помещается в одну строку, то обрамляем его в круглые скобки.

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

    7.2.3 Многострочная функция — блок кода обрамляется в фигурные скобки, используется явный return.

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

⬆ к оглавлению

Импорты

  • 8.1 Сначала идут импорты стандартных библиотек, потом импорты модулей из проекта. Можно отделить эти импорты одной пустой строкой. После импортов отступаем две строки и пишем код.

    import React from 'react';
    import _ from 'lodash';
    import {gettext} from 'gettext';
    
    import {view} from './views/index';
    import {model} from './model';
    import {update} from './update';
    
    
    function initApp() {};

⬆ к оглавлению

Операторы равенства и идентичности

  • 9.1 Используйте === и !== вместо == и !=.

    eslint: eqeqeq

    При сравнивании неявно выполняется приведение типов переменных. Чтобы избежать этой путаницы всегда используйте операторы === и !==, которые сравнивают и значения, и типы выражений.

    // bad
    false == 0;  // true
    false == ''; // true
    
    // good
    false === 0;  // false
    false === ''; // false
  • 9.2 Условные выражения, такие как if, вычисляются посредством приведения к логическому типу Boolean через метод ToBoolean, и всегда следует таким правилам:

    • Objects соответствует true
    • Undefined соответствует false
    • Null соответствует false
    • Booleans не меняется
    • Numbers соответствует false если является +0, 0, -0 или NaN, иначе true
    • Strings соответствует false если это пустая строка '', иначе true
    if ([0] && []) {
        // true
        // an array (even an empty one) is an object, objects will evaluate to true
    }
  • 9.3 Используйте короткий синтаксис.

    // bad
    if (name !== '') {
        // ...stuff...
    }
    
    // good
    if (name) {
        // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
        // ...stuff...
    }
    
    // good
    if (collection.length) {
        // ...stuff...
    }

⬆ к оглавлению

Блоки кода

  • 10.1 Используйте фигурные скобки для всех многострочных блоков.

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

⬆ к оглавлению

Комментарии

  • 11.1 Используйте /* ... */ для многострочных комментариев. Для jsdoc используйте /** ... */.

    // 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
     */
    function make(tag) {
        // ...stuff...
        return element;
    }
    
    // jsdoc format
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param {String} tag
     * @return {Element} element
     */
    function make(tag) {
        // ...stuff...
        return element;
    }
  • 11.2 Используйте // для однострочного комментария. Размещайте комментарий на новой строке над тем что комментируете. Можете добавить пустую строку над комментарием, чтобы визуально его выделить.

    // bad
    const active = true;  // is current tab
    
    // good
    // is current tab
    const active = true;
    
    // 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() {
        console.log('fetching type...');
        // set the default type to 'no type'
        const type = this._type || 'no type';
      
        return type;
    }

⬆ к оглавлению

Табы и пробелы

  • 12.1 Используйте программную табуляцию из 4 пробелов (делая Tab редактор подставит 4 пробела).

    // bad
    function foo() {
    ∙const name;
    }
    
    // bad
    function bar() {
    ∙∙const name;
    }
    
    // good
    function baz() {
    ∙∙∙∙const name;
    }
  • 12.2 Поставьте один пробел перед открывающей фигурной скобкой.

    eslint: space-before-blocks jscs: requireSpaceBeforeBlockStatements

    // 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',
    });
  • 12.3 Поставьте один пробел перед открывающей круглой скобкой в операторах управления (if, while, for и т.д.).

    eslint: space-after-keywords, space-before-keywords jscs: requireSpaceAfterKeywords

    // bad
    if(isJedi) {
        fight ();
    }
    
    // good
    if (isJedi) {
        fight();
    }
  • 12.4 Пробелы в вычислениях.

    eslint: space-infix-ops jscs: requireSpaceBeforeBinaryOperators, requireSpaceAfterBinaryOperators

    // bad
    const x=y+5;
    
    // good
    const x = y + 5;
  • 12.5 Длинна строки устанавливается в 100 символов, все что длиннее переносим. Допускается выход за границы в разумных пределах.

    Длинные строки неудобно читать. На современных FullHD мониторах можно расположить два окна рядом по горизонтали. Можно писать и под 80 символов, но это узковато.

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

⬆ к оглавлению

Запятые

  • 13.1 Скажите нет запятым в начале строки.

    eslint: comma-style jscs: requireCommaBeforeLineBreak

    // 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',
    };
  • 13.2 Можно ставить дополнительные запятые в конце объектов и массивов.

    eslint: comma-dangle jscs: requireTrailingComma

    Это улучшит diff'ы, меняется одна строка, а не две. Удобней добавлять свойства и элементы, не нужно беспокоиться есть или нет запятые, потому что они всегда есть.

    Если вы используете Babel — Babel при транскомпиляции игнорирует эти запятые, поэтому можно не беспокоиться об trailing comma problem.

    Если вы не используете Babel — современные браузеры, такие как Chrome, Firefox адекватно обрабатывают запятые в конце списка. Проблемы появляются только в браузерах IE (ниже 9 версии), так что тестируйте и будьте аккуратны.

    // bad - diff without trailing comma
    const hero = {
        firstName: 'Florence',
    -   lastName: 'Nightingale'
    +   lastName: 'Nightingale',
    +   inventorOf: ['coxcomb graph', 'modern nursing']
    };
    
    // good - 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',
    ];

⬆ к оглавлению

Точки с запятой

  • 14.1 Ставим в конце каждой инструкции.

    eslint: semi jscs: requireSemicolons

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

⬆ к оглавлению

Соглашение об именовании

  • 15.1 Избегайте однобуквенные имена. Имена должны давать представление о том, что это такое.

    // bad
    function q() {
        // ...stuff...
    }
    
    // good
    function query() {
        // ..stuff..
    }
  • 15.2 Используйте camelCase, когда именуете объекты, функции и переменные.

    eslint: camelcase jscs: requireCamelCaseOrUpperCaseIdentifiers

    Все популярные библиотеки для javascript используют camelCase, чтобы не путаться и соблюдать единообразие, пишем в camelCase. Исключением являются данные, которые передаются на сервер, там могут быть другие правила.

    // bad
    const OBJEcttsssss = {};
    const this_is_my_object = {};
    function c() {}
    
    // good
    const thisIsMyObject = {};
    function thisIsMyFunction() {}
  • 15.3 Используйте UpperCamelCase, когда именуете классы или конструкторы классов.

    eslint: new-cap jscs: requireCapitalizedConstructors

    // 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',
    });
  • 15.4 Используйте подчеркивание _ в качестве префикса для именования приватных методов и переменных объекта.

    eslint: no-underscore-dangle jscs: disallowDanglingUnderscores

    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
  • 15.5 Можете использовать CONSTANT_CASE для именования констант.

    const CAT_INIT_NUMBER_OF_LIVES = 9;
  • 15.6 В ES6 можно не сохранять ссылку на this. Используйте стрелочные функции или Function#bind.

    jscs: disallowNodeTypes

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

    Жить не можете без ссылки на this? Используйте тогда _this.

    function() {
        const _this = this;
        return function() {
            console.log(_this);
        };
    }

⬆ к оглавлению

About

Пишем JavaScript красиво.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published