تخطَّ إلى المحتوى

التفكيك والنشر في JavaScript: Destructuring و Spread

الدرس 18 من 28 · JavaScript

افتح أي كود JavaScript حديث وستجد أقواساً معقوفة على يسار علامة =، وثلاث نقاط ... في كل مكان. هاتان هما التفكيك (Destructuring) والنشر (Spread)، وهما من أكثر مزايا اللغة الحديثة استخداماً. لا يضيفان قدرة جديدة، لكنهما يجعلان الكود أقصر وأوضح بكثير. وفهمهما شرط لقراءة أي مشروع حقيقي.

رأيت لمحة منه في درس الكائنات. بدلاً من هذا:

const user = { name: "سارة", age: 14, city: "طنطا" };
const userName = user.name;
const userAge = user.age;

نكتب سطراً واحداً:

const { name, age } = user;
console.log(name, age);
الناتج
سارة 14

الأقواس المعقوفة على يسار علامة = معناها: «أخرج من الكائن الخاصيتين name و age، وضعهما في متغيرين بنفس الاسم».

تخيّل التفكيك مثل فتح حقيبة السفر: بدلاً من أن تمد يدك إلى الحقيبة كل مرة، تخرج ما تحتاجه مرة واحدة وتضعه على الطاولة.

تغيير الاسم والقيمة الافتراضية

Section titled “تغيير الاسم والقيمة الافتراضية”
const { city: town, country = "مصر" } = user;
console.log(town);
console.log(country);
الناتج
طنطا
مصر
  • city: town: خذ الخاصية city وضعها في متغير اسمه town.
  • country = "مصر": إذا لم تكن الخاصية موجودة في الكائن فاستخدم هذه القيمة.

التفكيك في معاملات الدالة

Section titled “التفكيك في معاملات الدالة”

هذا أكثر استخدام ستراه. بدلاً من أن تستقبل الدالة كائناً ثم تكتب options.title و options.size في كل مكان:

function showProduct({ title, price, currency = "جنيه" }) {
console.log(`${title}: ${price} ${currency}`);
}
showProduct({ title: "كتاب", price: 80 });
showProduct({ price: 15, title: "قلم", currency: "ريال" });
الناتج
كتاب: 80 جنيه
قلم: 15 ريال

فائدة مهمة: ترتيب القيم عند الاستدعاء لم يعد مهماً، لأن كل قيمة لها اسمها. وهذا أوضح كثيراً من دالة تأخذ خمسة معاملات لا تتذكر ترتيبها.

مع المصفوفات نستخدم الأقواس المربعة، والتوزيع يكون بالترتيب لا بالاسم:

const colors = ["أحمر", "أخضر", "أزرق"];
const [first, second] = colors;
console.log(first, second);
const [, , third] = colors;
console.log(third);
الناتج
أحمر أخضر
أزرق

الفاصلة الفارغة تتخطى عنصراً. ومن الحيل اللطيفة تبديل قيمتي متغيرين بدون متغير ثالث:

let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b);
الناتج
2 1

معامل النشر ... يفك محتويات مصفوفة أو كائن، كأنك كتبت عناصرها واحداً واحداً.

تخيّله مثل إفراغ علبة أقلام على المكتب: كانت الأقلام شيئاً واحداً داخل العلبة، وصارت قطعاً منفصلة.

const morning = ["فطور", "مذاكرة"];
const evening = ["رياضة", "قراءة"];
const day = [...morning, "غداء", ...evening];
console.log(day);
const copy = [...morning];
copy.push("نوم");
console.log(morning.length, copy.length);
الناتج
[ 'فطور', 'مذاكرة', 'غداء', 'رياضة', 'قراءة' ]
2 3

لاحظ أن الإضافة إلى copy لم تغيّر morning، لأن [...morning] أنشأت مصفوفة جديدة.

const settings = { theme: "light", fontSize: 16 };
const updated = { ...settings, theme: "dark" };
console.log(updated);
console.log(settings.theme);
الناتج
{ theme: 'dark', fontSize: 16 }
light

{ ...settings, theme: "dark" } معناها: «انسخ كل خصائص settings، ثم اجعل theme تساوي dark». الخاصية المكتوبة لاحقاً تغلب السابقة. والكائن الأصلي لم يتغير.

هذا النمط، أي إنشاء نسخة معدّلة بدلاً من تعديل الأصل، هو أساس العمل في مكتبات الواجهات الحديثة مثل React.

نشر مصفوفة كمعاملات دالة

Section titled “نشر مصفوفة كمعاملات دالة”
const grades = [70, 95, 82];
console.log(Math.max(...grades));
الناتج
95

Math.max تنتظر أرقاماً منفصلة لا مصفوفة، والنشر يفك المصفوفة إلى معاملات.

نفس النقاط الثلاث، لكن بمعنى معاكس. إذا جاءت على يسار علامة = أو في تعريف دالة، فهي تجمع ما بقي:

const [winner, ...others] = ["منى", "عمر", "هدى", "علي"];
console.log(winner);
console.log(others);
const { theme, ...rest } = { theme: "dark", fontSize: 16, lang: "ar" };
console.log(rest);
الناتج
منى
[ 'عمر', 'هدى', 'علي' ]
{ fontSize: 16, lang: 'ar' }

وفي الدوال تجمع أي عدد من المعاملات في مصفوفة:

function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2));
console.log(sum(5, 10, 15, 20));
الناتج
3
50
النشر (Spread) الباقي (Rest)
ماذا يفعل يفك مجموعة إلى عناصر يجمع عناصر في مجموعة
أين يُكتب عند إنشاء مصفوفة أو كائن، أو استدعاء دالة عند التفكيك، أو في تعريف الدالة
جرّب بنفسكJavaScript
أو اضغط Ctrl + Enter

الناتج

جرّب أن تضيف خاصية color إلى discounted، ثم اطبع firstItem وتأكد أنه لم يتغير.

  • تفكيك خاصية غير موجودة بدون قيمة افتراضية: تحصل على undefined بهدوء. أضف قيمة افتراضية إذا كانت الخاصية اختيارية.
  • الظن أن النشر ينسخ نسخاً عميقاً: النشر ينسخ المستوى الأول فقط. إذا كان داخل الكائن كائن آخر، فالنسخة والأصل يتشاركانه.
  • نسيان ترتيب النشر في الكائنات: { theme: "dark", ...settings } تجعل قيمة settings هي الغالبة، وهو عكس ما تريد غالباً.

سؤال

ما قيمة x بعد السطر: const { x = 5 } = { y: 2 }; ؟

سؤال

كيف تنشئ نسخة مستقلة من مصفوفة arr؟

  • التفكيك يستخرج قيماً من كائن بالاسم، ومن مصفوفة بالترتيب.
  • يمكن تغيير الاسم بـ : وتحديد قيمة افتراضية بـ =.
  • التفكيك في معاملات الدالة يجعل الاستدعاء أوضح ولا يعتمد على الترتيب.
  • النشر ... ينسخ ويدمج المصفوفات والكائنات بدون تعديل الأصل.
  • معامل rest يجمع الباقي في مصفوفة أو كائن.