در این درس میخوانید
یک پروفایل فقط یک نام نیست؛ شناسه، تنظیمات و پیشرفت نیز دارد. شیء این دادههای مرتبط را زیر یک ساختار جمع میکند. پیش از ساختن ساختار، معنای هر ویژگی را روشن کنید: آیا نبود شماره تلفن با رشتهٔ خالی نشان داده میشود یا null؟ آیا completed تعداد است یا وضعیت؟ در درس هشتم، پروفایل یادگیرنده را میسازیم و تغییر میدهیم، بدون اینکه نسخهٔ قبلی را ناخواسته خراب کنیم.
ویژگی ثابت و کلید پویا
Object literal با آکولاد ساخته میشود و زوجهای کلید و مقدار دارد. برای ویژگی با نام مشخص میتوانید از نقطه استفاده کنید؛ وقتی نام ویژگی از یک متغیر میآید، براکت لازم است. خواندن ویژگی موجودنبودن معمولاً undefined میدهد، اما این با وجود ویژگیِ دارای مقدار undefined یکسان نیست. Object.hasOwn برای بررسی ویژگی متعلق به خود شیء مفید است؛ operator in میتواند زنجیرهٔ prototype را هم بررسی کند.
const learner = { id: 7, name: "مینا", completed: 3 };
const key = "completed";
console.log(learner.name); // مینا
console.log(learner[key]); // 3
console.log(Object.hasOwn(learner, "name")); // true
console.log(learner.phone); // undefinedDestructuring و نامهای روشنتر
با destructuring میتوانید چند ویژگی را به متغیرهای مستقل استخراج کنید. نام تازه با نشانهٔ دونقطه مشخص میشود؛ مثلاً name: learnerName. مقدار پیشفرض فقط در حالت undefined اعمال میشود و null را جایگزین نمیکند. destructuring نیز خودش کپی عمیق نمیسازد؛ اگر یک شیء تودرتو را استخراج کنید، همچنان میتواند همان مرجع قبلی باشد. هنگام دریافت ورودی اختیاری، قبل از destructuring به null یا undefined بودن خود ورودی نیز فکر کنید.
const profile = { name: "آرمان", lessons: 5 };
const { name: learnerName, lessons = 0, theme = "light" } = profile;
console.log(learnerName, lessons, theme); // آرمان 5 light
const { nickname = "مهمان" } = { nickname: null };
console.log(nickname); // nullSpread؛ کپی سطحی و ترتیب بازنویسی
در ساختار {...old, changed: value} ویژگیهای قبلی خوانده میشوند و مقدار تازه جای ویژگی همنام را میگیرد. ترتیب مهم است؛ اگر spread را بعد از ویژگی تازه بگذارید، دادهٔ قدیمی میتواند تغییرتان را بازنویسی کند. این روش فقط سطح اول را جدا میکند. برای ویرایش settings.theme باید خود settings را نیز تازه بسازید. این الگو در مدیریت وضعیت رابط کاربرد زیادی دارد و کمک میکند نسخهٔ قبل برای مقایسه و بازگشت قابل اعتماد بماند.
const before = {
name: "سارا",
settings: { theme: "light", fontSize: 16 }
};
const after = {
...before,
settings: { ...before.settings, theme: "dark" }
};
console.log(before.settings.theme); // light
console.log(after.settings.theme); // dark
console.log(before.settings === after.settings); // falseاگر فقط {...before} بنویسید و بعد settings.theme را تغییر دهید، تنظیمات مشترکِ هر دو نسخه ممکن است عوض شود. برای بعضی دادههای قابل کپی، structuredClone کپی عمیقتری میسازد، اما همهٔ انواع مقدار مانند تابعها قابل انتقال با آن نیستند. تبدیل رفتوبرگشتی JSON نیز روش عمومیِ کپی همهچیز نیست؛ تاریخ، undefined و دادههای دیگر محدودیت دارند. روش کپی را بر اساس شکل واقعی داده و قرارداد برنامه انتخاب کنید.
Optional Chaining جای اعتبارسنجی را نمیگیرد
با ?. میتوانید دسترسی به مسیری را که ممکن است null یا undefined باشد، کوتاه کنید. در آن حالت نتیجه undefined میشود و با ?? میتوانید مقدار پیشفرض بدهید. این ابزار وجود واقعی یک دادهٔ ضروری را تضمین نمیکند. اگر API طبق قرارداد باید شناسهٔ کاربر را برگرداند، تبدیل همهٔ خطاها به رشتهٔ خالی میتواند مشکل سرور را پنهان کند. از پیشفرض برای دادهٔ اختیاری استفاده کنید و کمبود دادهٔ ضروری را جدا گزارش کنید.
const response = { learner: { name: "نیما" } };
const city = response.learner?.address?.city ?? "ثبت نشده";
console.log(city); // ثبت نشده
console.log(response.learner?.name); // نیماتمرین: ویرایش پیشرفت بدون تغییر نسخهٔ قبل
شیئی شامل نام و progress بسازید. در progress، مقدار completed را 2 و total را 20 قرار دهید. نسخهٔ تازه باید completed برابر 3 داشته باشد و نسخهٔ اولیه همچنان 2 بماند. سپس در هر دو نسخه، total را مقایسه کنید. هدف، ساختن نسخهٔ تازه در مسیر تغییر است؛ لازم نیست تمام دادهٔ برنامه را بیدلیل کپی کنید. پاسخ پیشنهادی زیر فقط سطحهایی را بازسازی میکند که تغییر کردهاند.
const original = {
name: "مریم",
progress: { completed: 2, total: 20 }
};
const updated = {
...original,
progress: { ...original.progress, completed: original.progress.completed + 1 }
};
console.log(original.progress.completed); // 2
console.log(updated.progress.completed); // 3
console.log(updated.progress.total); // 20اشتباههای رایج
- نوشتن obj.key وقتی نام ویژگی داخل متغیر key قرار دارد.
- انتظار جایگزینی null با مقدار پیشفرض destructuring.
- اشتباهگرفتن spread با کپی عمیق.
- گذاشتن spread قدیمی بعد از ویژگی تازه و بازنویسی ناخواستهٔ تغییر.
- پنهانکردن نبود دادهٔ ضروری با زنجیرهای از ?. و پیشفرضها.
برای مرور، یک ویژگی به نسخهٔ تازه اضافه کنید، نام کاربر را با destructuring استخراج کنید و تفاوت Object.hasOwn با صرفاً خواندن مقدار را توضیح دهید. اگر دادهٔ API را وارد شیء میکنید، کلیدهای مجاز را مشخص کنید؛ ادغام بیقاعدهٔ تمام ورودی کاربر موضوعی است که در درس امنیت به آن بازمیگردیم.
منابع انگلیسی
- MDN — Working with objects
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_objects
- MDN — Destructuring
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring
- MDN — Spread syntax
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
مثالها و توضیح فارسی برای این مجموعه تدوین شدهاند. در درس بعد، رابطهٔ this، prototype و class را با یک مدل سادهٔ پیشرفت میخوانیم.
پیشنیاز و محیط اجرای مثالها
درس 8 از ۲۰؛ پیشنیاز: درسهای قبلی مجموعه. کدهای مستقل این درس در Console تازهٔ مرورگر یا فایل JavaScript اجرا میشوند؛ نمونهها را جداگانه آزمایش کنید.
پیشرفت روی همین مرورگر ذخیره میشود.