آوا باکویی
آوا باکویی
خواندن ۳ دقیقه·۵ سال پیش

تفاوت Shallow copy و Deep copy در جاوا اسکریپت

کپی چیست؟

همونطور که می‌دونین در زبان‌های برنامه‌نویسی مقادیر در متغیرها ذخیره می‌شن، وقتی از یه متغیر کپی می‌گیریم در واقع داریم یه متغیر جدید ایجاد می‌کنیم که مقدارش برابر مقدار متغیر اصلی هست و انتظارمون اینه که با تغییر در مقدار کپی، تغییری در مقدار اصلی ایجاد نشه .

اما این انتظاری که داریم اغلب اتفاق نمی‌افته، چرا؟ قبل از اینکه توضیح بدم چرا، بهتره که با انواع کپی آشنا بشیم.


کپی با مقدار (Copy by Value)

در این نوع کپی با تغییر مقدار کپی شده، هیچ تغییری در مقدار اصلی ایجاد نمی‌شه. این حالت در Primitive Data Types اتفاق می‌افته.

انواع Primitive Data Types

  • Number
  • String
  • Boolean
  • Undefined
  • Null
در مثال بالا یک کپی از a به اسم b ایجاد کردیم که با تغییر در مقدار b، مقدار متغیر a هیچ تغییری نمی‌کنه.


کپی با ارجاع (Copy by Reference)

این نوع کپی در Objects و Arrays اتفاق می‌افته و به این صورت که وقتی از یک Object/Array کپی می‌گیریم هر تغییری در کپی جدید، باعث تغییر در مقدار Object/Array اصلی می‌شه.

علتش اینه که هر متغیر یه فضایی رو در حافظه اشغال می‌کنه که اون فضا یک آدرس مشخصی داره؛ وقتی از Object/Array کپی می‌گیریم درواقع اون کپی جدید هم به همون فضا اشاره می‌کنه و آدرسش برابر با همون آدرس متغیر اصلیه، برای همین هر تغییر در متغیر کپی شده باعث تغییر در متغیر اصلی می‌شه.

برای اینکه بهتر تعریف بالا رو درک کنیم مثال پایین رو باهم بررسی می‌کنیم.

در کد بالا، هر تغییر در Object کپی شده (newUser) باعث تغییر در آبجکت اصلی (user) می‌شه.


Copy by Reference vs Copy by Value
Copy by Reference vs Copy by Value


در ادامه راه‌حل‌های موجود که از این اتفاق جلوگیری می‌کنه رو باهم بررسی می‌کنیم اما قبلش بیایم با دو مفهوم Shallow copy و Deep copy آشنا بشیم.


کپی کم عمق (Shallow copy)

کپی کم عمق به این معنی هست که، مقادیر زیرمجموعه (sub-values) هنوز به مقادیر اصلی متصل هستن.

کپی عمیق (Deep copy)

کپی عمیق به این معنی هست که، تمام مقادیر متغیر جدید از متغیر اصلی کپی و جدا می‌شه.

Shallow copy vs Deep copy
Shallow copy vs Deep copy



راه حل

عملگر Spread

این عملگر در ES6 معرفی شده که از لحاظ سینتکس کوتاه و سادست و همه‌ی مقادیر رو در یک Object جدید منتشر می‌کنه.

متد Object.assign

قبل از معرفی Spread operator از این روش استفاده می‌شد که اساسا همون کارو میکنه. اما باید حواستون باشه که در Object.assign تغییرات روی پارامتر اول که یه Object خالیه اتفاق می‌افته و همون تغییر روی پارامتر return می‌شه و اینکه Object که قراره کپی بشه باید در پارامتر دوم پاس داده شه.

روش‌هایی که بالا در موردش صحبت کردیم برای Objects ساده بکار میرن که sub-values ندارن، یعنی در Objects تو در تو (Nested Objects) که sub-values دارن تغییرات روی Object اصلی هم تاثیر میذاره که در واقع Shallow copy ایجاد میشه.

به کد زیر دقت کنید.

با توجه به نتیجه‌ای که از کد بالا گرفتیم درواقع اگه از Spread operator و Object.assign در Nested Objects به دو روش گفته شده استفاده بشه Shallow copy ایجاد میشه.

برای جلوگیری از این اتفاق از Spread operator به روش زیر استفاده می‌کنیم که در واقع با این روش یک Deep copy ایجاد کردیم.

متد JSON.stringify

به کمک متد JSON.stringify، می‌تونیم به راحتی یک Deep copy ایجاد کنیم:


آرایه ها (Arrays)

از نظر فنی Arrays شبیه Objects هستن پس مثل هم رفتار میکنن و همون روش‌هایی که برای Objects بکار بردیم برای Arrays هم بکار میره.

عملگر Spread

این عملگر همه‌ی مقادیر رو در یک Array جدید منتشر می‌کنه.

متد Map

در این متد یک Array جدید تعریف شده و تغییرات مورد نیاز در این Array جدید ذخیره می‌شه. که در این صورت، آرایه اصلی دست‌نخورده باقی می‌مونه.

متد Slice

این متد مجموعه‌ای از عناصر انتخابی رو برمیگردونه. که با (0)slice یه کپی از آرایه ایجاد میشه.

هر سه روش بالا اگه برای کپی کردن Arrays تو در تو (Nested Arrays) استفاده شه بازم Shallow copy اتفاق می‌افته که برای اینکه Deep copy ایجاد بشه از ‌JSON.stringify استفاده می‌کنیم.




ممنونم که تا انتهای مقاله همراهم بودید، امیدوارم براتون مفید واقع شده باشه : )


منابع:

How to differentiate between deep and shallow copies in JavaScript

Understanding Deep and Shallow Copy in Javascript

javascriptshallow copydeep copy
در پی یادگیری
شاید از این پست‌ها خوشتان بیاید