آموزش ارجاع اشیاء و کپی کردن اشیاء

در این بخش از آموزش جاوا اسکریپت، نحوه کپی کردن اشیاء، مقایسه آنها از طریق reference و همچنین nested cloning با استفاده از مثال های کاربردی توضیح داده می شود.

آموزش ارجاع اشیاء و کپی کردن اشیاء

ارجاع اشیاء (object reference) و کپی کردن اشیاء

یکی از تفاوت های اساسی اشیاء در مقابل انواع دیگر این است که اشیاء «با مرجع» (reference) ذخیره و کپی می شوند، در حالی که انواع اولیه: رشته ها، اعداد، بولی ها و غیره همیشه «به عنوان یک مقدار کامل» کپی می شوند.

درک این موضوع آسان است اگر کمی به این موضوع نگاه کنیم که هنگام کپی کردن یک مقدار چه اتفاقی می افتد.

بیایید با یک چیز بدوی مانند یک رشته شروع کنیم.

در اینجا یک کپی از پیام را در عبارت قرار می دهیم:

ارجاعات اشیاء (object references) و کپی کردن آنها

در نتیجه ما دو متغیر مستقل داریم که هر کدام رشته "Hello!" را ذخیره می کنند.

Object references and copying in javascript

نتیجه کاملا واضح است، درست است؟ اما اشیاء اینطور نیستند.

متغیری که به یک شیء اختصاص داده می شود، خود شیء را دخیره نمی کند، بلکه «آدرس آن در حافظه»، به عبارت دیگر «یک مرجع» به آن را ذخیره می کند.

بیایید به مثالی از چنین متغیری نگاه کنیم:

Object references and copying in javascript

و در اینجا نحوه ذخیره آن در حافظه نشان داده شده است:

Object references and copying in javascript

شیء در جایی در حافظه ذخیره می شود (در سمت راست تصویر)، در حالی که متغیر user (در سمت چپ) یک "مرجع" به آن دارد.

ممکن است یک متغیر شی، مانند user، را مانند یک برگه کاغذ با آدرس شیء روی آن تصور کنیم.

وقتی اعمالی را با شیء انجام می دهیم، به عنوان مثال. یک user.name را در نظر بگیرید، موتور جاوا اسکریپت به آنچه در آن آدرس است نگاه می کند و عملیات را روی شیء واقعی انجام می دهد.

به این نکته مهم توجه کنید که هنگامی که یک متغیر شیء کپی می شود، مرجع آن کپی می شود، اما خود شیء کپی نمی شود.

برای مثال:

Object references and copying in javascript

اکنون دو متغیر داریم که هر کدام مرجعی به یک شیء یکسان ذخیره می کنند:

اشیاء در جاوا اسکریپت و کپی کردن آنها

همانطور که می بینید، هنوز یک شیء وجود دارد، اما اکنون با دو متغیر که به آن ارجاع می دهند.

برای دسترسی به شیء و تغییر محتوای آن می توانیم از هر یک از متغیرها استفاده کنیم:

Object references and copying in javascript

مانند آن است که یک کابینت با دو کلید داشتیم و برای ورود به آن و ایجاد تغییرات از یکی از آنها (admin) استفاده کردیم. سپس، اگر بعداً از کلید دیگری (user) استفاده کنیم، همچنان در حال باز کردن همان کابینت هستیم و می توانیم به محتویات تغییر یافته قبلی دسترسی داشته باشیم.

مقایسه اشیاء از طریق مرجع

دو شیء فقط در صورتی مساوی هستند که یک شیء باشند.

به عنوان مثال، در اینجا a و b به یک شیء اشاره می کنند، بنابراین آنها برابر هستند:

مقایسه اشیاء از طریق مرجع در جاوا اسکریپت


و در اینجا دو شیء مستقل با هم برابر نیستند، اگرچه هر دو شبیه هم هستند (هر دو خالی هستند):

مقایسه اشیاء از طریق مرجع

برای مقایسه هایی مانند obj1 > obj2 یا برای مقایسه با یک obj == 5 ، اشیاء به انواع اولیه تبدیل می شوند. در بخش های بعدی نحوه عملکرد تبدیل شیء را مطالعه خواهیم کرد، اما در حقیقت، چنین مقایسه هایی به ندرت مورد نیاز است - معمولاً در نتیجه یک اشتباه برنامه نویسی ظاهر می شوند.

cloning و ادغام، Object.assign

بنابراین، کپی کردن یک متغیر شی یک مرجع بیشتر به همان شیء ایجاد می کند.

اما اگر بخواهیم یک شیء را کپی کنیم چه می کنیم؟ یک کپی مستقل، یک clone ایجاد می کنید؟

این عمل نیز قابل انجام است، اما کمی دشوارتر است، زیرا هیچ متد داخلی برای آن در جاوا اسکریپت وجود ندارد. اما به ندرت نیاز به این کار وجود دارد - کپی کردن با مرجع در بیشتر مواقع خوب است.

