Лучшие советы и уловки JavaScript
По факту эти техники сохранят вам кучу времени.
Возможно большинство из них вам уже знакомо, возможно некоторые нет. Но в любом случае, они будут вам крайне полезны.
Это переводы трех статей, которые действительно помогут вам писать лучше, быстрее и продуктивнее.
Очистка и сокращение массива
Вот простой способ очистки или сокращения массива его переназначения. Вы можете просто поменять его length
свойство:
const arr = [11, 22, 33, 44, 55, 66];
// Сокращение
arr.length = 3;
console.log(arr); //=> [11, 22, 33]
// Очистка
arr.length = 0;
console.log(arr); //=> []
console.log(arr[2]); //=> undefined
Имитация параметров с помощью деструктуризации
Велик шанс того, что вы уже используете конфигурацию объектов, когда вам надо передать переменный набор аргументов для функции, например как тут:
doSomething({ foo: 'Hello', bar: 'Hey!', baz: 42 });
function doSomething(config) {
const foo = config.foo !== undefined ? config.foo : 'Hi';
const bar = config.bar !== undefined ? config.bar : 'Yo!';
const baz = config.baz !== undefined ? config.baz : 13; // ...}
Это старый, но эффективный паттерн, который пытается имитировать именованные параметры в JavaScript. Вызов функции выглядит хорошо. С другой стороны, объект конфигурация содержит логику, которая занимает ну уж слишком много писанины. А вот помощью ES2015 деструктуризации, вы можете обойти этот недостаток:
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 }) { // ...}
И если вам нужно сделать конфиг объекта опциональным, но это тоже очень просто:
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 } = {}) { // ...}
Деструктуризация объекта для элементов массива
Назначайте элементы массива индивидуальным переменным с помощью деструктуризации объектов:
const csvFileLine = '1997,John Doe,US,john@doe.com,New York';
const { 2: country, 4: state } = csvFileLine.split(',');
Асинхронные функции с async/await
Да, есть возможно обождать множественную асинхронную функцию, используя Promise.all
:
await Promise.all([anAsyncCall(), thisIsAlsoAsync(), oneMore()])
Создание пустых объектов
Вы можете создать 100% пустой объект, который не будет наследовать какие-либо свойства или методы из изначального объекта (например, constructor
, toString()
и так далее).
const pureObject = Object.create(null);
console.log(pureObject); //=> {}
console.log(pureObject.constructor); //=> undefined
console.log(pureObject.toString); //=> undefined
console.log(pureObject.hasOwnProperty); //=> undefined
Форматирование кода JSON
JSON.stringify
может не просто конвертировать объект в строку. А так же может сделать красивую выдачу JSON:
const obj = {
foo: { bar: [11, 22, 33, 44], baz: { bing: true, boom: ‘Hello’ } }
};
// Третий параметр это чисто пробелов, используемое для украшения JSON вывода
JSON.stringify(obj, null, 4);
// =>”{
// => “foo”: {
// => “bar”: [
// => 11,
// => 22,
// => 33,
// => 44
// => ],
// => “baz”: {
// => “bing”: true,
// => “boom”: “Hello”
// => }
// => }
// =>}”
Удаление дублирующихся элементов из массива
Используя сеты ES2015 вместе с оператором расширения, вы легко можете удалить дублирующиеся элементы из массива:
const removeDuplicateItems = arr => […new Set(arr)];
removeDuplicateItems([42, 'foo', 42, 'foo', true, true]);
//=> [42, "foo", true]
Слияние многоуровневых массивов
Вообще слияние многоуровневых массивов довольно тривиально с помощью оператора расширения:
const arr = [11, [22, 33], [44, 55], 66];
const flatArr = [].concat(…arr); //=> [11, 22, 33, 44, 55, 66]
К сожалению, трюк выше сработает только на двухуровневых массивах. Но с рекурсивными запросами, мы можем применить это для массивов с более чем двумя уровнями:
function flattenArray(arr) {
const flattened = [].concat(...arr);
return flattened.some(item => Array.isArray(item)) ? flattenArray(flattened) : flattened
;}
const arr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
const flatArr = flattenArray(arr);
//=> [11, 22, 33, 44, 55, 66, 77, 88, 99]
Перетасовка переменных
Используйте деструктуризацию массивов для перетасовки переменных. Тут все будет понятно из кода:
let a = 'world', b = 'hello'
[a, b] = [b, a]
console.log(a) // -> hello
console.log(b) // -> world
// Да, это магия, детка!
Деструктуризация с async/await
И снова, деструктуризация массивов просто шедевральна. Вместе с async/await
она делает невообразимые вещи.
const [user, account] = await Promise.all([
fetch('/user'),
fetch('/account')
])
Дебаггинг
Для всех кто любит дебаггинг с помощью console.log
, вот вам крутой пример и да, я слышал про console.table
:
const a = 5, b = 6, c = 7
console.log({ a, b, c })
// выдаёт объект:
// {
// a: 5,
// b: 6,
// c: 7
// }
Однострочники
Синтаксис может быть гораздо компактнее для операций с массивами.
// Найти максимальное значение
const max = (arr) => Math.max(…arr);
max([123, 321, 32]) // вывод: 321
// Сумма массива
const sum = (arr) => arr.reduce((a, b) => (a + b), 0)
sum([1, 2, 3, 4]) // вывод: 10
Конкатенация(слияние) массивов
Вообще оператор расширения может использоваться вместо concat
:
const one = ['a', 'b', 'c']
const two = ['d', 'e', 'f']
const three = ['g', 'h', 'i']
// старый способ #1
const result = one.concat(two, three)
// старый способ #2
const result = [].concat(one, two, three)
// новый
const result = [...one, ...two, ...three]
Клонирование
Клонирование массивов и объектов с легкостью:
const obj = { …oldObj }
const arr = [ …oldArr ]
Именованные параметры
Создание функции и ее вызов можно сделать более читабельным с помощью деструктуризации:
const getStuffNotBad = (id, force, verbose) => {
...do stuff
}
const getStuffAwesome = ({ id, name, force, verbose }) => {
...do stuff
}
// Где то там в коде, что такое вообще true, true и к чему оно?
getStuffNotBad(150, true, true)
// А вот уже по другому... I ❤ JS!!!
getStuffAwesome({ id: 150, force: true, verbose: true })
И третья, бонусная статья аж 2013-го года. Все эти практики уже давно знакомы, но все же самые интересные из них не помешает закрепить.
Вызов метода
Я реально не люблю блоки if
/else
и это довольно полезный трюк для вызова нужной функции, основываясь на булиных значениях.
// Скукота
if (success) {
obj.start();
} else {
obj.stop();
}
// По-хипстерски
var method = (success ? 'start' : 'stop');
obj[method]();
Совмещаем строки
Известный факт, что строки подобны другим строкам. И рано или поздно вы бы захотели связать две или более из них. Мне действительно не нравится работа с плюсом, по-этому join()
приходит на помощь.
['first', 'name'].join(' '); // = 'first name';
['milk', 'coffee', 'sugar'].join(', '); // = 'milk, coffee, sugar'
Дефолтный оператор
JavaScript сам по себе не знает, что содержит объект. Иногда вы получаете его как аргумент функции, в другой раз вы можете прочесть его из сети или из файла конфигурации. В любом случае, вы можете использовать ||
оператор, чтобы использовать второй аргумент, если первый выдает false
.
// Ставим по дефолту 'No name' когда myName пуст (или null, или undefined)
var name = myName || 'No name';
// Убеждаемся, есть ли у нас объект options
var doStuff = function(options) {
options = options || {};
// ...
};
Оператор на страже точности &&
Схожий с дефолтным оператором, этот оператор супер полезен. Он исключает буквально все if
запросы и делает код куда лучше.
// Скучно
if (isThisAwesome) {
alert('yes'); // нет
}
// Отлично
isThisAwesome && alert('yes');
// Ещё один крутой подход
var aCoolFunction = undefined;
aCoolFunction && aCoolFunction(); // не запустится и не закрэшится