DanLevy.net

חידון: תענוגות הפירוק

האם אתה מומחה בדיסטרקצ'רינג?


או שזו Symphony of Destruction שלך?

החידון הזה יבחן את הידע שלך ב-Destructuring ב-JavaScript: מתחביר אובייקט “בסיסי” ועד ל-Destructuring מקונן וערכי ברירת מחדל. בתוספת שאלות בונוס על TypeScript וטיפוסים מוטבעים!

קפוץ ישר לחימום - הוכח את כישורי ה-Destructuring שלך! 👇

מה ידפיס הקוד הזה?

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}`);

המשתנה age לא קיים במערך tuple, ולכן הוא יהיה 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, לכן הוא יזרוק שגיאה. פתרון אחד הוא לספק ערכי ברירת מחדל למאפיינים מקוננים.

כאשר ניגשים למאפיינים מקוננים - היזהרו - כי השגיאות יכולות להיות קשות לאיתור. והודעות השגיאה משתנות בין דפדפנים ופלטפורמות אחרות, מה שהופך את הניפוי למאתגר יותר.

בכרום מודרני: 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 לא קיים על person, לכן האובייקט כולו עדיין צריך ברירת מחדל, לא רק המאפיין המקונן. בעצם חסרה ברירת מחדל של = {} שם.

הדרך שבה זה כתוב, אומרת “אם person.birth הוא undefined, אז place הוא Unknown”. אבל person.birth הוא undefined, אז זה מנסה לפרק undefined, מה שגורם לשגיאה.

In modern Chrome: `TypeError: Cannot read properties of undefined (reading 'place')`
In Node, this is also a `TypeError` because JavaScript tries to destructure `place` from `undefined`.
Exact wording varies between browsers and runtimes.

מה זה יעשה?

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().

מצב קפדן (strict mode) לא משנה את זה: קריאה לקישור 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" });

הפונקציה displayPlace תשתמש באובייקט ברירת מחדל רק אם לא הועבר אובייקט. לכן, הדרך היחידה לקבל את ברירת המחדל { place: "Unknown" } היא עם אפס ארגומנטים displayPlace().

התנהגות בולטת נוספת כאן היא שהעברת undefined עבור place תגרום לשימוש בערך ברירת המחדל, בדומה להתנהגות של JSON.stringify (מתעלמת מ-undefined, מזהה null).

התוצאה היא:

displayPlace() // Unknown
displayPlace({ name: "Dan" }) // N/A
displayPlace({ 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 בשנייה. ערך ברירת המחדל עבור place משמש רק אם האובייקט כולו חסר או undefined. ערכי null יגיעו כ-null.

עכשיו ב-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}`); // ✅