آموزش کاملاً کاربردی، از ساده تا حرفهای + مثالهای واقعی پروژهای
مقدمه
یکی از مهمترین و حیاتیترین مفاهیمی که یک دانشجوی جاوااسکریپت باید عمیقاً یاد بگیرد، تفاوت دادههای Primitive و Reference است.
۹۰٪ باگهای تازهکارها و حتی برنامهنویسان سطح متوسط، دقیقاً از همین تفاوت ایجاد میشود.
مثلاً چرا این کد رفتار عجیبی دارد؟
const user1 = { name: "Ali" };
const user2 = user1;
user2.name = "Sara";
console.log(user1.name); // 😱 چرا شد "Sara" ؟!
اگر جواب را دقیق نمیدانید، این مقاله شما را نجات میدهد.
در این آموزش یاد میگیرید:
• Primitive و Reference دقیقاً چه هستند
• در حافظه چطور ذخیره میشوند
• چرا تغییر یک شیء، روی دیگری اثر میگذارد
• چطور کپی درست بگیریم (Shallow و Deep Copy)
• مثالهای واقعی: فرم، API، سبد خرید، UI، LocalStorage
• اشتباهات رایج و روش حرفهای کار با دادهها
تا آخر مقاله، این موضوع را لمس میکنید.
بخش ۱: قبل از هر چیز، Primitive چیست؟
Primitive یعنی دادهای که:
• مقدار مستقیم دارد
• کوچک و سبک است
• در Stack ذخیره میشود
• همیشه با مقدار کپی میشود (BY VALUE)
Primitiveها:
• string
• number
• boolean
• undefined
• null
• symbol
• bigint
بخش ۲: Reference چیست؟
Reference یعنی دادهای که:
• خودش مقدار نیست
• فقط آدرس یک داده را نگه میدارد
• داده اصلی در Heap ذخیره میشود
• هنگام نسبت دادن، آدرس کپی میشود (BY REFERENCE)
Referenceها:
• object
• array
• function
• date
• map / set
• regex
• هر نوع ساختار پیچیده
بخش ۳: فرق از نگاه حافظه (کاملاً تصویری و قابل لمس)
Primitive در حافظه
let a = 10;
let b = a;
b = 20;
معنی واقعی:
a → 10
b → 10 (کپی مقدار)
بعد b → 20 میشود ولی a دستنخورده باقی میماند.
خروجی:
a = 10
b = 20
Reference در حافظه
const obj1 = { name: "Ali" };
const obj2 = obj1; // آدرس مشترک
obj2.name = "Sara";
در حقیقت:
obj1 → [Address: X]
obj2 → [Address: X]
و شیء اصلی داخل Heap است.
پس تغییر یکی یعنی تغییر دیگری.
بخش ۴: اولین مثال واقعی (فرم ثبتنام)
فرض کنید داده کاربر را ذخیره میکنید:
const user = {
name: "",
age: 0
};
function register(u) {
u.name = "Ali";
u.age = 19;
}
register(user);
نتیجه:
user = { name: "Ali", age: 19 }
چرا؟
چون تابع به جای مقدار، آدرس شیء را دریافت کرده.
این در پروژههای واقعی خیلی مهم است.
بخش ۵: مشکل بزرگ Reference – وقتی فکر میکنید کپی گرفتهاید ولی نگرفتهاید
مثال اشتباه بسیار رایج:
const original = { score: 10 };
const copy = original;
copy.score = 90;
console.log(original.score); // 90 😱
دانشجویان همیشه شوکه میشوند.
بخش ۶: آیا آرایهها هم همین رفتار را دارند؟ بله!
const list1 = [1, 2, 3];
const list2 = list1;
list2.push(4);
console.log(list1); // [1,2,3,4] 😳
این دقیقاً Reference Behavior است.
بخش ۷: Shallow Copy – کپی سطحی (تا حدی مفید)
روشهای کپی سطحی:
۱. اسپرد (Spread)
const copy = { ...original };
۲. Object.assign
const copy = Object.assign({}, original);
۳. آرایه:
const copyArr = [...list];
اما مشکل کپی سطحی:
اگر شیء تو در تو باشد، باز هم Reference میماند.
const user = {
name: "Ali",
address: {
city: "Tehran"
}
};
const copy = { ...user };
copy.address.city = "Shiraz";
console.log(user.address.city); // Shiraz 😱
بخش ۸: Deep Copy – کپی واقعی و عمیق
بهترین روشهای ۲۰۲۶:
۱. structuredClone (جدید و عالی)
const copy = structuredClone(user);
۲. JSON روش کلاسیک
const copy = JSON.parse(JSON.stringify(user));
(محدودیت دارد ولی کاربردی است)
بخش ۹: پروژه واقعی – مدیریت سبد خرید فروشگاه
در سبد خرید، یکی از رایجترین باگها:
const cart = [];
const product = { id: 1, price: 200 };
cart.push(product);
const copy = product;
copy.price = 300;
کاربر قیمت محصول را در UI دستکاری میکند:
cart[0].price → 300 😱
برای رفع این مشکل:
cart.push(structuredClone(product));
بخش ۱۰: پروژه واقعی – مدیریت فرم در React / Vue / Angular
اگر با Reference اشتباه رفتار کنید، کامپوننتها بهطور ناخواسته رندر میشوند.
کد اشتباه:
setUser(newUser); // newUser با reference به قبلی
راهحل:
setUser({ ...newUser });
بخش ۱۱: مثال بسیار مهم – دستکاری ناخواسته شیء در تابع
اشتباه رایج:
function updateProduct(p) {
p.price = 900;
}
updateProduct(product); // محصول اصلی خراب شد
راهحل:
function updateProduct(p) {
const copy = { ...p };
copy.price = 900;
return copy;
}
بخش ۱۲: Primitive ها هیچوقت این مشکل را ندارند
function changeAge(age) {
age = 100;
}
let a = 20;
changeAge(a);
console.log(a); // 20
چون Primitive → مقدار کپی میشود.
بخش ۱۳: مثال بزرگتر – مدیریت لیست Todo
const todos = [
{ id: 1, text: "Learn JS", done: false }
];
function complete(todo) {
todo.done = true;
}
complete(todos[0]);
مشکل: داده اصلی تغییر میکند.
راهحل حرفهای:
function complete(todo) {
return { ...todo, done: true };
}
todos[0] = complete(todos[0]);
بخش ۱۴: Reference در ساختارهای پیچیدهتر
Map
const map = new Map();
map.set("user", { name: "Sara" });
const u = map.get("user");
u.name = "Aysan";
console.log(map.get("user").name); // Aysan
Set
const set = new Set();
const obj = { a: 1 };
set.add(obj);
const x = obj;
x.a = 99;
console.log([...set][0].a); // 99
بخش ۱۵: اشتباهات رایج دانشجوها
❌ فکر میکنند = یعنی کپی واقعی
❌ فکر میکنند Spread همیشه Deep Copy است
❌ نمیدانند توابع Reference را تغییر میدهند
❌ در JSON و LocalStorage کپی درست نمیگیرند
❌ در React/Vue با Reference ناخواسته UI خراب میکنند
بخش ۱۶: روش حرفهای مدیریت Reference
✓ همیشه برای ارسال به تابع، کپی بسازید
✓ اگر داده تو در تو است → Deep Copy
✓ برای UI → همیشه Immutable رفتار کنید
✓ هرگز Reference را بدون آگاهی پاس ندهید
✓ در React/Vue حتماً از ساختارهای جدید بسازید
بخش ۱۷: تمرین پروژهای – ساخت سیستم مدیریت کاربر
const user = {
name: "Ali",
address: {
city: "Tehran"
}
};
function setCity(obj, city) {
const u = structuredClone(obj);
u.address.city = city;
return u;
}
const updated = setCity(user, "Shiraz");
console.log(user.address.city); // Tehran
console.log(updated.address.city); // Shiraz
این تمرین را چند بار با دادههای مختلف تکرار کنید.
نتیجهگیری
در این مقاله یاد گرفتید:
• Primitive کپی مقدار است
• Reference کپی آدرس است
• Reference در پروژهها خطرناک است
• کپی سطحی همیشه کافی نیست
• کپی عمیق بهترین روش برای دادههای پیچیده است
• در پروژههایی مثل فروشگاه، Todo، فرم، UI، API → Reference بسیار مهم است
اگر این را دقیق یاد گرفته باشید، پایهی ذهنی شما برای ادامهی مسیر جاوااسکریپت بسیار قوی میشود.