JavaScript: Четыре хитрых концепции в одной строке

Источник: «Four tricky JavaScript concepts in one line of code»
На днях коллега подошёл ко мне со строкой кода, которую нашёл на StackOverflow, и спросил, как это работает. И я подумал, что это отличный пример четырёх концепций от среднего до продвинутого уровня JavaScript, поэтому решил записать и обосновать своё объяснение.

Речь идёт о следующей строке:

const result = (({ a, c }) => ({ a, c }))({ a: 1, b: 2, c: 3, d: 4 });

Прежде чем читать дальше, подумайте и посмотрите, сможете ли вы решить это самостоятельно.

Готовы продолжить?

Деструктуризация Объекта

Документация на MDN

Деструктуризация объекта — это концепция выбора свойств из объекта в пакетном режиме вместо ручного доступа к каждому свойству и присвоения его переменной. Скажем, у вас есть объект, входящий в качестве параметра в некоторую функцию, и вы хотите что-то сделать, используя лишь некоторые из свойств этого объекта. Деструктуризация объекта делает это возможным.

Вместо того, что бы сделать так:

const a = myObject.a;
const b = myObject.b;

doStuff(a, b);

Мы можем сократить до:

const { a, b } = myObject;

doStuff(a, b);

Этот код делает то же самое, но объём кода намного меньше, что особенно важно когда мы обрабатываем несколько свойств объекта. Это избавляет нас от необходимости каждый раз писать полный путь к свойству.

Замечательно то, что такого рода деструктуризация работает везде, где у нас есть объект. Даже для параметров функции. Так

const myFunction = (myObject) => {
console.log(myObject.a);
console.log(myObject.b);
};

может быть записан как

const myFunction = ({ a, b }) => {
console.log(a);
console.log(b);
};

Сокращённая форма объектов

Документация на MDN

При составлении объектов мы часто откуда-то получаем входящие параметры, преобразуем их и возвращаем новый объект. Часто это может выглядеть так:

const someDescriptiveName = doStuff(a);
const someOtherDescriptiveName = doOtherStufF(b);

const newObject = {
someDescriptiveName: someDescriptiveName,
someOtherDescriptiveName: someOtherDescriptiveName,
};

Как вы видите, это кажется очень монотонным. Мы присваиваем свойству ключ с определённым именем с содержимым переменной с таким же именем. К счастью, есть более короткий способ записи.

const someDescriptiveName = doStuff(a);
const someOtherDescriptiveName = doOtherStufF(b);

const newObject = {
someDescriptiveName,
someOtherDescriptiveName,
};

Мы можем указать имя переменной один раз, и JavaScrip поймёт, что нам нужно свойство с тем же именем, что и переменная, значение которой мы использовали.

Неявный возврат в стрелочных функциях

Документация на MDN

Когда у стрелочной функции единственным оператором в выражении является return, можно сократить его до неявной формы — убрав return и фигурные скобки. Довольно часто мы пишем функции которые возвращают только тернарный оператор, promise или результат простого вычисления. В этом случае не нужен полны блок кода вокруг содержимого функции.

Вместо полной записи:

const multiplyByTwo = (inputNumber) => {
return inputNumber * 2;
};

Мы можем убрать return и удалить фигурные скобки (или заменить их круглыми скобками, если возвращается литерал объекта).

const multiplyByTwo = (inputNumber) => inputNumber * 2;

Совет. В Visual Studio Code вы можете поместить курсор в средину стрелочной части функции и нажать Cmd/Ctrl + ., что бы вызвать меню быстро исправления, где вы можете быстро добавить или удалить фигурные скобки из функции.

Прямой вызов анонимной функции

Документация на MDN

Это наименее используемая из четырёх концепций. И, возможно, самая запутанная. Это позволяет нам немедленно вызвать стрелочную функцию, не присваивая ей переменной.

Вместо этого:

const myLog = (text) => {
console.log('Hello ' + text);
};
myLog('world');

Мы можем вызвать напрямую, без присваивания.

((text) => {
console.log('hello ' + text);
})('world');

Это очень редко бывает нужно, но может быть полезно в некоторых ситуациях, когда вам нужно вызвать асинхронный метод в контексте, который не помечен как async

Возвращаемся к нашей запутанной строке

С этими четырьмя частями мы можем разобрать запутанную строку во что-то имеющее смысл. Если вы о ней забыли, то напомню:

const result = (({ a, c }) =>
({ a, c }))({ a: 1, b: 2, c: 3, d: 4 });

Мы начнём с конца и видим, что это стрелочная функция, которая вызывается напрямую. Давайте назначим функции переменную и вызовем её.

const myFunction = ({ a, c }) => ({ a, c });

const result = myfunction({ a: 1, b: 2, c: 3, d: 4 });

Также переместим входящий параметр в переменную, что бы сделать код чище.

const myFunction = ({ a, c }) => ({ a, c });
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myfunction(myObject);

Это уже более читабельное. Но давайте продолжим. Теперь мы обратим внимание на стрелочную функцию, где мы видим, что можно начать с добавления фигурных скобок и ключевого слова return.

const myFunction = ({ a, c }) => {
return { a, c };
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myfunction(myObject);

Следующим шагом будет удаление деструктуризации во входных параметрах функции.

const myFunction = (inputObject) => {
const a = inputObject.a;
const b = inputObject.c;

return { a, c };
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myfunction(myObject);

И последний шаг — удалить сокращённую форму объекта, возвращаемого нашей функцией.

const myFunction = (inputObject) => {
const a = inputObject.a;
const b = inputObject.c;

const newObject = {
a: a,
c: c,
};

return newObject;
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myfunction(myObject);

Мы удалили четыре "волшебных" концепции JavaScript и получили то, что требует только базовых знаний.

Когда комплексное слишком сложное?

Как и в случае с большинством подобных вопросов, у разных разработчиков и команд это будет сильно различаться. Но как для разработчика, ваш код должен быть удобочитаемым без особых усилий. Но в то же время мы не можем отказаться концепций, доступных нам в языке. Мы просто, должны знать, когда их использовать.

Я бы записал эту строку так:

const pickAC = ({ a, c }) => ({ a, c });
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = pickAC(myObject);

Это сделает его более читаемым, чем однострочный, в то же время он останется кратким и лаконичным. Немедленный вызов анонимной функции — это настолько редко используемая концепция, что на мой взгляд, её следует использовать только в случае крайней необходимости. Но каждому своё, просто убедитесь, в своём согласии с командой.

Дополнительные материалы

Предыдущая Статья

HTML: Семь интересных атрибутов

Следующая Статья

Рассмотрим Tailwind CSS