[Перевод] Чистый javascript.Объекты и структуры данных. Асинхронность. Обработка ошибок
Перевод книги Райана Макдермота 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;
}
set balance(amount) {
if (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
Асинхронность
Используйте промисы вместо колбеков
Колбеки приводят к чрезмерной вложенности и плохой читаемости кода.
Плохо:
require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) => {
if (requestErr) {
console.error(requestErr);
} else {
require('fs').writeFile('article.html', response.body, (writeErr) => {
if (writeErr) {
console.error(writeErr);
} else {
console.log('File written');
}
});
}
});
Хорошо:
require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
.then((response) => {
return require('fs-promise').writeFile('article.html', response);
})
.then(() => {
console.log('File written');
})
.catch((err) => {
console.error(err);
});
Async/Await делает код чище, чем промисы
Промисы очень хорошая альтернатива колбекам, но в ES2017 / ES8 спецификации появился аsync/аwait, который предлагает ещё лучше решение. Все, что вам нужно, это написать функцию с префиксом async, внутри которой вы можете писать вашу асинхронную логику императивно. аsync/аwait можно использовать прямо сейчас при помощи babel.
Плохо:
require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
.then((response) => {
return require('fs-promise').writeFile('article.html', response);
})
.then(() => {
console.log('File written');
})
.catch((err) => {
console.error(err);
});
Хорошо:
async function getCleanCodeArticle() {
try {
const response = await require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
await require('fs-promise').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);
// Или используйте все три варианта!
});