Альтернативы решения типичных задач
Maksym Pohribniak![](/file/5b73fc554f5d09676b33a.png)
Мне часто приходится выступать в роли ментора:
- в офисе (для junior разработчиков)
- на курсах (для совсем начинающих)
И в связи с этим приходится читать много javascript кода написанного неопытными разработчиками. Также, схожий код часто встречается у разработчиков с другим основным языком (не JavaScript).
Я собрал подборку простых кусков кода, которые по моему мнению можно более красиво и понятно(не всегда) переписать.
Это не значит, что код, в примерах ниже, плохой и так писать неверно. Я лишь предлагаю альтернативы.
Далее по пунктам:
0.
Так как здесь есть совсем новички, этот пункт для вас:
Это единственный базовый пример, но я не мог о нем не сказать, так, как слишком много людей в начале обучения пишут подобный код.
Вам не нужно четко сравнивать булевые значения в условных выражения с true/false. Код:
if (v0 === false) {}
if (v1 === true && v2 === true) {}
Полностью аналогичен следующему:
if (!v0) {}
if (v1 && v2) {}
Возвращая булевое значение из функции, не нужно делать так:
function isEven(value){
if (value % 2 === 0) {
return true;
} else {
return false;
}
}
Если вы пишете как в примере выше, вы не понимаете, что такое логическое значение и как с ними работать.
if (condition) {
answer = true;
} else {
answer = false;
}
это просто излишне многословная версия условия
answer = condition // или !!condition
Функцию isEven можно переписать так:
function isEven() {
return value % 2 === 0;
}
1.
Нужно узнать является переменная letter гласной буквой. Как эту задачу решают чаще всего? Так как и понимают. Через if и "или"(||):
if (
letter === 'a' ||
letter === 'e' ||
letter === 'u' ||
letter === 'o' ||
letter === 'i') {
// do something
}
Более декларативно этот кусок можно переписать таким образом:
if (['a', 'e', 'u', 'o', 'i'].includes(letter)) {
// do something
}
Мы храним все допустимые варианты в одном месте и нам просто разширять возможности условия.
2.
Есть список,
const files = [
'index.js',
'ui.js',
'select.js'
];
его нужно расширить в зависипости от определенных условий,
if (someFlag) {
files.push('f1.js');
}
if (anotherFlag) {
files.push('q1.js');
} else {
files.push('q2.js');
}
добавить тот или иной элемент или добавлять/не добавлять вовсе.
Описанный выше код можно сделать более лаконичным с перебирающими методами массивов:
const files = [
'index.js'
'index.js',
'ui.js',
'select.js'
someFlag && 'f1.js',
anotherFlag ? 'q1.js' : 'q2.js'
].filter(Boolean);
Если someFlag будет приведен в false, то он откинется методом фильтр. Плюс больше не нужно создавать if'ы. Перебирающие методы позволяют делать наш код более декларативным.
Например следующий код:
if (variable1 === 'some value' && variable2 === 'another value' && variable3 &&
(variable4 === 'some value' || variable5 === 'another value' || variable6)) {
// do something
}
Условие получилось очень длинным и читать его затруднительно.
Конечно, можно разбить его на несколько переменных, но посмотрите на следующий вариант:
const requiredExpressions = [
variable1 === 'some value',
variable2 === 'another value',
variable3
].every(Boolean);
const optionalExpressions = [
variable4 === 'some value',
variable5 === 'another value',
variable6
].some(Boolean);
if (requiredExpressions && optionalExpressions) {
// do something
}
Конечно, теперь он выгляди намного длиннее чем в первом варианте, но Методы some и every четко дают понять о происходящем, и код читается проще.
3.
Иногда, нужно проверить пустой ли обьект, и встроенного метода для этого в JavaScript нет. Вариант который я встречал выглядит так:
function isEmpty(obj) {
for (let prop in obj) {
if (obj[prop]) {
return false;
}
}
return true;
}
isEmpty(objectToCheck)
Примерно такой же ответ в топах ответов на этот вопрос на Stackoverflow.
Весь код выше можно заменить одной строкой:
Object.keys(objectToCheck).length === 0
Метод Object.keys собирает ключи объекта. Соответственно если их нет –возвращает пустой массив.
4.
Преобразование данных. Мы получаем объект в свойствах которого числа почему-то хранятся в виде строк. Для дальнейшей работы с ним нужно сделать их числами. Как чаще всего решают эту задачу:
const data = {
value1: '1234',
value2: '34',
value3: '52'
}
const imperativeDataNumberValues = {};
for (let property in inputObject) {
if (inputObject.hasOwnProperty(property)) {
imperativeDataNumberValues[property] = parseFloat(inputObject[property]);
}
}
Перебираем ключи, получаем значение, с помощью функции parseFloat делаем из него число. Далее записываем пару ключ/значение в созданный пустой объект. Вполне логично. А теперь декларативный вариант:
const dataNumberValues = Object.keys(inputObject)
.reduce((acc, next) => ({ ...acc, [next]: parseFloat(inputObject[next]) }), {});
Возможно, этот метод окажется менее читабельным, но если у вас и команды есть должный опыт работы с перебирающими методами – проблем возникнуть не должно.
6.
Флаги аргументы. При расширении функционала, иногда, добавление аргумента флага может показаться вам хорошей идеей, но, поверьте, это не так.
function createFile(name, isPublic) {
if (isPublic) {
fs.create(`./public/${name}`);
} else {
fs.create(name);
}
}
Вместо этого создайте 2 функции, каждая из которы выполнит свою задачу:
function createFile(name) {
fs.create(name);
}
function createPublicFile(name) {
createFile(`./public/${name}`);
}
Чем такой подход лучше читать здесь.
5.
И последний очень спорный вариант: я не видел чтобы кто-то писал такой код, и не могу рекомендовать использовать его. Но мне он кажется интересным.
Допустим, вам нужно задать различные значения 3-м переменным в зависимости от переменной someFlag:
let v1, v2, v3;
if (someFlag) {
v1 = '+v1';
v2 = '+v2'
v2 = '+v3'
} else {
v1 = '-v1';
v2 = '-v2';
v2 = '-v3';
}
Используем деструктуризацию и приведение типов чтобы перезаписать это пример в одну строку:
let [v1, v2, v3] = [['+v1', '+v2', '+v3'], ['-v1', '-v2', '-v3']][+!someFlag];
Ставьте лайк посту, если разобрались как работает данный пример 😊.
Спасибо за внимание!