اشیا (Objects)

آشنایی با Object، ایجاد اشیا، ویژگی‌ها و متدها، دسترسی و تغییر ویژگی‌ها، Getters و Setters، Object Destructuring و Spread، متدهای Object.keys و values و entries و assign و freeze و seal و create، Property Descriptor و مالکیت ویژگی‌ها.

آشنایی با Object

شیء (Object) مهم‌ترین ساختار داده‌ای جاوا اسکریپت است. یک شیء مجموعه‌ای از ویژگی‌ها (Properties) است که هر کدام یک کلید (نام) و یک مقدار دارند. تقریباً همه چیز در جاوا اسکریپت — آرایه‌ها، توابع، تاریخ‌ها — از نوع شیء است.

const person = {
    name: "علی",
    age: 25,
};

console.log(typeof person); // "object"
console.log(person.name);   // "علی"
اشیا مقادیر مرجع (Reference) هستند؛ یعنی متغیر فقط آدرس شیء در حافظه را نگه می‌دارد. کپی‌کردن متغیر، شیء جدید نمی‌سازد.

ایجاد اشیا

سه روش رایج برای ساخت شیء:

// 1. Object Literal (رایج‌ترین روش):
const student = {
    firstName: "سارا",
    lastName: "احمدی",
    grade: 12,
};

// 2. سازنده Object:
const car = new Object();
car.brand = "پراید";
car.year = 1402;

// 3. Object.create — با یک نمونه‌اولیه مشخص:
const animal = Object.create(null); // بدون نمونه‌اولیه
animal.name = "شیر";

const base = { greet() { return "سلام"; } };
const cat = Object.create(base);   // cat از base ارث می‌برد
cat.name = "گربه";
console.log(cat.greet()); // "سلام"

ویژگی‌های اشیا

کلید ویژگی همیشه رشته (یا Symbol) است و مقدار می‌تواند هر نوعی باشد — حتی تابع:

const user = {
    "user name": "رضا",     // کلید با فاصله — نیازمند کوتیشن
    age: 30,
    "سطح": "حرفه‌ای",         // کلید فارسی هم مجاز است
};

console.log(user["user name"]); // "رضا"
console.log(user["سطح"]);       // "حرفه‌ای"

// کلید محاسبه‌شده (Computed):
const key = "city";
const obj = { [key]: "تهران" };
console.log(obj.city); // "تهران"
نام کلیدهای کوتاه (Shorthand): اگر متغیری با همان نام وجود دارد، می‌توانید فقط نامش را بنویسید: const obj = { name, age };

متدهای اشیا

وقتی مقدار یک ویژگی تابع باشد، به آن متد می‌گویند:

const calculator = {
    x: 0,
    y: 0,
    // متد کامل:
    sum: function (a, b) {
        return a + b;
    },
    // روش کوتاه (Method Shorthand):
    multiply(a, b) {
        return a * b;
    },
    // Arrow Function به‌عنوان متد (this ندارد — با احتیاط):
    power: (a, b) => a ** b,
};

console.log(calculator.sum(3, 4));      // 7
console.log(calculator.multiply(3, 4)); // 12
console.log(calculator.power(2, 8));    // 256

دسترسی به ویژگی‌ها

دو روش برای خواندن ویژگی‌ها وجود دارد — نقطه و براکت:

const book = { title: "شازده کوچولو", pages: 120 };

// 1. نماد نقطه (Dot Notation):
console.log(book.title); // "شازده کوچولو"

// 2. نماد براکت (Bracket Notation):
console.log(book["pages"]); // 120

// براکت برای کلیدهای پویا لازم است:
const prop = "title";
console.log(book[prop]); // "شازده کوچولو"
// console.log(book.prop); // undefined — غلط!
نماد نقطه فقط وقتی کار می‌کند که کلید، شناسه معتبر باشد (بدون فاصله، شروع با عدد و...). در غیر این صورت حتماً از براکت استفاده کنید.

افزودن و حذف ویژگی‌ها

اشیا در جاوا اسکریپت پویا هستند و می‌توان هر لحظه ویژگی جدید اضافه یا حذف کرد:

const product = { name: "لپ‌تاپ" };

// افزودن:
product.price = 45000000;
product["in-stock"] = true;

