Перевод книги Райана Макдермота clean-code-javascript

В javascript отсутствуют ключевые слова private и public, что усложняет реализацию классов. Лучше использовать геттеры и сеттеры для доступа к свойствам объекта, чем напрямую к ним обращаться. Вы спросите «Зачем?». Вот несколько причин:
Плохо:
Хорошо:
Это возможно с помощью замыканий.
Плохо:
Хорошо:
Колбеки приводят к чрезмерной вложенности и плохой читаемости кода.
Плохо:
Хорошо:
Промисы очень хорошая альтернатива колбекам, но в ES2017 / ES8 спецификации появился аsync/аwait, который предлагает ещё лучшее решение. Все, что вам нужно, это написать функцию с префиксом async, внутри которой вы можете писать вашу асинхронную логику императивно. аsync/аwait можно использовать прямо сейчас при помощи babel.
Плохо:
Хорошо:
Бросать ошибки — хорошее решение! Это означает, что во время выполнения вы будете знать, если что-то пошло не так. Вы сможете остановить выполнение вашего приложения в нужный момент и видеть место ошибки с помощью стек трейса в консоли.
Ничего не делая с пойманной ошибкой, вы теряете возможность исправить ошибку или отреагировать на неё когда-либо. Вывод ошибки в консоль(console.log(error)) не дает лучшего результата, потому что ошибка может потеряться среди выводимых записей в консоль. Если вы заворачиваете кусок кода в try / catch, значит вы предполагаете возникновение ошибки. В таком случае вы должны иметь запасной план.
Плохо:
Хорошо:
Вы не должны игнорировать ошибки, возникшие в промисе, по той же причине, что отловленные ошибки в try / catch.
Плохо:
Хорошо:
Оглавление:
- Введение
- Переменные
- Функции
- Классы
- Объекты и структуры данных. Асинхронность. Обработка ошибок.
- Тестирование. Форматирование. Комментарии.

Объекты и структуры данных
Используйте геттеры и сеттеры
В javascript отсутствуют ключевые слова private и public, что усложняет реализацию классов. Лучше использовать геттеры и сеттеры для доступа к свойствам объекта, чем напрямую к ним обращаться. Вы спросите «Зачем?». Вот несколько причин:
- Если вы хотите реализовать больше, чем просто доступ к свойству, вам нужно поменять реализацию в одном месте, а не по всему коду.
- Валидацию легко реализовать на уровне реализации сеттера
- Инкапсуляция внутреннего состояния объекта
- Легко добавить логирование и обработку ошибок на уровне геттеров и сеттеров
- Наследуя этот класс, вы можете переопределить функциональность по умолчанию
- Вы можете лениво подгружать свойства вашего объекта, например, с сервера.
Плохо:
class BankAccount { constructor() { this.balance = 1000; } } const bankAccount = new BankAccount(); // Покупаем, например, обувь... bankAccount.balance -= 100;
Хорошо:
class BankAccount { constructor(balance = 1000) { this._balance = balance; } // It doesn't have to be prefixed with `get` or `set` to be a getter/setter set balance(amount) { if (this.verifyIfAmountCanBeSetted(amount)) { this._balance = amount; } } get balance() { return this._balance; } verifyIfAmountCanBeSetted(val) { // ... } } const bankAccount = new BankAccount(); // Покупаем, например, обувь... bankAccount.balance -= shoesPrice; // получаем баланс let balance = bankAccount.balance;
Реализуйте приватные свойства ваших объектов
Это возможно с помощью замыканий.
Плохо:
const Employee = function(name) { this.name = name; }; Employee.prototype.getName = function getName() { return this.name; }; const employee = new Employee('John Doe'); console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
Хорошо:
const Employee = function (name) { this.getName = function getName() { return name; }; }; const employee = new Employee('John Doe'); console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
Асинхронность
Используйте промисы вместо колбеков
Колбеки приводят к чрезмерной вложенности и плохой читаемости кода.
Плохо:
const request = require('request'); const fs = require('fs'); const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin'; request.get(url, (requestErr, response) => { if (requestErr) { console.error(requestErr); } else { fs.writeFile('article.html', response.body, (writeErr) => { if (writeErr) { console.error(writeErr); } else { console.log('File written'); } }); } });
Хорошо:
const requestPromise = require('request-promise'); const fsPromise = require('fs-promise'); const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin'; requestPromise.get(url) .then((response) => { return fsPromise.writeFile('article.html', response); }) .then(() => { console.log('File written'); }) .catch((err) => { console.error(err); });
Async/Await делает код чище, чем промисы
Промисы очень хорошая альтернатива колбекам, но в ES2017 / ES8 спецификации появился аsync/аwait, который предлагает ещё лучшее решение. Все, что вам нужно, это написать функцию с префиксом async, внутри которой вы можете писать вашу асинхронную логику императивно. аsync/аwait можно использовать прямо сейчас при помощи babel.
Плохо:
const requestPromise = require('request-promise'); const fsPromise = require('fs-promise'); const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin'; requestPromise.get(url) .then((response) => { return fsPromise.writeFile('article.html', response); }) .then(() => { console.log('File written'); }) .catch((err) => { console.error(err); });
Хорошо:
const requestPromise = require('request-promise'); const fsPromise = require('fs-promise'); async function getCleanCodeArticle() { try { const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin'; const response = await requestPromise.get(url); await fsPromise.writeFile('article.html', response); console.log('File written'); } catch(err) { console.error(err); } }
Обработка ошибок
Бросать ошибки — хорошее решение! Это означает, что во время выполнения вы будете знать, если что-то пошло не так. Вы сможете остановить выполнение вашего приложения в нужный момент и видеть место ошибки с помощью стек трейса в консоли.
Не игнорируйте отловленные ошибки
Ничего не делая с пойманной ошибкой, вы теряете возможность исправить ошибку или отреагировать на неё когда-либо. Вывод ошибки в консоль(console.log(error)) не дает лучшего результата, потому что ошибка может потеряться среди выводимых записей в консоль. Если вы заворачиваете кусок кода в try / catch, значит вы предполагаете возникновение ошибки. В таком случае вы должны иметь запасной план.
Плохо:
try { functionThatMightThrow(); } catch (error) { console.log(error); }
Хорошо:
try { functionThatMightThrow(); } catch (error) { // Один из вариантов (более заметный, чем console.log): console.error(error); // Другой вариант - известить пользователя про ошибку: notifyUserOfError(error); // И еще вариант - отправить ошибку на сервер : reportErrorToService(error); // Или используйте все три варианта! }
Не игнорируйте ошибки, возникшие в промисах
Вы не должны игнорировать ошибки, возникшие в промисе, по той же причине, что отловленные ошибки в try / catch.
Плохо:
getdata() .then((data) => { functionThatMightThrow(data); }) .catch((error) => { console.log(error); });
Хорошо:
getdata() .then((data) => { functionThatMightThrow(data); }) .catch((error) => { // Один из вариантов (более заметный, чем console.log): console.error(error); // Другой вариант - известить пользователя про ошибку: notifyUserOfError(error); // И еще вариант - отправить ошибку на сервер : reportErrorToService(error); // Или используйте все три варианта! });