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

А вы помните те времена, когда JavaScript был языком, который использовали только для оживления страниц сайта? Это время уже прошло, так как, языки программирования развиваются вместе с методами их использования. Посмотрите на код, который вы написали один, два года назад: он вас смущает? Если да, этот пост для вас.


Ниже вы найдете мой список из хороших практик, которые помогут сделать ваш JavaScript код проще в написании, чтении и поддержке.

Используйте линтер для форматирования вашего кода


Первый совет, который я вам дам это использование линтера. Он создан для проверки, соблюдения единого стиля в вашем коде. Это позволит сделать код консистентным во всех .js файлах. Очень полезно использовать линтер в командной работе — это позволит сделать стиль кода единым среди всех разработчиков одного проекта.

Что более важно, линтер делает исправления в вашем коде, когда это возможно. ESLint, например, делает довольно таки хорошо (используйте опцию --fix) и он отлично интегрируется с популярными IDE.

Вы также можете использовать Prettier, если сравнивать с ESLint, он больше сфокусирован на форматировании кода. Разница между ними небольшая.

Следующие пункты помогут вам выбрать какие правила использовать с линтером:

Используйте современные правила для линтера


Если вы в замешательстве и не можете решить, какой набор правил использовать для кода, выбирайте StandardJS. Это очень строгий линтер, который не даст вам отступить от правил. Несколько примеров:

  • Используйте 2 пробела для табуляции
  • Не используйте точки с запятой (мне это показалось очень странным, но через несколько дней я привык)
  • Пробелы после ключевых слов (например if) и фигурных скобок
  • И другие

StandardJS может устанавливаться в виде отдельного Node модуля. Но если вы хотите использовать его в большом проекте с существующей кодовой базой и отключить некоторые правила, используйте ESLint predefined config. Я например отключил no-mixed-operators и import/no-webpack-loader-syntax.

Используйте новые фишки ES2015+


Вы скорее всего, слышали об изменениях в ES2015+ (или ES6, ES7…). Вот несколько, без которых я больше не могу жить:

  • Arrow functions: написание функций типа x => x*2 очень полезно в функциональном программировании
  • Классы: перестаньте использовать прототипы, классы гораздо круче
  • Операции с массивами и объектами:

function doSomething() {
  const a = doSomethingElse()
  const b = doSomethingWithA(a)
  const otherResults = { c: '', d: '' }
  return { a, b, ...otherResults } // equivalent to { a: a, b: b }
}
const { a, c, ...rest } = doSomething() // Also works with arrays!
// `rest` looks like { b: ..., d: '' }

  • Создание промисов становится легче с async/await:

// Please try to write the same code with classic promises ;)
async function doSomething() {
  const a = await getValueForA()
  const b = await getValueForBFromA(a)
  const [c, d] = await Promise.all([
    // parallel execution
    getValueForC(), getValueForDFromB(b)
  ])
  const total = await calculateTotal(a, b, c, d)
  return total / 1000
}

Используйте функциональное программирование


Вокруг данной темы точится много споров. Но не смотря на это, функциональное программирование набирает популярность и не только в JavaScript. В чем причина? Оно делает код более предсказуемым, безопасным и гораздо более поддерживаемым. Вот вам несколько простых советов:

Для начала перестаньте использовать for для циклов. В большинстве случаев они вам не нужны. Доказательство:

const arr = [{ name: 'first', value: 13 }, { name: 'second', value: 7 }]

// Instead of:
const res = {}
for (let i = 0; i < arr.length; i++) {
  const calculatedValue = arr[i].value * 10
  if (calculatedValue > 100) {
    res[arr[i].name] = calculatedValue
  }
}

// Prefer:
const res = arr
  .map(elem => ({ name: elem.name, calculatedValue: elem.value * 10 }))
  .filter(elem => elem.calculatedValue > 100)
  .reduce((acc, elem) => ({
    [elem.name]: calculatedValue,
    ...acc
  }), {})

Хорошо, я признаю, что это довольно таки экстремальный пример и если вы не привыкли к функциональному программированию, это может показаться сложным. Давайте упростим код:

const enrichElementWithCalculatedValue =
  elem => ({ name: elem.name, calculatedValue: elem.value * 10 })
const filterElementsByValue = value =>
  elem => elem.calculatedValue > value
const aggregateElementInObject = (acc, elem) => ({
  [elem.name]: calculatedValue,
  ...acc
})
const res = arr
  .map(enrichElementWithCalculatedValue)
  .filter(filterElementsByValue(100))
  .reduce(aggregateElementInObject, {})

Здесь мы определили три функции, названия которых указывают на то, что эти функции делают.
Второй совет: создавайте локальные функции (даже в существующих функциях) подобрав для них грамотные названия, вы можете документировать код без комментариев!

Обратите внимание, что три локальные функции не изменяют контекст, в котором они выполняются. Не меняют внешние переменные, не обращаются к внешним сервисам… В функциональном программировании они называются pure (чистыми) функциями. У них есть большие преимущества:

  • Их легко тестировать, поскольку из заданных параметров возможен только один возможный результат, даже если мы вызываем функцию несколько раз;
  • Они гарантированно дают одинаковый результат независимо от фактического состояния приложения;
  • Состояние приложения остается прежним до и после вызова функции.

Исходя из вышесказанного, мой третий совет: используйте такие функции как можно чаще!