// حذف با delete:
delete product["in-stock"];
console.log(product); // { name: "لپ‌تاپ", price: 45000000 }

// بررسی وجود ویژگی:
console.log("price" in product);          // true
console.log(product.hasOwnProperty("name")); // true

// نام‌وجودبودن با مقدار undefined فرق دارد:
product.color = undefined;
console.log("color" in product); // true — ویژگی هست ولی undefined است

Getters و Setters

با get و set می‌توانید ویژگی‌هایی بسازید که هنگام خواندن یا نوشتن، کد اجرا می‌کنند — مثل دروازه‌بان:

const account = {
    _balance: 0, // قراردادی — خصوصی است

    get balance() {
        return this._balance + " تومان";
    },
    set balance(value) {
        if (value < 0) {
            console.log("مقدار نامعتبر!");
            return;
        }
        this._balance = value;
    },
};

account.balance = 500000;        // Setter اجرا می‌شود
console.log(account.balance);    // "500000 تومان" — Getter اجرا می‌شود
account.balance = -100;          // "مقدار نامعتبر!"
Getter مثل ویژگی خوانده می‌شود (بدون پرانتز) اما در واقع تابع است. این الگو برای اعتبارسنجی یا محاسبه مقادیر مشتق‌شده عالی است.

Object Destructuring

تخریب ساختار یعنی استخراج ویژگی‌های شیء مستقیم در متغیرها:

const movie = {
    title: "گرگ وال_street",
    director: "مارتین اسکورسیزی",
    year: 2013,
    country: "آمریکا",
};

// استخراج پایه:
const { title, director } = movie;
console.log(title);     // "گرگ وال_street"
console.log(director);  // "مارتین اسکورسیزی"

// مقدار پیش‌فرض:
const { year, genre = "درام" } = movie;
console.log(genre); // "درام"

// تغییر نام متغیر:
const { title: movieTitle } = movie;
console.log(movieTitle); // "شغل"

// تودرتو:
const { address: { city } = {} } = { address: { city: "تهران" } };
console.log(city); // "تهران"

Object Spread

عملگر سه‌نقطه (...) ویژگی‌های یک شیء را «باز می‌کند» و در شیء جدید می‌ریزد:

const defaults = { theme: "light", lang: "fa", fontSize: 14 };
const userPrefs = { theme: "dark", fontSize: 16 };

// ادغام — کلیدهای بعدی، قبلی‌ها را بازنویسی می‌کنند:
const settings = { ...defaults, ...userPrefs };
console.log(settings);
// { theme: "dark", lang: "fa", fontSize: 16 }

// کپی سطحی (Shallow Copy):
const copy = { ...defaults };

// افزودن/بازنویسی در حین کپی:
const updated = { ...defaults, fontSize: 18 };
console.log(updated.fontSize); // 18
Spread فقط یک کپی سطحی می‌سازد؛ اگر ویژگی‌ای خودش شیء باشد، مرجع آن کپی می‌شود نه محتوایش. برای کپی عمیق از structuredClone() استفاده کنید.

Object.keys و values و entries

این سه متد برای پیمایش اشیا بسیار کاربردی هستند:

const scores = { math: 18, physics: 17, chemistry: 19 };

// کلیدها به‌صورت آرایه:
console.log(Object.keys(scores));   // ["math", "physics", "chemistry"]

// مقادیر به‌صورت آرایه:
console.log(Object.values(scores)); // [18, 17, 19]

// جفت‌های [کلید, مقدار]:
console.log(Object.entries(scores));
// [["math", 18], ["physics", 17], ["chemistry", 19]]

// پیمایش امن با for...of:
for (const [subject, score] of Object.entries(scores)) {
    console.log(subject + ": " + score);
}
// math: 18, physics: 17, chemistry: 19

Object.assign

این متد چند شیء را در شیء مقصد ادغام می‌کند و شیء مقصد را برمی‌گرداند:

const target = { a: 1, b: 2 };
const source1 = { b: 3, c: 4 };
const source2 = { c: 5 };

// همه منابع در target ادغام می‌شوند (بعدی‌ها غالب‌اند):
Object.assign(target, source1, source2);
console.log(target); // { a: 1, b: 3, c: 5 }

