مقدمة: لماذا لا يعمل النسخ العادي؟
\n\nتخيل أنك تكتب تطبيقًا يعالج إعدادات مستخدم معقدة. تقوم بـ const newConfig = oldConfig، وتغير بعض الحقول — وفجأة يتغير الكائن الأصلي أيضًا. ألم مألوف؟ 👋
في JavaScript، يتم تمرير الكائنات بالمرجع. هذا يعني أن عملية التعيين البسيطة لا تنشئ كائنًا جديدًا، بل تنسخ المؤشر إلى الكائن القديم. ومن هنا يأتي السؤال الرئيسي الذي يؤرق آلاف المطورين على Stack Overflow: "ما هي الطريقة الأكثر كفاءة لعمل نسخة عميقة من كائن في JavaScript؟".
\n\nفي هذه المقالة، سنستعرض جميع الطرق الشائعة للنسخ العميق، مميزاتها، عيوبها، وأداءها. ستتعرف على الطريقة المناسبة لعام 2024، وستتعلم كتابة دالة deep clone موثوقة خاصة بك.
\n\nلماذا نتحدث عن النسخ العميق؟
\n\nالنسخ العميق (deep clone) ينشئ نسخة مستقلة تمامًا من الكائن بجميع الهياكل المتداخلة. على عكس النسخ السطحي (shallow copy)، حيث تبقى الكائنات المتداخلة مشتركة.
\n\nإليك سيناريو نموذجي تحتاج فيه إلى ذلك:
\n- \n
- العمل مع Redux / Zustand: لا يمكن تغيير الحالة، بل يجب إنشاء نسخ. \n
- تسلسل البيانات لإرسالها إلى الخادم. \n
- العمل مع ذاكرة التخزين المؤقت أو الإعدادات التي لا يمكن تعديلها. \n
- اختبارات الوحدة: لضمان عدم التأثير على البيانات الأصلية. \n
لكن الأمر ليس بهذه البساطة. إذا حاولت نسخ كائن يحتوي على دوال، أو undefined، أو Symbol، أو مراجع دائرية — فإن العديد من الطرق ستفشل أو تفقد البيانات. دعنا نستعرض كل طريقة بالتفصيل.
الطريقة 1: JSON.parse(JSON.stringify(obj)) — قديمة ولكنها فعالة
\n\nهذه هي الطريقة الأكثر شيوعًا بين المبتدئين. إنها بسيطة وسريعة الكتابة:
\n\nconst original = {\n name: "آنا",\n age: 25,\n address: { city: "موسكو", zip: 101000 },\n hobbies: ["قراءة", "جري"]\n};\n\nconst clone = JSON.parse(JSON.stringify(original));\n\nclone.address.city = "سانت بطرسبرغ";\nconsole.log(original.address.city); // "موسكو" — ✅ النسخة مستقلة!\n\n\nالمميزات:
\n- \n
- 🔥 بناء جملة بسيط جدًا. \n
- 🔥 تعمل في معظم الحالات البسيطة. \n
العيوب (حرجة!):
\n- \n
- ❌ تفقد الدوال — ستتحول إلى
undefined. \n - ❌ تفقد undefined — الحقول التي قيمتها
undefinedستختفي. \n - ❌ تفقد Symbol — المفاتيح من نوع Symbol ستختفي. \n
- ❌ لا تعمل مع المراجع الدائرية — ستظهر خطأ
TypeError. \n - ❌ مشاكل مع التواريخ —
Dateستتحول إلى نص. \n - ❌ مشاكل مع Map, Set, RegExp — ستتحول إلى كائنات فارغة. \n
الخلاصة: مناسبة فقط للبيانات القابلة للتسلسل إلى JSON بشكل مضمون (نصوص، أرقام، مصفوفات، كائنات بسيطة).
\n\nالطريقة 2: Structured Clone API — المعيار الحديث (2024)
\n\nابتداءً من عام 2022، ظهرت في JavaScript طريقة أصلية للنسخ العميق — structuredClone. وهي متاحة في جميع المتصفحات الحديثة و Node.js 17+.
const clone = structuredClone(original);\n\nclone.address.city = "قازان";\nconsole.log(original.address.city); // "موسكو" — ✅\n\n\nما يمكن لـ structuredClone فعله:
\n- \n
- ✅ المراجع الدائرية — تعمل! \n
- ✅ التواريخ — تحافظ على نوع
Date. \n - ✅ Map و Set — تنسخ بشكل صحيح. \n
- ✅ RegExp — تعمل أيضًا. \n
- ✅ ArrayBuffer و Ty \n