الفرق بين النسخ المرجعي, النسخ السطحي والنسخ العميق

Mohanad Alrwaihy

June 1, 2023

181

0

هناك العديد من الطرق لنسخ المتغيرات في JavaScript ولكن هناك ثلاثة أنواع رئيسية من النسخ التي يجب معرفتها قبل النسخ لفهم سلوك البيانات في التطبيق الخاص بك.

5 دقائق للقراءة

هناك أنواع مختلفة من أنواع البيانات في لغات البرمجة وفي JavaScript ، قد تواجه بعض المشاكل في فهم الفرق بين المتغيرات البدائية وغير البدائية حيث يتم استخدام بعض المتغيرات كـ قيم وغيرها كـ مرجع إلى الذاكرة لكي يتم تخزين المعلومات فيها.

الفرق الرئيسي بين هذين النوعين هو عندما نحاول نسخ المعلومات وتحديثها.

  • البدائي (Primitive) - يتم نسخ المعلومات وتحديثها بسهولة في متغيرات أخرى.
  • غير بدائي (Non-primitive) - تحديث المعلومات في كائن سهل ولكن إذا أردنا نسخ كائن في متغير آخر ، سننسخ مرجع موقع الكائن في الذاكرة بدل معلومات الكائن نفسه وستنعكس أي تغييرات على المتغير الجديد على المتغير الأصلي.

سنناقش اليوم أنواع نسخ البيانات غير البدائية إلى متغيرات أخرى ومعرفة طرق النسخ المختلفة المستخدمة في JavaScript لنسخ المعلومات.

هناك طرق متعددة لنسخ البيانات في JavaScript ، لكننا نريد مناقشة الأنواع المختلفة من النسخ وكيف يمكننا استخدام كل منها.

نسخ مرجعي

النسخة المرجعية هي عندما يشير متغيران إلى نفس موقع الذاكرة ، مما يعني أن أي تغييرات على المتغير المنسوخ ستغير المتغير الأصلي أيضًا.

التعيين ( let b = a )

طريقة التعيين هيا الطريقة الأسهل والأوضح وهيا جدا مفيدة ولكن ليس دائمًا.

الطريقة

يمكن أن تكون طريقة التعيين مفيدة عند نسخ القيم البدائية لأنها ستقوم بنسخ القيمة بدلاً من المرجع.

قائمة القيم البدائية في JavaScript:

  1. number
  2. string
  3. boolean
  4. null
  5. undefined
  6. symbol
  7. bigint

تأتي المشكلة عند محاولة نسخ أنواع بيانات أخرى مثل كائن (Object) أو مصفوفة (Array) أو Map أو أنواع بيانات JavaScript الغير بدائية الأخرى.

الSyntax

JAVASCRIPT
let number = 5;
let copyNumber = number

copyNumber = copyNumber + number // 5 + 5 = 10

// number => 5
// copyNumber => 10

قام copyNumber بنسخ قيمة متغير number وعند محاولة تغيير قيمة copyNumber ، سيتم ذلك بدون أي تغييرات في المتغير الأصلي.

JAVASCRIPT
let person = {id: 1, name: 'Mohanad'}
let copyPerson = person // Will copy object reference

copyPerson.id = 5
copyPerson.name = 'Othman'

// console.log(person)     // {id: 5, name: 'Othman'}
// console.log(copyPerson) // {id: 5, name: 'Othman'}

عند محاولة تنفيذ نفس الطريقة مع الكائنات ، سنلاحظ مشكلة عند محاولة الكتابة فوق الكائن المنسوخ ( copyPerson )

النسخ المرجعي يمكن أن يكون مفيدًا عند محاولة تغيير اسم المتغير للوصول إليه في مكان آخر حيث يمكن أن يكون له معنى أكبر أو مفهوم أفضل داخل وظيفة أو فئة أخرى ولكن لا يفضل لأنه يؤدي إلى الارتباك في فهم الكود.

نسخ سطحي

تعتبر معظم طرق النسخ في JavaScript نسخ سطحية.

النسخة السطحية مفيدة لنسخ الكائنات التي تحتوي على قيم بدائية في السطح لأنها ستنشئ كائنًا آخر وتنسخ جميع القيم و المراجع من هذا الكائن إلى كائن جديد.

طريقة الSpread Operator (...)

إحدى أسهل الطرق لنسخ كائن في JavaScript أو أي نوع من أنواع البيانات القابلة للتكرار هي مع ال (...) Spread Operator لتوسيعه داخل كائن آخر أو مصفوفة (Array).

الطريقة

سيقوم الSpread Operator بنشر جميع المعلومات داخل الكائن في كائن جديد يقع في موقع ذاكرة آخر بمفاتيح وقيم جديدة.