اما اگر واقعاً به آن نیاز داریم، پس باید یک شیء جدید ایجاد کنیم و ساختار شیء موجود را با تکرار ویژگی های آن و کپی کردن آنها در سطح اولیه تکرار کنیم.

مانند مثال زیر:

cloning و ادغام، Object.assign

همچنین می توانیم از متد Object.assign برای انجام آن استفاده کنیم.

سینتکس آن عبارت است از:

آموزش جاوااسکریپت

*** اولین آرگومان dest شیء هدف است.

*** آرگومان های دیگر src1، ...، srcN (به هر تعدادی که نیاز است) اشیاء منبع (source objects) هستند.

*** ویژگی های تمام اشیاء منبع src1، ...، srcN را در هدف dest کپی می کند. به عبارت دیگر، ویژگی های همه آرگومان هایی که از دومی شروع می شوند در شیء اول کپی می شوند.

*** فراخوانی، dest را بر می گرداند.

به عنوان مثال، ما می توانیم از این مفهوم برای ادغام چندین شیء در یک شیء استفاده کنیم:

آموزش جاوااسکریپت

اگر نام ویژگی کپی شده از قبل وجود داشته باشد، بازنویسی می شود:

آموزش مقایسه و کپی کردن اشیاء جاوااسکریپت

همچنین ما می توانیم ازObject.assign برای جایگزینی با حلقه for..in جهت شبیه سازی (cloning) استفاده کنیم.

آموزش مقایسه و کپی کردن اشیاء جاوااسکریپت

این تمام ویژگی های user را در شیء خالی کپی می کند و آن را برمی گرداند.

همچنین متد های دیگری برای شبیه سازی یک شیء وجود دارد، به عنوان مثال با استفاده از کلون syntax spread = {...user}، که در ادامه در آموزش توضیح داده می شود.

شبیه سازی تو در تو (Nested cloning)

تا به حال ما فرض می کردیم که تمام ویژگی های user از نوع اولیه یا همان primitive هستند. اما ویژگی ها می توانند ارجاعاتی به اشیاء دیگر باشند. با اینها چیکار کنیم؟

مانند مثال زیر:

شبیه سازی تو در تو (Nested cloning)

اکنون کپی کردن clone.sizes = user.sizes کافی نیست، زیرا user.sizes یک شیء است که با reference کپی می شود. بنابراین clone و user اندازه های یکسانی دارند:

مانند مثال زیر:

شبیه سازی تو در تو (Nested cloning)

برای رفع این مشکل، باید از یک حلقه شبیه سازی (cloning loop) استفاده کنیم که هر مقدار user[key] را بررسی می کند و اگر یک شیء است، ساختار آن را نیز تکرار می کند. که به آن "کلونینگ عمیق" (deep cloning) می گویند.

برای پیاده سازی آن می توانیم از بازگشتی (recursion) استفاده کنیم. یا برای اینکه چرخ را دوباره اختراع نکنید، یک پیاده سازی که اکنون موجود است را انتخاب کنید، به عنوان مثال _.cloneDeep(obj) از کتابخانه جاوا اسکریپت lodash.

اشیاء Const را می توان تغییر داد!

یک اثر جانبی مهم ذخیره اشیاء به عنوان مرجع این است که یک شیء تعریف شده به عنوان const قابل تغییر است.

برای مثال:

اشیاء Const را می توان تغییر داد!

ممکن است به نظر برسد که خط (*) باعث خطا می شود، اما اینطور نیست. مقدارuser ثابت است، همیشه باید به همان شیء ارجاع دهد، اما خصوصیات آن شی آزاد هستند که تغییر کنند.

به عبارت دیگر، const user تنها در صورتی خطا می دهد که بخواهیم ...=user را به طور کلی ست کنیم.

گفته می شود، اگر واقعاً نیاز به ایجاد ویژگی های شیء به صورت constant داشته باشیم، این امکان نیز وجود دارد، اما با استفاده از متدهای کاملاً متفاوت.

خلاصه مطالب

اشیاء با مرجع یا همان reference تخصیص و کپی می شوند. به عبارت دیگر، یک متغیر نه "مقدار شی"، بلکه یک "مرجع" (آدرس در حافظه) را برای مقدار ذخیره می کند. بنابراین کپی کردن چنین متغیری یا ارسال آن به عنوان آرگومان تابع، آن مرجع را کپی می کند، نه خود شیء را.

تمام عملیات از طریق reference های کپی شده (مانند افزودن یا حذف ویژگی ها) روی یک شیء واحد انجام می شود.

برای ایجاد یک "کپی واقعی" (یک کلون) می توانیم از Object.assign برای به اصطلاح "کپی کم عمق" (اشیاء تودرتو با مرجع کپی می شوند) یا یک تابع "کلونینگ عمیق" مانند _.cloneDeep(obj) استفاده کنیم.

Object references and copying مقایسه اشیاء کپی اشیاء Nested cloning آموزش جاوا اسکریپت

مقالات این دسته بندی