Тест: Прелести деструктуризации
Вы мастер деструктуризации?
Или это ваша Симфония разрушения?
Этот тест проверит ваши знания деструктуризации в JavaScript: от «базового» синтаксиса объектов до вложенной деструктуризации и значений по умолчанию. Плюс бонусные вопросы по TypeScript и inline‑типам!
Перейдите сразу к разогреву — продемонстрируйте свои навыки деструктуризации! 👇
Что выведет этот код?
const person = { name: 'Dan Levy', location: 'Cape Town',};const { name, age } = person;console.log(`Name: ${name}, Age: ${age}`);Свойство age отсутствует в объекте person, поэтому age будет undefined. Точно не Infinity 😅
В результате получаем:
Name: Dan Levy, Age: undefinedЧто сделает этот код?
const person = [ 'Dan Levy', 'Cape Town' ];const [ name, origin, age ] = person;console.log(`Name: ${name}, Age: ${age}`);В массиве person нет третьего элемента, поэтому переменная age получает undefined.
Это приводит к:
Name: Dan Levy, Age: undefinedКак насчёт вложенной деструктуризации?
'use strict';const person = { name: { first: 'Dan' }, address: { city: 'Denver' },};const { name: { first }, address: { city }, birth: { place },} = person;console.log( `First: ${first}, City: ${place}`,);Свойство birth: { place } отсутствует у person, поэтому будет выброшена ошибка.
Одно из решений — задать значения по умолчанию для вложенных свойств.
При доступе к вложенным свойствам будьте осторожны — ошибки могут быть трудно заметны. Текст сообщений об ошибках различается в разных браузерах и платформах, что усложняет отладку.
В современном Chrome: TypeError: Cannot read properties of undefined (reading 'place')
В Node это тоже TypeError, потому что JavaScript пытается деструктурировать place из undefined, прежде чем place будет прочитан.
Точная формулировка зависит от браузера и среды выполнения.
Теперь с некоторыми значениями по умолчанию, что это сделает?
'use strict';const person = { name: { first: 'Dan' }, address: { city: 'Denver' },};const { name: { first = 'Unknown' }, birth: { place = 'Unknown' },} = person;console.log( `Hi ${first} from ${place}`,);Свойство birth отсутствует, поэтому вложенная деструктуризация пытается прочитать place из undefined. Значение по умолчанию для place не помогает: сначала должен существовать содержащий его объект.
TypeErrorФормулировка сообщения зависит от среды выполнения. Чтобы подставить пустой объект при отсутствии birth, используйте birth: { place = "Unknown" } = {}.
Что это сделает?
const person = { name: { first: 'Dan' }, address: { city: 'Denver' },};const { name: { first = 'Unknown' }, birth: { place = 'Unknown' } = {},} = person;
console.log( `Hi ${first} from ${place}`,);Свойство birth отсутствует в объекте person, поэтому используется пустой объект = {}. Это позволяет применить значение по умолчанию.
А теперь в параметрах функции, что это сделает?
'use strict';function displayUser({ name = "Unknown", age = -1,} = { place: "Unknown" }) { console.log(`Hi ${name} from ${place}`);}displayUser({ name: "Dan" });Эта функция извлекает свойства name и age, используя значения по умолчанию при необходимости. В данном случае ключ place в объекте по умолчанию — просто шум, он не используется внутри displayUser().
Строгий режим не меняет ситуацию: попытка чтения необъявленной привязки place бросает ReferenceError.
Как обрабатываются значения undefined?
'use strict';function displayPlace({ name = "N/A", place = "N/A", age = -1,} = { place: "Unknown" }) { console.log(`${place}`);}displayPlace({ name: "Dan" });displayPlace({ name: "Dan", place: undefined });displayPlace({ name: "Dan", place: "Joburg" });Объект параметра по умолчанию { place: "Unknown" } используется, если аргумент пропущен или явно равен undefined: и displayPlace(), и displayPlace(undefined) выводят Unknown.
Если объект передан, отсутствующее или равное undefined свойство place получает значение "N/A". Значения по умолчанию параметра и свойства работают на разных уровнях.
displayPlace() // UnknowndisplayPlace({ name: "Dan" }) // N/AdisplayPlace({ name: "Dan", place: undefined }) // N/AПохожим образом на предыдущий пример… как обрабатывается null?
function displayPlace({ name = "N/A", place = "N/A", age = -1,} = { place: "Unknown" }) { console.log(`${place}`);}displayPlace({ name: "Dan", place: null });displayPlace({ name: "Dan", place: undefined });В первом вызове place равно null, во втором — undefined. Значение свойства по умолчанию "N/A" применяется, когда place отсутствует или равно undefined, даже если сам объект передан. null не запускает подстановку, поэтому выводятся null и N/A.
Теперь TypeScript
Теперь в TypeScript… что это сделает?
'use strict';function displayPlace( { name = 'N/A', place = 'N/A', }: { name: string; place: string; age: number; },) { console.log(`${place}`);}displayPlace({ name: 'Dan', place: null });TypeScript сообщает об ошибке, потому что place имеет тип string, а вызов передаёт null. Кроме того, вызов опускает обязательное свойство age.
Если игнорировать ошибки типов, выполнение кода выведет null в консоль.
Попробуем немного переименования/присваивания…
'use strict';function displayPlace({ name = 'N/A', place: location = 'N/A',}: { name: string; place: string; age?: number;}) { console.log(`${location}`);}displayPlace({ name: 'Dan', place: 'Denver' });Это выведет Denver в консоль. Свойство place переименовано в location в сигнатуре функции. Это распространённый приём (переименование свойств при деструктуризации) при адаптации сторонних структур данных.
Найдите ошибку типов:
function greet({ name: {first = "N/A", last = "N/A"}, birth: {place = "N/A"} = {}, age = -1,}: { name: {first?: string, last?: string}; birth: {place?: string}; age: number;}) { console.log(`Hi ${first} ${last} from ${place}`);}greet({ name: {first: 'Dan'} });Ошибка находится в сигнатуре функции greet. Свойства age и birth отсутствуют в переданном объекте, поэтому они должны быть опциональными в определении типа.
Хотя свойство birth деструктурируется с значением по умолчанию, определение типа требует его присутствия. Чтобы пометить свойство как опциональное в TypeScript, следует использовать оператор ?.
Обратите внимание, что birth?: { place?: string } не то же самое, что birth: { place?: string } | undefined.
Теперь с присваиванием (обратите внимание на переменные f, l и p)
'use strict';function greet( { name: {first: f = "N/A", last: l = "N/A"}, birth: {place: p = "N/A"} = {}, age = -1, }: { name: {first?: string, last?: string}; birth?: {place?: string}; age?: number; }) { console.log(`Hi ${f} ${l} from ${place}`); // What will 👆 do?}greet({ name: {first: 'Dan', last: 'Levy'}, birth: {place: 'Cape Town'},});Еще одна ошибка! Вы начинаете угадывать, не так ли?!
Трудно читать слои деструктуризации с дефолтами, присваиванием и типами!
Как только place переназначается в переменную p, она больше не определена в области видимости оператора console.log.
console.log(`Hi ${f} ${l} from ${place}`); // ❌// to:console.log(`Hi ${f} ${l} from ${p}`); // ✅Продолжайте тренироваться: ещё викторины.