إذا كانت القيم الرئيسية بدائية فستكون جديدة تمامًا ولكن إذا كانت كائن أو Array أو أي قيمة غير بدائية فستكون القيمة المخزنة في الكائن الجديد مرجع لنفس موقع الذاكرة في الكائن الرئيسي ( ستساعد الأمثلة أدناه ).

الSyntax

لنأخذ المثال الأخير وننسخ كائن الشخص باستخدام Spread Operator 👇

JAVASCRIPT
let person = { id: 1, name: 'Mohanad' }
let shallowCopyPerson = { ...person } // Will copy Object values including references

shallowCopyPerson.id = 5
shallowCopyPerson.name = 'Othman'

// console.log(person)            // {id: 1, name: 'Mohanad'}
// console.log(shallowCopyPerson) // {id: 5, name: "Othman"}

قيم الكائن المنسوخ مختلفة عن الأصل ولا تشير إلى موقع الذاكرة ولهذا السبب عندما قمنا بتعديل القيم الموجودة بداخله ، لا يزال كائن الشخص الأصلي يحمل القيم القديمة, على عكس طريقة النسخ المرجعي.

لكن النسخ السطحي لا يعتبر نسخ عميق بمعنى إذا كانت هناك قيمة غير بدائية للمفاتيح سينسخ القيمة المرجعية 👇

JAVASCRIPT
let person = { id: 1, name: 'Mohanad', hobbies: ['Video Games'] }
let shallowCopyPerson = { ...person } // Will copy Object values including references

shallowCopyPerson.id = 5
shallowCopyPerson.name = 'Othman'
shallowCopyPerson.hobbies.push('Walking')
// console.log(person) // {id: 1, name: 'Mohanad', hobbies: ['Video Games', 'Walking']}
// console.log(shallowCopyPerson) // {id: 5, name: "Othman", hobbies: ['Video Games', 'Walking']}

لقد أضفنا مصفوفة للهوايات داخل كائن الشخص واضفنا قيمة واحدة فقط وعندما حاولنا اضافة هواية اخرى الى الكائن المنسوخ لاحظنا أن المصفوفة في المتغير الأصلي تغيرت ايضا وتمت اضافة الهواية الجديدة إليه!

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

نسخ عميق

هذه هي اكثر عملية مكلفة من بين طرق النسخ الثلاثة لأنها ستقوم بنسخ خصائص كائن واحدًا تلو الآخر ولكن الفرق بين النسخ عميق و النسخ السطحي هو أن النسخ العميق يستدعي نفسه بشكل متكرر عندما يجد بيانات غير بدائية (القيمة المرجعية) لإنشاء نسخة جديدة منه أيضًا.

الطريقة الأكثر شهرة في JavaScript لعمل نسخ عميق لكائن هوا عن طريق استخدام الJSON Hack والذي هوا عبارة عن استخدام طريقتين لتحويل البيانات إلى JSON وهيا JSON.parse() و JSON.stringify()

الطريقة

ستأخذ طريقة JSON.stringify() النسخة الأصلية من الكائن وتحولها إلى سلسلة JSON و JSON.parse() ستحول القمية من سلسلة JSON مرة أخرى إلى كائن.

الSyntax

دعنا نستخدم المثال الأخير وننسخ كائن الشخص باستخدام طريقة JSON Hack 👇

JAVASCRIPT
let person = { id: 1, name: 'Mohanad', hobbies: ['Video Games'] }
let shallowCopyPerson = { ...person } // Will copy Object values including references
let deepCopyPerson = JSON.parse(JSON.stringify(person)) // Will deep clone the same person object.
shallowCopyPerson.id = 5 shallowCopyPerson.name = 'Othman'
shallowCopyPerson.hobbies.push('Walking')
// console.log(person) // {id: 1, name: 'Mohanad', hobbies: ['Video Games', 'Walking']} // console.log(shallowCopyPerson) // {id: 5, name: "Othman", hobbies: ['Video Games', 'Walking']}
// console.log(deepCopyPerson) // {id: 1, name: 'Mohanad', hobbies: ['Video Games']}

الJSON Hack يعمل ولدينا متغير منسوخ بالكامل ويشبه تمامًا نفس الكائن الأصلي ولكن جميع المراجع فيه مختلفة.

وظيفة جديدة - structuredClone()

وظيفة الstructuredClone( ) جديدة نسبيًا على الويب وتحتاج إلى التحقق من أن لديك على Node.js الإصدار 18 على الأقل لكي تعمل.

الطريقة

يقوم بإنشاء نسخ عميق لقيمة معينة باستخدام خوارزمية الاستنساخ المنظمة .

الSyntax

JAVASCRIPT
const deepClone = structuredClone(person)

ال structuredClone() أسهل في الاستخدام من الJSON Hack و أداءه أفضل للكائنات الكبيرة.