Несколько советов в завершение


  • Привыкайте работать с асинхронным кодом, как можно чаще используйте промисы, посмотрите на observales в RxJS (вот отличный туториал по функциональному реактивному программированию)
  • Пишите тесты! Возможно вам это покажется очевидным, но я знаю кучу проектов с непроверенным кодом. Хотя тестирование на JavaScript (front или backend) не так сложно, как кажется.
  • Используйте последние возможности языка: например, прекратите писать arr.indexOf(elem) !== -1. Замените это на arr.includes(elem).
  • Читайте много технических статей: subreddit JavaScript — очень хороший источник черпания информации о самых крутых практиках в экосистеме.

И в заключение самый важный совет, который я могу вам дать: всегда занимайтесь рефакторингом! Попробуйте улучшить модуль, который вы написали год назад. Попробуйте заменить var на const, используйте стрелочные функции или async / await чтобы упростить код… Всегда приятно работать с простым и понятным кодом.
Поделиться с друзьями
-->

Комментарии (7)


  1. mushamib
    08.06.2017 16:50
    -1

    Интересный материал, спасибо!


  1. http2
    08.06.2017 16:53
    +9

    Большинство (если не все) советов — ерунда. :)


  1. spmbt
    08.06.2017 16:53

    Как-то оно у вас бедно написано, если в 2 словах — это «используйте await и функциональщину». Здесь https://habrahabr.ru/post/312022/ всё гораздо подробнее и полнее.
    (А про await — да, там — ни слова, хотя уже стало актуально.)


  1. raveclassic
    08.06.2017 18:22
    +2

    Для начала перестаньте использовать for для циклов.
    Ну вот зачем вы так, сейчас же опять начнется…


  1. mwizard
    08.06.2017 18:35
    +1

    Пример с async-функцией лучше написать без await-ов, оставив await-ы на как можно позже в цепочке вызовов — когда результат на самом деле нужен. Разница заключается в том, что если промис не await-ить, то родительская функция не заблокируется, и тем самым несколько промисов будут исполняться параллельно (но не одновременно, конечно же).


    Следующий код демонстрирует разницу в два раза во времени выполнения между функцией, где все промисы сразу await-ятся, и между функцией, в которой await откладывается до последнего.


    Скрытый текст
    function delay(timeout) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, timeout * 1000);
        });
    }
    
    async function getValueForA() {
        await delay(1.0);
        return 1000;
    }
    
    async function getValueForBFromA(a) {
        await delay(1.0);
        const b = (await a) / 5;
        await delay(1.0);
    
        return b;
    }
    
    async function getValueForC() {
        await delay(1.0);
        return 30;
    }
    
    async function getValueForDFromB(b) {
        await delay(1.0);
        const d = (await b) / 50;
    
        return d;
    }
    
    async function calculateTotal(a, b, c, d) {
        return (await a) + (await b) + (await c) + (await d);
    }
    
    async function doSomethingA() {
        const a = await getValueForA();
        const b = await getValueForBFromA(a);
        const [ c, d ] = await Promise.all([
            getValueForC(),
            getValueForDFromB(b),
        ])
        const total = await calculateTotal(a, b, c, d);
        return total / 1000;
    }
    
    async function doSomethingB() {
        const a = getValueForA();
        const b = getValueForBFromA(a);
        const c = getValueForC();
        const d = getValueForDFromB(b);
        const total = await calculateTotal(a, b, c, d); // только тут!
        return total / 1000;
    }
    
    async function timeit(times, func) {
        const start = process.hrtime();
        for (let x = 0; x < times; x++) {
            value = await func();
        }
        const [diff_s, diff_ns] = process.hrtime(start);
    
        return [diff_s * 1000000000 + diff_ns, value];
    }
    
    async function main() {
        const rep = 1;
        const [[At, Av], [Bt, Bv]] = await Promise.all([timeit(rep, doSomethingA), timeit(rep, doSomethingB)]);
    
        console.log(`A: (==${Av}) ${At}`);
        console.log(`B: (==${Av}) ${Bt}`);
        console.log(`d: ${At/Bt}`);
    }
    
    main()


  1. Fen1kz
    08.06.2017 23:01
    +1

    Я хоть и причисляю себя к так ненавидимым "js-хипстерам", но тут советы за гранью зла.


    "Не используйте точки с запятой"

    someFunction()
    //и замыкание
    (() => 42)()

    ^ ПЫЩЩЩ, Uncaught TypeError: someFunction(...) is not a function


    Для начала перестаньте использовать for для циклов.

    Я думаю имелось ввиду переставать использовать for для простого прохода по массиву. Формулировка автора заслуживает расстрела.


    Используйте функциональное программирование и фишки ES

    Опять же, к этому каждый должен приходить сам.


    Я тут недавно экспериментировал, https://jsperf.com/flatten1, так вот ES6 функция раза в 3 медленнее чем ES5


    Ну и в целом советы очень тупые, нельзя в одном тексте скакать от "пишите 2 пробела" до "пишите тесты" и "читайте много технических статей на reddit'е"


    Попробуйте заменить var на const

    Попробуйте не давать такие советы, а реально объяснять.


    Ух, что-то начал цеплятся и автор прям выбесил, пойду ему там в комменты выскажу всё.


  1. Hazrat
    09.06.2017 00:31

    Главный вопрос, что выбрать AirBnB Style или Standard Style?