اختبار: مسرات التفكيك
هل أنت ماهر في التفكيك؟
أم هي سمفونية التدمير الخاصة بك؟
سيختبر هذا الاختبار معرفتك بـ 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 غير موجودة في 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().
الوضع المتشدد لا يغير هذا: قراءة الربط 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() // 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 في الثاني. القيمة الافتراضية لـ 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}`); // ✅