От автора: когда мы применяем в JavaScript условные операторы, хочется сделать код как можно чище. Вот 5 советов относительно того, как писать лучшие / более чистые условные выражения.
1. Используйте Array.includes для множественных критериев.
Давайте рассмотрим приведенный ниже пример:
// condition function test(fruit) { if (fruit == 'apple' || fruit == 'strawberry') { console.log('red'); } }
На первый взгляд, этот пример выглядит неплохо. Однако, что, если у нас будут другие красные фрукты, скажем, cherry и cranberries? Будем ли мы расширять оператор с помощью других || ?
Мы можем переписать условие выше, используя Array.includes
function test(fruit) { // extract conditions to array const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries']; if (redFruits.includes(fruit)) { console.log('red'); } }
Мы извлекаем красные фрукты (условия) в массив. Таким образом, код выглядит более аккуратным.
2. Меньше вложений, раннее возвращение
Давайте расширим предыдущий пример, чтобы включить еще два условия:
если фрукт не предоставлен, выдать ошибку
принимать и выводить количество фруктов, если оно превышает 10.
function test(fruit, quantity) { const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries']; // condition 1: fruit must has value if (fruit) { // condition 2: must be red if (redFruits.includes(fruit)) { console.log('red'); // condition 3: must be big quantity if (quantity > 10) { console.log('big quantity'); } } } else { throw new Error('No fruit!'); } } // test results test(null); // error: No fruits test('apple'); // print: red test('apple', 20); // print: red, big quantity
Посмотрите на этот код, у нас есть:
1 if / else, который отфильтровывает недействительные условия
3 уровня вложенных операторов if (условие 1, 2 и 3)
Общее правило, которое я лично соблюдаю — возвращайте раньше, когда найдены недействительные условия.
/_ return early when invalid conditions found _/ function test(fruit, quantity) { const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries']; // condition 1: throw error early if (!fruit) throw new Error('No fruit!'); // condition 2: must be red if (redFruits.includes(fruit)) { console.log('red'); // condition 3: must be big quantity if (quantity > 10) { console.log('big quantity'); } } }
Таким образом, мы имеем на один уровень вложенности меньше. Этот стиль кодирования особенно подходит, когда у вас есть длинный оператор if (представьте, что вам нужно прокрутить страницу до самого дна, чтобы узнать, что указывает оператор else, это не круто).
Мы можем дополнительно уменьшить вложенность if, инвертируя условия и возвращая раньше. Посмотрите на условие 2 ниже:
/_ return early when invalid conditions found _/ function test(fruit, quantity) { const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries']; if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red console.log('red'); // condition 3: must be big quantity if (quantity > 10) { console.log('big quantity'); } }
Инвертируя условие 2, мы убираем из кода вложенный оператор. Этот метод полезен, когда у нас длинная логика, и мы хотим прекратить дальнейший процесс, когда условие не выполняется.
Однако такая практика не является жестким требованием. Спросите себя: эта версия (без вложения) лучше / читабельнее предыдущей (условие 2 с вложением)?
Я просто бы оставила предыдущую версию (условие 2 с вложением). Потому что тогда:
код короткий и прямой, он более понятный с вложением
инвертирование условие может быть более трудным для понимания (увеличение когнитивной нагрузки)
Поэтому всегда будьте нацелены на «меньше вложений» и «раннее возвращение», но не переусердствуйте.
3. Используйте параметры функции по умолчанию и деструктурирование
Я думаю, что приведенный ниже код может выглядеть вам знакомым, при работе с JavaScript нам всегда нужно проверить значение на null / undefined и присвоить значение по умолчанию:
function test(fruit, quantity) { if (!fruit) return; const q = quantity || 1; // if quantity not provided, default to one console.log(`We have ${q} ${fruit}!`); } //test results test('banana'); // We have 1 banana! test('apple', 2); // We have 2 apple!
Фактически, мы можем исключить переменную q, назначив параметры функции по умолчанию.
function test(fruit, quantity = 1) { // if quantity not provided, default to one if (!fruit) return; console.log(`We have ${quantity} ${fruit}!`); } //test results test('banana'); // We have 1 banana! test('apple', 2); // We have 2 apple!
Гораздо проще и интуитивнее, не так ли? Обратите внимание, что каждый параметр может иметь собственный параметр функции по умолчанию. Например, мы можем назначить значение по умолчанию для fruit — function test(fruit = ‘unknown’, quantity = 1).
Что, если fruit является объектом? Можем ли мы назначить параметр по умолчанию?
function test(fruit) { // printing fruit name if value provided if (fruit && fruit.name) { console.log (fruit.name); } else { console.log('unknown'); } } //test results test(undefined); // unknown test({ }); // unknown test({ name: 'apple', color: 'red' }); // apple
Посмотрите на пример выше, мы хотим вывести название фрукта, если оно доступно, или мы выводим unknown. Мы можем избежать условной проверки fruit && fruit.name с помощью параметра функции по умолчанию и деструктурирования.
// destructing - get name property only // assign default empty object {} function test({name} = {}) { console.log (name || 'unknown'); } //test results test(undefined); // unknown test({ }); // unknown test({ name: 'apple', color: 'red' }); // apple
Поскольку нам нужно только свойства name, мы можем деструктурировать параметр с помощью {name}, тогда мы можем в коде использовать name как переменную вместо fruit.name.
Мы также назначаем пустой объект {} в качестве значения по умолчанию. Если мы этого не сделаем, вы получите ошибку при выполнении строки test(undefined): «Cannot destructure property name of ‘undefined’ or ‘null’.», — потому что свойство name не определено.
сли вы не возражаете против использования сторонних библиотек, есть несколько способов сократить проверку на null:
используйте функцию Lodash get
используйте библиотеку с открытым исходным кодом Facebook idx (с Babeljs)
Вот пример использования Lodash:
// Include lodash library, you will get _ function test(fruit) { console.log(__.get(fruit, 'name', 'unknown'); // get property name, if not available, assign default value 'unknown' } //test results test(undefined); // unknown test({ }); // unknown test({ name: 'apple', color: 'red' }); // apple
Здесь вы можете запустить демо-код. Кроме того, если вы являетесь поклонником функционального программирования (FP), вы можете использовать Lodash fp, функциональную версию Lodash (метод изменен на get или getOr).
4. Отдавайте предпочтение Map / литералам объектов, а не оператору switch
Давайте рассмотрим пример ниже, мы хотим вывести фрукты по цвету:
function test(color) { // use switch case to find fruits in color switch (color) { case 'red': return ['apple', 'strawberry']; case 'yellow': return ['banana', 'pineapple']; case 'purple': return ['grape', 'plum']; default: return []; } } //test results test(null); // [] test('yellow'); // ['banana', 'pineapple']
Вышеприведенный код кажется нормальным, но я нахожу его довольно раздутым. Тот же результат может быть достигнут с помощью литерала объекта:
// use object literal to find fruits in color const fruitColor = { red: ['apple', 'strawberry'], yellow: ['banana', 'pineapple'], purple: ['grape', 'plum'] }; function test(color) { return fruitColor[color] || []; }
В качестве альтернативы вы можете использовать Map для достижения того же результата:
// use Map to find fruits in color const fruitColor = new Map() .set('red', ['apple', 'strawberry']) .set('yellow', ['banana', 'pineapple']) .set('purple', ['grape', 'plum']); function test(color) { return fruitColor.get(color) || []; }
Map — это тип объекта, доступный с ES2015, который позволяет хранить пару ключа значение.
Стоит ли нам вообще отказаться от оператора switch? Не думаю. Лично я использую литерал объекта, когда это возможно, но я не ставила бы жесткое правило запретить использовать его, если это имеет смысл для вашего сценария.
Рефакторинг синтаксиса
В приведенном выше примере мы можем фактически реорганизовать наш код, чтобы добиться того же результата с помощью Array.filter.
const fruits = [ { name: 'apple', color: 'red' }, { name: 'strawberry', color: 'red' }, { name: 'banana', color: 'yellow' }, { name: 'pineapple', color: 'yellow' }, { name: 'grape', color: 'purple' }, { name: 'plum', color: 'purple' } ]; function test(color) { // use Array filter to find fruits in color return fruits.filter(f => f.color == color); }
Всегда есть несколько способов добиться того же результата. Мы показали вам 4. Кодирование — это весело!
5. Используйте Array.every и Array.some для полных / частичных критериев
Этот последний совет больше посвящен использованию новой (но не такой уже и новой) функции массивов Javascript для сокращения кода. Посмотрите на код ниже, мы хотим проверить, все ли фрукты красного цвета:
const fruits = [ { name: 'apple', color: 'red' }, { name: 'banana', color: 'yellow' }, { name: 'grape', color: 'purple' } ]; function test() { let isAllRed = true; // condition: all fruits must be red for (let f of fruits) { if (!isAllRed) break; isAllRed = (f.color == 'red'); } console.log(isAllRed); // false }
Код такой длинный! Мы можем уменьшить количество строк с помощью Array.every:
const fruits = [ { name: 'apple', color: 'red' }, { name: 'banana', color: 'yellow' }, { name: 'grape', color: 'purple' } ]; function test() { // condition: short way, all fruits must be red const isAllRed = fruits.every(f => f.color == 'red'); console.log(isAllRed); // false }
Гораздо чище? Аналогичным образом, если мы хотим проверить, является ли какой-либо из фруктов красным, мы можем использовать Array.some.
const fruits = [ { name: 'apple', color: 'red' }, { name: 'banana', color: 'yellow' }, { name: 'grape', color: 'purple' } ]; function test() { // condition: if any fruit is red const isAnyRed = fruits.some(f => f.color == 'red'); console.log(isAnyRed); // true }
Заключение
Давайте вместе создавать более читаемый код. Надеюсь, вы узнали что-то новое из этой статьи. Это все. Удачного кодирования!
Автор: Jecelyn Yeen
Источник: https://scotch.io/
Редакция: Команда webformyself.