Советы по написанию короткого, лаконичного и чистого кода на JavaScript Обсудить Сложные вещи должны стать проще – и современный JavaScript дает нам все возможности для этого. 1. Добавление свойства в объект по условию Если нужно добавить в объект свойство при соблюдении некоторого условия, используйте комбинацию spread-оператора ... и оператора логического умножения &&. const condition = true; const person = { id: 1, name: 'John Doe', ...(condition && { age: 16 }), }; Если условие condition верно, оператор && вернет результат последнего выполненного выражения – объект { age: 16 }. А оператор распространения позволит быстро ввести его в структуру уже существующего объекта person. Если же condition ложно, то мы получим нечто подобное: const person = { id: 1, name: 'John Doe', ...(false), // оператор && вернул false }; console.log(person); // { id: 1, name: 'John Doe' } Spread-оператор, получив false, никак не изменит объект person. Условие не выполнено – свойство не добавлено. 2. Проверка наличия свойства в объекте Иногда важную роль играет не значение некоторого свойства в объекте, а само его наличие или отсутствие. Например, такая проверка часто используется для формирования дефолтных конфигураций. Чтобы узнать, определен ли в объекте некоторый ключ, нужно использовать оператор in. const person = { name: 'John Doe', salary: 1000 }; console.log('salary' in person); // true console.log('age' in person); // false Он возвращает true или false, в зависимости от того, удалось ли найти требуемое поле. 3. Динамические имена свойств в объектах Чтобы определить в объекте свойство, имя которого лежит в переменной, используйте синтаксис с квадратными скобками: const dynamic = 'flavour'; var item = { name: 'Biscuit', [dynamic]: 'Chocolate' } console.log(item); // { name: 'Biscuit', flavour: 'Chocolate' } Точно таким же образом вы можете обратиться к этому свойству объекта: const keyName = 'name'; console.log(item[keyName]); // returns 'Biscuit' Динамическая установка и доступ очень удобны, если имя свойства определяется только в рантайме (например, берется из пользовательского ввода). 4. Деструктуризация объекта с динамическими ключами Вы точно знаете, что при деструктуризации переменной ее свойства могут быть переименованы с помощью синтаксиса oldKey: newKey. Но знаете ли вы, что это можно сделать даже в том случае, если вы точно не знаете, как называется свойство объекта (например, если оно устанавливается динамически). Вот простой пример деструктуризации с использованием алиасов (переименованием свойств): const person = { id: 1, name: 'John Doe' }; const { name: personName } = person; console.log(personName); // 'John Doe' При деструктуризации объекта person значение его свойства name записывается в новую переменную personName. А теперь представим, что имя свойства устанавливается динамически. Чтобы извлечь его значение при деструктуризации, нужно воспользоваться квадратными скобками, точно так же, как мы делали в предыдущем примере: const templates = { 'hello': 'Hello there', 'bye': 'Good bye' }; const templateName = 'bye'; const { [templateName]: template } = templates; console.log(template) // 'Good bye' При деструктуризации будет создана переменная template, в которую запишется значение свойства templates.bye. 5. Оператор нулевого слияния (??) Nullish coalescing operator ?? (оператор слияния с null) полезен, когда вы хотите проверить, равно ли некоторое значение null или undefined. Если это так, будет возвращено выражение с правой стороны оператора, если же значение ненулевое, то вернется оно само. // 1 const foo = null ?? 'Hello'; console.log(foo); // 'Hello' // 2 const bar = 'Not null' ?? 'Hello'; console.log(bar); // 'Not null' // 3 const baz = 0 ?? 'Hello'; console.log(baz); // 0 Обратите внимание на третий пример – возвращается 0, а не строка с правой стороны оператора. 0 является falsy-значением в JavaScript (при логических преобразованиях превращается в false), однако оператор нулевого слияния это не волнует, он проверяет только null и undefined. const cannotBeZero = 0 || 5; console.log(cannotBeZero); // 5 const canBeZero = 0 ?? 5; console.log(canBeZero); // 0 В этом примере вы четко видна разница между оператором логического сложения ||, который использует логические преобразования, и оператором ??, который этого не делает. 6. Опциональные последовательности (?.) Вас тоже ужасно раздражают ошибки доступа к свойствам несуществующего объекта, вроде TypeError: Cannot read property 'foo' of null? Каждый JavaScript-разработчик с ними сталкивался. const book = { id:1, title: 'Title', author: null }; // обычно мы делаем так console.log(book.author.age) // и получаем ошибку доступа к свойству, т.к. book.author == null // добавляем проверку на первом уровне console.log(book.author && book.author.age); // возвращает null, ошибок нет Для элегантного решения этой проблемы предназначен оператор опциональной последовательности. // обращаемся к свойству с помощью optional chaining console.log(book.author?.age); // возвращает undefined // более глубокий доступ console.log(book.author?.address?.city); // возвращает undefined Прежде всего синтаксис стал более кратким. Также обратите внимание на возвращаемое значение. При использовании для проверки оператора && мы получаем значение первого операнда – book.author, которое равно null. А оператор ?. возвращает undefined, что более логично. Опциональные последовательности можно использовать даже для вызова методов: const person = { firstName: 'Haseeb', lastName: 'Anwar', printName: function () { return `${this.firstName} ${this.lastName}`; }, }; console.log(person.printName()); // возвращает 'Haseeb Anwar' console.log(persone.doesNotExist?.()); // возвращает undefined Если метод не найдется, вернется undefined. 7. Логическое преобразование с помощью оператора !! Для быстрой конвертации любого значения в булево (true/false) вы можете использовать двойной оператор логического отрицания !!. const greeting = 'Hello there!'; console.log(!!greeting) // true const noGreeting = ''; console.log(!!noGreeting); // false Важно: обязательно нужно провести двойное отрицание, так как унарное вернет противоположное значение. 8. Преобразование между строками и числами Помимо неявных булевых преобразований (см. предыдущий пример), в JavaScript существуют также хаки для неявных преобразований в строку или число. Например, унарный оператор + превратит значение строкового типа в числовое (если сможет, конечно): const stringNumer = '123'; console.log(+stringNumer); // 123 console.log(typeof +stringNumer); // 'number' Для обратного преобразования также нужен оператор +, на этот раз стандартный бинарный: const myString = 25 + ''; console.log(myString); // returns '25' console.log(typeof myString); // returns 'string' Если при сложении оператору попадает операнд строкового типа, то результат тоже будет строкой. Важно: такие неявные преобразования типов очень удобны, но могут плохо читаться. Не жертвуйте читаемостью кода ради его краткости, особенно если вы работаете в команде. 9. Проверка на falsy-значения в массиве В JS есть несколько отличных методов для перебора массивов – filter, some и every. Они позволяют использовать разнообразные коллбэки для оценки значений элементов. Однако иногда требуется самая простая проверка – является ли значение истинным (truthy) или ложным (falsy). Для этого прекрасно подойдет функция конвертации типа Boolean: const myArray = [null, false, 'Hello', undefined, 0]; // фильтруем falsy-Значения const filtered = myArray.filter(Boolean); console.log(filtered); // ['Hello'] // проверяем, если ли хоть одно truthy-Значение const anyTruthy = myArray.some(Boolean); console.log(anyTruthy); // true // проверяем, являются ли все значения truthy const allTruthy = myArray.every(Boolean); console.log(allTruthy); // false Обратите внимание на краткую запись. Привычнее было бы написать вот так: myArray.filter(val => Boolean(val)); Однако это ровно то же самое. Boolean выступает в роли коллбэк-функции, которая будет вызвана для каждого элемента массива. Она принимает лишь один аргумент – им и будет элемент массива. Важно: будьте внимательны с такими краткими записями. В коллбэк-функцию передается не один параметр, а два или три (в зависимости от метода). Если ваша функция принимает больше, чем один аргумент, вы можете столкнуться с неожиданными эффектами, например: const myArray = ['1', '7', '11'] const mappedArray = myArray.map(parseInt); // [1, NaN, 3] 10. Уменьшение уровня вложенности в массиве массивов Метод Array.prototype.flat – свежее приобретение JavaScript. Он позволяет раскрывать вложенные массивы, поднимая их содержимое на уровень родительского. const myArray = [{ id: 1 }, [{ id: 2 }], [{ id: 3 }]]; const flattedArray = myArray.flat(); // [ { id: 1 }, { id: 2 }, { id: 3 } ] Он даже может работать на несколько уровней в глубину – для этого нужно передать в метод соответствующий параметр: const arr = [0, 1, 2, [[[3, 4]]]]; console.log(arr.flat(2)); // [0, 1, 2, [3,4]] *** Пользуетесь этими приемами? Нашли что-то новое для себя? Делитесь своими любимыми JavaScript-трюками в комментариях.
Сложные вещи должны стать проще – и современный JavaScript дает нам все возможности для этого.
Если нужно добавить в объект свойство при соблюдении некоторого условия, используйте комбинацию spread-оператора ... и оператора логического умножения &&.
...
&&
const condition = true; const person = { id: 1, name: 'John Doe', ...(condition && { age: 16 }), };
Если условие condition верно, оператор && вернет результат последнего выполненного выражения – объект { age: 16 }. А оператор распространения позволит быстро ввести его в структуру уже существующего объекта person.
condition
{ age: 16 }
person
Если же condition ложно, то мы получим нечто подобное:
const person = { id: 1, name: 'John Doe', ...(false), // оператор && вернул false }; console.log(person); // { id: 1, name: 'John Doe' }
Spread-оператор, получив false, никак не изменит объект person. Условие не выполнено – свойство не добавлено.
false
Иногда важную роль играет не значение некоторого свойства в объекте, а само его наличие или отсутствие. Например, такая проверка часто используется для формирования дефолтных конфигураций.
Чтобы узнать, определен ли в объекте некоторый ключ, нужно использовать оператор in.
in
const person = { name: 'John Doe', salary: 1000 }; console.log('salary' in person); // true console.log('age' in person); // false
Он возвращает true или false, в зависимости от того, удалось ли найти требуемое поле.
true
Чтобы определить в объекте свойство, имя которого лежит в переменной, используйте синтаксис с квадратными скобками:
const dynamic = 'flavour'; var item = { name: 'Biscuit', [dynamic]: 'Chocolate' } console.log(item); // { name: 'Biscuit', flavour: 'Chocolate' }
Точно таким же образом вы можете обратиться к этому свойству объекта:
const keyName = 'name'; console.log(item[keyName]); // returns 'Biscuit'
Динамическая установка и доступ очень удобны, если имя свойства определяется только в рантайме (например, берется из пользовательского ввода).
Вы точно знаете, что при деструктуризации переменной ее свойства могут быть переименованы с помощью синтаксиса oldKey: newKey. Но знаете ли вы, что это можно сделать даже в том случае, если вы точно не знаете, как называется свойство объекта (например, если оно устанавливается динамически).
Вот простой пример деструктуризации с использованием алиасов (переименованием свойств):
const person = { id: 1, name: 'John Doe' }; const { name: personName } = person; console.log(personName); // 'John Doe'
При деструктуризации объекта person значение его свойства name записывается в новую переменную personName.
name
personName
А теперь представим, что имя свойства устанавливается динамически. Чтобы извлечь его значение при деструктуризации, нужно воспользоваться квадратными скобками, точно так же, как мы делали в предыдущем примере:
const templates = { 'hello': 'Hello there', 'bye': 'Good bye' }; const templateName = 'bye'; const { [templateName]: template } = templates; console.log(template) // 'Good bye'
При деструктуризации будет создана переменная template, в которую запишется значение свойства templates.bye.
template
templates.bye
Nullish coalescing operator ?? (оператор слияния с null) полезен, когда вы хотите проверить, равно ли некоторое значение null или undefined. Если это так, будет возвращено выражение с правой стороны оператора, если же значение ненулевое, то вернется оно само.
??
null
undefined
// 1 const foo = null ?? 'Hello'; console.log(foo); // 'Hello' // 2 const bar = 'Not null' ?? 'Hello'; console.log(bar); // 'Not null' // 3 const baz = 0 ?? 'Hello'; console.log(baz); // 0
Обратите внимание на третий пример – возвращается 0, а не строка с правой стороны оператора. 0 является falsy-значением в JavaScript (при логических преобразованиях превращается в false), однако оператор нулевого слияния это не волнует, он проверяет только null и undefined.
0
const cannotBeZero = 0 || 5; console.log(cannotBeZero); // 5 const canBeZero = 0 ?? 5; console.log(canBeZero); // 0
В этом примере вы четко видна разница между оператором логического сложения ||, который использует логические преобразования, и оператором ??, который этого не делает.
||
Вас тоже ужасно раздражают ошибки доступа к свойствам несуществующего объекта, вроде TypeError: Cannot read property 'foo' of null? Каждый JavaScript-разработчик с ними сталкивался.
TypeError: Cannot read property 'foo' of null
const book = { id:1, title: 'Title', author: null }; // обычно мы делаем так console.log(book.author.age) // и получаем ошибку доступа к свойству, т.к. book.author == null // добавляем проверку на первом уровне console.log(book.author && book.author.age); // возвращает null, ошибок нет
Для элегантного решения этой проблемы предназначен оператор опциональной последовательности.
// обращаемся к свойству с помощью optional chaining console.log(book.author?.age); // возвращает undefined // более глубокий доступ console.log(book.author?.address?.city); // возвращает undefined
Прежде всего синтаксис стал более кратким. Также обратите внимание на возвращаемое значение. При использовании для проверки оператора && мы получаем значение первого операнда – book.author, которое равно null. А оператор ?. возвращает undefined, что более логично.
book.author
?.
Опциональные последовательности можно использовать даже для вызова методов:
const person = { firstName: 'Haseeb', lastName: 'Anwar', printName: function () { return `${this.firstName} ${this.lastName}`; }, }; console.log(person.printName()); // возвращает 'Haseeb Anwar' console.log(persone.doesNotExist?.()); // возвращает undefined
Если метод не найдется, вернется undefined.
Для быстрой конвертации любого значения в булево (true/false) вы можете использовать двойной оператор логического отрицания !!.
!!
const greeting = 'Hello there!'; console.log(!!greeting) // true const noGreeting = ''; console.log(!!noGreeting); // false
Важно: обязательно нужно провести двойное отрицание, так как унарное вернет противоположное значение.
Помимо неявных булевых преобразований (см. предыдущий пример), в JavaScript существуют также хаки для неявных преобразований в строку или число.
Например, унарный оператор + превратит значение строкового типа в числовое (если сможет, конечно):
+
const stringNumer = '123'; console.log(+stringNumer); // 123 console.log(typeof +stringNumer); // 'number'
Для обратного преобразования также нужен оператор +, на этот раз стандартный бинарный:
const myString = 25 + ''; console.log(myString); // returns '25' console.log(typeof myString); // returns 'string'
Если при сложении оператору попадает операнд строкового типа, то результат тоже будет строкой.
Важно: такие неявные преобразования типов очень удобны, но могут плохо читаться. Не жертвуйте читаемостью кода ради его краткости, особенно если вы работаете в команде.
В JS есть несколько отличных методов для перебора массивов – filter, some и every. Они позволяют использовать разнообразные коллбэки для оценки значений элементов. Однако иногда требуется самая простая проверка – является ли значение истинным (truthy) или ложным (falsy).
Для этого прекрасно подойдет функция конвертации типа Boolean:
Boolean
const myArray = [null, false, 'Hello', undefined, 0]; // фильтруем falsy-Значения const filtered = myArray.filter(Boolean); console.log(filtered); // ['Hello'] // проверяем, если ли хоть одно truthy-Значение const anyTruthy = myArray.some(Boolean); console.log(anyTruthy); // true // проверяем, являются ли все значения truthy const allTruthy = myArray.every(Boolean); console.log(allTruthy); // false
Обратите внимание на краткую запись. Привычнее было бы написать вот так:
myArray.filter(val => Boolean(val));
Однако это ровно то же самое. Boolean выступает в роли коллбэк-функции, которая будет вызвана для каждого элемента массива. Она принимает лишь один аргумент – им и будет элемент массива.
Важно: будьте внимательны с такими краткими записями. В коллбэк-функцию передается не один параметр, а два или три (в зависимости от метода). Если ваша функция принимает больше, чем один аргумент, вы можете столкнуться с неожиданными эффектами, например:
const myArray = ['1', '7', '11'] const mappedArray = myArray.map(parseInt); // [1, NaN, 3]
Метод Array.prototype.flat – свежее приобретение JavaScript. Он позволяет раскрывать вложенные массивы, поднимая их содержимое на уровень родительского.
const myArray = [{ id: 1 }, [{ id: 2 }], [{ id: 3 }]]; const flattedArray = myArray.flat(); // [ { id: 1 }, { id: 2 }, { id: 3 } ]
Он даже может работать на несколько уровней в глубину – для этого нужно передать в метод соответствующий параметр:
const arr = [0, 1, 2, [[[3, 4]]]]; console.log(arr.flat(2)); // [0, 1, 2, [3,4]]
***
Пользуетесь этими приемами? Нашли что-то новое для себя? Делитесь своими любимыми JavaScript-трюками в комментариях.
Ваш адрес email не будет опубликован. Обязательные поля помечены *
Сохранить моё имя, email и адрес сайта в этом браузере для последующих моих комментариев.
Δ
Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.