همونطور که میدونین در زبانهای برنامهنویسی مقادیر در متغیرها ذخیره میشن، وقتی از یه متغیر کپی میگیریم در واقع داریم یه متغیر جدید ایجاد میکنیم که مقدارش برابر مقدار متغیر اصلی هست و انتظارمون اینه که با تغییر در مقدار کپی، تغییری در مقدار اصلی ایجاد نشه .
اما این انتظاری که داریم اغلب اتفاق نمیافته، چرا؟ قبل از اینکه توضیح بدم چرا، بهتره که با انواع کپی آشنا بشیم.
در این نوع کپی با تغییر مقدار کپی شده، هیچ تغییری در مقدار اصلی ایجاد نمیشه. این حالت در Primitive Data Types اتفاق میافته.
در مثال بالا یک کپی از a به اسم b ایجاد کردیم که با تغییر در مقدار b، مقدار متغیر a هیچ تغییری نمیکنه.
این نوع کپی در Objects و Arrays اتفاق میافته و به این صورت که وقتی از یک Object/Array کپی میگیریم هر تغییری در کپی جدید، باعث تغییر در مقدار Object/Array اصلی میشه.
علتش اینه که هر متغیر یه فضایی رو در حافظه اشغال میکنه که اون فضا یک آدرس مشخصی داره؛ وقتی از Object/Array کپی میگیریم درواقع اون کپی جدید هم به همون فضا اشاره میکنه و آدرسش برابر با همون آدرس متغیر اصلیه، برای همین هر تغییر در متغیر کپی شده باعث تغییر در متغیر اصلی میشه.
برای اینکه بهتر تعریف بالا رو درک کنیم مثال پایین رو باهم بررسی میکنیم.
در کد بالا، هر تغییر در Object کپی شده (newUser) باعث تغییر در آبجکت اصلی (user) میشه.
در ادامه راهحلهای موجود که از این اتفاق جلوگیری میکنه رو باهم بررسی میکنیم اما قبلش بیایم با دو مفهوم Shallow copy و Deep copy آشنا بشیم.
کپی کم عمق به این معنی هست که، مقادیر زیرمجموعه (sub-values) هنوز به مقادیر اصلی متصل هستن.
کپی عمیق به این معنی هست که، تمام مقادیر متغیر جدید از متغیر اصلی کپی و جدا میشه.
این عملگر در ES6 معرفی شده که از لحاظ سینتکس کوتاه و سادست و همهی مقادیر رو در یک Object جدید منتشر میکنه.
قبل از معرفی 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، میتونیم به راحتی یک Deep copy ایجاد کنیم:
از نظر فنی Arrays شبیه Objects هستن پس مثل هم رفتار میکنن و همون روشهایی که برای Objects بکار بردیم برای Arrays هم بکار میره.
این عملگر همهی مقادیر رو در یک Array جدید منتشر میکنه.
در این متد یک Array جدید تعریف شده و تغییرات مورد نیاز در این Array جدید ذخیره میشه. که در این صورت، آرایه اصلی دستنخورده باقی میمونه.
این متد مجموعهای از عناصر انتخابی رو برمیگردونه. که با (0)slice یه کپی از آرایه ایجاد میشه.
هر سه روش بالا اگه برای کپی کردن Arrays تو در تو (Nested Arrays) استفاده شه بازم Shallow copy اتفاق میافته که برای اینکه Deep copy ایجاد بشه از JSON.stringify استفاده میکنیم.
ممنونم که تا انتهای مقاله همراهم بودید، امیدوارم براتون مفید واقع شده باشه : )
منابع:
How to differentiate between deep and shallow copies in JavaScript