// ساخت شیء جدید با assign:
const merged = Object.assign({}, source1, source2);
console.log(merged); // { b: 3, c: 4, c: 5 } → { b: 3, c: 5 }
{ ...obj1, ...obj2 } معادل خواناتر Object.assign({}, obj1, obj2) است و در کد مدرن ترجیح داده می‌شود.

Object.freeze و Object.seal

برای محافظت از اشیا در برابر تغییر:

// freeze — کاملاً غیرقابل تغییر:
const frozen = Object.freeze({ x: 1 });
frozen.x = 99;        // بی‌اثر (در Strict Mode خطا)
console.log(frozen.x); // 1

// seal — مقدارها قابل تغییر ولی افزودن/حذف ممنوع:
const sealed = Object.seal({ x: 1, y: 2 });
sealed.x = 50;         // ✅ مجاز
sealed.z = 3;          // ❌ بی‌اثر
delete sealed.y;       // ❌ بی‌اثر
console.log(sealed);   // { x: 50, y: 2 }

// تشخیص وضعیت:
console.log(Object.isFrozen(frozen)); // true
console.log(Object.isSealed(sealed));  // true
freeze فقط سطحی است؛ اشیای تودرتو همچنان قابل تغییرند. برای freeze عمیق باید بازگشتی همه سطوح را freeze کنید.

Object.create

این متد شیء جدیدی با نمونه‌اولیه (Prototype) مشخص می‌سازد — پایه وراثت prototype-based:

const vehicle = {
    init(wheels, speed) {
        this.wheels = wheels;
        this.speed = speed;
        return this;
    },
    describe() {
        return this.wheels + " چرخ، سرعت " + this.speed;
    },
};

// bike از vehicle ارث می‌برد:
const bike = Object.create(vehicle).init(2, 30);
console.log(bike.describe()); // "2 چرخ، سرعت 30"
console.log(Object.getPrototypeOf(bike) === vehicle); // true

Property Descriptor (توصیف‌گر ویژگی)

هر ویژگی یک توصیف‌گر پنهان دارد که رفتارش را کنترل می‌کند:

const obj = {};
Object.defineProperty(obj, "name", {
    value: "علی",
    writable: false,   // قابل نوشتن نیست
    enumerable: true,  // در حلقه‌ها دیده می‌شود
    configurable: false, // قابل حذف/تنظیم مجدد نیست
});

console.log(obj.name); // "علی"
obj.name = "رضا";      // بی‌اثر!
console.log(obj.name); // "علی"

// دیدن توصیف‌گر:
console.log(Object.getOwnPropertyDescriptor(obj, "name"));
// {
//   "value": "Ali",
//   "writable": false,
//   "enumerable": true,
//   "configurable": false
// }

// همه توصیف‌گرها:
console.log(Object.getOwnPropertyDescriptors(obj));
چهار صفت توصیف‌گر: value (مقدار)، writable (قابل نوشتن)، enumerable (قابل شمارش) و configurable (قابل پیکربندی).

مالکیت ویژگی‌ها

تفکیک ویژگی‌های خود شیء از ویژگی‌های نمونه‌اولیه:

function Person(name) {
    this.name = name;
}
Person.prototype.greet = function () {
    return "سلام " + this.name;
};

const p = new Person("مریم");

// ویژگی‌های خودِ شیء:
console.log(Object.getOwnPropertyNames(p)); // ["name"]

// کل کلیدها (شامل نمونه‌اولیه):
console.log(Object.keys(p)); // ["name"] — فقط enumerable ها

// بررسی مالکیت:
console.log(p.hasOwnProperty("name"));  // true — خودش
console.log(p.hasOwnProperty("greet")); // false — از prototype

// ES2022 — بررسی امن حتی برای ویژگی‌های خاص:
console.log(Object.hasOwn(p, "name")); // true

جمع‌بندی

اشیا قلب جاوا اسکریپت هستند. در این مبحث با ایجاد اشیا (Literal، سازنده و Object.create)، دسترسی نقطه/براکتی، افزودن و حذف، Getters/Setters، Destructuring و Spread، متدهای کلیدی Object و مفاهیم پیشرفته مثل Descriptor و مالکیت ویژگی آشنا شدید. در مباحث بعدی سراغ توابع و سپس وراثت prototype-based می‌رویم.

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط