تفاوت رفتار اشیاء با مقادیر Primitive
مقادیر اولیه (مثل number، string، boolean) در جاوااسکریپت به صورت مستقیم در متغیر ذخیره میشوند. هر زمان که یک مقدار
اولیه را به متغیر دیگری انتساب میدهید، در واقع یک کپی کاملاً مستقل از آن مقدار ساخته میشود. اما اشیاء از
نوع ارجاعی (reference) هستند. وقتی یک شیء را به متغیر دیگری تخصیص میدهید، فقط «ارجاع» یا اشاره به آن شیء
منتقل میشود؛ پس هر دو متغیر دقیقاً به یک محل حافظه اشاره میکنند.
بنابراین، هر تغییر روی یکی از متغیرها روی دیگری هم اثر میگذارد.
این تفاوت باعث میشود که رفتار عملیاتهایی مثل تخصیص، مقایسه و کپیبرداری برای اشیاء و نوعهای اولیه کاملاً
متفاوت باشد. هر یک از این رفتارها را به طور جداگانه با مثال بررسی میکنیم.
تخصیص (Assignment)
تخصیص یک مقدار اولیه به متغیر جدید یعنی ایجاد یک کپی کاملاً مستقل. اما در مورد اشیاء، فقط ارجاع به محل حافظه
شیء کپی میشود. بنابراین هر تغییری روی یکی، روی دیگری هم اثر میگذارد.
JAVASCRIPT
let a = 10;
let b = a;
b = 20;
console.log(a);
console.log(b);
let user1 = {name: "Ali"};
let user2 = user1;
user2.name = "Sara";
console.log(user1.name);
console.log(user2.name);
در این مثال، تغییر مقدار b روی a اثر ندارد
(کپی مستقل). اما هر تغییری روی user2 دقیقاً روی user1 هم اثر دارد چون هر دو به یک شیء مشترک
اشاره دارند.
مقایسه (Comparison)
مقایسه مقادیر اولیه با عملگر === یا == براساس مقدار
واقعی انجام میشود. اما مقایسه اشیاء همیشه بر اساس ارجاع است؛ یعنی اگر و فقط اگر هر دو متغیر به یک شیء واحد
(یک محل حافظه) اشاره کنند نتیجه true است. در غیر این صورت، حتی اگر پراپرتیهای هر
دو شیء کاملاً یگسان باشد، حاصل مقایسه false خواهد بود.
JAVASCRIPT
let n1 = 42;
let n2 = 42;
console.log(n1 === n2);
let user1 = {name: "Ali"};
let user2 = {name: "Ali"};
console.log(user1 === user2);
let user3 = user1;
console.log(user1 === user3);
همانطور که میبینید، مقایسه دو مقدار اولیه با مقدار یکسان نتیجه true میدهد، اما مقایسه دو شیء مستقل،
حتی اگر پراپرتیهایشان یکی باشد همیشه false است، چون به محلهای مختلفی در حافظه اشاره دارند. فقط
زمانی که هر دو
متغیر به یک شیء واحد اشاره کنند، نتیجه مقایسه true خواهد بود.
کپی کردن (Cloning)
دیدیم که وقتی یک شیء را به متغیر دیگر اختصاص میدهید، فقط ارجاع (reference) منتقل میشود و شیء جدید ساخته
نمیشود.
اگر میخواهید یک شیء جدید کاملاً مستقل بسازید، باید عملیات cloning انجام دهید. کپیبرداری (clone) به دو شکل
کپی سطحی (shallow) و کپی عمیق (deep) انجام میشود.
کپی سطحی با Object.assign
متد Object.assign() یک کپی سطحی از شیء ایجاد میکند. این یعنی فقط لایه اول شیء
کپی میشود و
اگر شیء شامل اشیاء تو در تو باشد، ارجاع به آنها کپی میشود نه خود آنها. بنابراین تغییرات در لایههای درونی
روی شیء اصلی تأثیر میگذارد.
JAVASCRIPT
let person = {
name: "Reza",
address: { city: "Tehran" }
};
let clone = Object.assign({}, person);
clone.address.city = "Tabriz";
console.log(person.address.city);
در این مثال، تغییر در clone.address.city باعث تغییر در person.address.city هم میشود چون هر دو
به
همان شیء داخلی address اشاره دارند. برای کپی عمیق باید از روشهای دیگری استفاده کنیم.
کپی عمیق با structuredClone
متد structuredClone() در جاوااسکریپت مدرن، همهی لایههای شیء را به صورت
بازگشتی (recursive) و
کاملاً مستقل کپی میکند. مثال زیر را ببینید.
JAVASCRIPT
let person = {
name: "Sara",
address: { city: "Shiraz" }
};
let deepClone = structuredClone(person);
deepClone.address.city = "Isfahan";
console.log(person.address.city);
structuredClone() یک روش امن و مدرن برای کپی عمیق اشیاء در جاوااسکریپت است. این
متد به
شما این امکان را میدهد که بدون نگرانی از تغییرات ناخواسته در اشیاء اصلی، نسخههای مستقل ایجاد کنید.
تفاوت رفتار ارجاعی اشیاء با مقادیر اولیه، پایهی طراحی کدهای امن و بدون باگ در جاوااسکریپت است. همیشه هنگام
تخصیص، مقایسه و کپی اشیاء به ارجاعی بودن آنها دقت کنید و برای کپی مستقل، از روشهای امن cloning بهره ببرید.
در درس بعد با مفهوم متدها و کاربرد کلیدواژه this در اشیاء آشنا میشویم.