اختبار: مسرات التفكيك
هل أنت ماهر في التفكيك؟
أم هي سمفونية التدمير الخاصة بك؟
سيختبر هذا الاختبار معرفتك بـ Destructuring في JavaScript: من بناء الجملة «الأساسي» للكائنات إلى التفكيك المتداخل والقيم الافتراضية. بالإضافة إلى أسئلة إضافية حول 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، لذا ستؤدي إلى خطأ.
أحد الحلول هو توفير قيم افتراضية للخصائص المتداخلة.
عند الوصول إلى الخصائص المتداخلة - كن حذرًا - لأن الأخطاء قد تكون صعبة الاكتشاف. وتختلف رسائل الخطأ بين المتصفحات والمنصات الأخرى، مما يجعل التصحيح أكثر تحديًا.
في 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 مفقودة، لذلك تحاول JavaScript تفكيك place من undefined. يحدث الخطأ قبل تطبيق القيمة الافتراضية للخاصية:
TypeErrorتختلف صياغة التشخيص بين بيئات التشغيل. تعالج القيمة الافتراضية للخاصية غيابها أو قيمتها undefined بعد إتاحة الكائن الحاوي. استخدم birth: { place = "Unknown" } = {} لتوفير كائن افتراضي عندما تكون birth مفقودة أو undefined.
ماذا سيفعل هذا؟
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" });تستخدم الدالة الكائن الافتراضي عند عدم تمرير وسيطة أو عند تمرير undefined صراحة. أما في الاستدعاءات المعروضة فهناك كائن في كل مرة: غياب place أو قيمتها undefined يفعّل القيمة الافتراضية للخاصية N/A، بينما تبقى Joburg كما هي. لذا الناتج هو N/A, N/A, Joburg.
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 تُستخدم عندما تكون الخاصية مفقودة أو تساوي 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}`); // ✅هل تريد المزيد؟ زر مجموعة الاختبارات لتحديات أخرى!
