Object.assign در جاوااسکریپت
20 دقیقه زمان برای خواندن این مطلب نیاز است.
فهرست مطالب
- نحو (Syntax) و پارامترهای Object.assign
- توصیف کامل و نحوه عملکرد Object.assign
- موارد استفاده عملی از Object.assign
- تفاوت Object.assign با عملگر Spread (…)
- Polyfill برای مرورگرهای قدیمی
- نکات پیشرفته و بهترین روشها
- اشتباهات رایج و دامها
- کارایی (Performance) Object.assign
- مثالهای جامع و واقعی
- Object.assign و Object.create: ترکیب قدرتمند
- نتیجهگیری نهایی
- سوالات متداول (FAQ)
در دنیای برنامهنویسی جاوااسکریپت، کار با اشیاء (Objects) یکی از ارکان اصلی توسعه نرمافزارهای مدرن است. هر روزه نیاز داریم که اشیاء را کپی کنیم، ویژگیهای آنها را ادغام نماییم، یا مقادیر پیشفرض را تنظیم کنیم. اما آیا تا به حال به این فکر کردهاید که چه روش استاندارد و بهینهای برای این کار وجود دارد؟ Object.assign در جاوااسکریپت یک متد قدرتمند و داخلی است که از ES6 (ECMAScript 2015) به زبان اضافه شده و این نیازهای رایج را به سادهترین شکل ممکن پاسخ میدهد. در این مقاله از دانا پدیا، قصد داریم به طور کامل و تخصصی به بررسی Object.assign در جاوااسکریپت بپردازیم. اگر شما یک توسعهدهنده جاوااسکریپت هستید، چه مبتدی و چه حرفهای، این مطلب دقیقاً همان چیزی است که برای درک عمیق Object.assign در جاوااسکریپت، کاربردهای عملی آن و تفاوتهای آن با سایر روشهای کپی نیاز دارید.
Object.assign در جاوااسکریپت یک متد ایستا از شیء سراسری Object است که ویژگیهای قابل شمارش (enumerable) و متعلق به خود (own) از یک یا چند شیء منبع (source) را در یک شیء هدف (target) کپی میکند و در نهایت شیء هدف را بازمیگرداند. این متد در واقع یک روش استاندارد، سریع و خوانا برای ترکیب اشیاء و کپی سطحی (shallow copy) آنها ارائه میدهد. پیش از ظهور Object.assign در جاوااسکریپت، توسعهدهندگان مجبور بودند با استفاده از حلقههای for...in، توابع سفارشی یا کتابخانههایی مانند jQuery (با متد $.extend) این کارها را انجام دهند. اما امروزه Object.assign در جاوااسکریپت به عنوان راهحل بومی و بهینه در همه محیطهای مدرن جاوااسکریپت (مرورگرها، Node.js، Deno و…) در دسترس است.
در این مقاله از دانا پدیـا، ابتدا با نحو (syntax) و پارامترهای Object.assign در جاوااسکریپت آشنا میشویم، سپس رفتار آن را در سناریوهای مختلف بررسی میکنیم. خواهیم دید که چگونه میتوان از Object.assign در جاوااسکریپت برای کپی کردن اشیاء، ادغام چندین شیء، مقداردهی اولیه با مقادیر پیشفرض، تبدیل آرایهگونهها (array-like objects) به شیء واقعی، و حتی کپی کردن نمادها (Symbols) استفاده کرد. همچنین تفاوتهای کلیدی Object.assign در جاوااسکریپت با عملگر spread (...) را بررسی کرده و به تحلیل عمیق مفهوم کپی سطحی (shallow copy) خواهیم پرداخت. در ادامه، به اشتباهات رایج، کارایی، polyfill برای مرورگرهای قدیمی و سوالات متداول خواهیم پرداخت. هدف ما ارائه یک مرجع کامل و سئو شده است که نه تنها پاسخگوی نیازهای روزانه شما باشد، بلکه به عنوان یک منبع آموزشی معتبر در دانا پدیا باقی بماند.

نحو (Syntax) و پارامترهای Object.assign
برای استفاده مؤثر از Object.assign در جاوااسکریپت، ابتدا باید نحو دقیق آن را بدانیم. ساختار کلی این متد به صورت زیر است:
javascript
Object.assign(target, ...sources)
پارامترها
- target: شیء هدف. این شیء پس از اجرای متد، ویژگیهای کپی شده را دریافت میکند. مهم:
targetتغییر داده میشود (mutate میشود) و سپس همان شیء به عنوان خروجی بازگردانده میشود. - sources: یک یا چند شیء منبع. ویژگیهای قابل شمارش و متعلق به خود این اشیاء در
targetکپی میشوند. اگر بیش از یک منبع وجود داشته باشد، ویژگیهای منابع بعدی، ویژگیهای منابع قبلی را بازنویسی (override) میکنند.
خروجی (Return Value)
متد Object.assign در جاوااسکریپت، همان شیء target را پس از اعمال تغییرات برمیگرداند.
مثال بسیار ساده
javascript
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
const result = Object.assign(target, source);
console.log(target); // { a: 1, b: 3, c: 4 }
console.log(result); // { a: 1, b: 3, c: 4 }
console.log(target === result); // true (همان شیء است)
در این مثال، Object.assign در جاوااسکریپت مقدار ویژگی b از source را در target جایگزین کرده و ویژگی c را به آن اضافه کرده است.
توصیف کامل و نحوه عملکرد Object.assign
درک عمیق Object.assign در جاوااسکریپت نیازمند بررسی دقیق رفتار این متد است. در این بخش به صورت گام به گام توضیح میدهیم که درون این متد چه اتفاقی میافتد.
۱. کپی کردن فقط ویژگیهای قابل شمارش (Enumerable) و متعلق به خود (Own)
Object.assign در جاوااسکریپت فقط ویژگیهایی را کپی میکند که:
- ویژگی
enumerableآن برابرtrueباشد. - ویژگی مستقیماً به خود شیء منبع تعلق داشته باشد (نه از طریق زنجیره پروتوتایپ).
ویژگیهای غیرقابل شمارش (non‑enumerable) و ویژگیهای به ارث رسیده از زنجیره پروتوتایپ نادیده گرفته میشوند.
javascript
const obj = Object.create({ inherited: 1 }, {
enumerableProp: { value: 2, enumerable: true },
nonEnumerableProp: { value: 3, enumerable: false }
});
const target = {};
Object.assign(target, obj);
console.log(target); // { enumerableProp: 2 } (تنها ویژگی قابل شمارش)
۲. کپی سطحی (Shallow Copy)
یکی از مهمترین نکات در Object.assign در جاوااسکریپت این است که کپی سطحی انجام میدهد. یعنی اگر مقدار یک ویژگی خود یک شیء یا آرایه باشد، مرجع آن کپی میشود، نه یک کپی عمیق (deep copy). این بدان معناست که تغییر در شیء درونیِ منبع، در شیء هدف نیز منعکس خواهد شد (و بالعکس).
javascript
const source = { a: { b: 1 } };
const target = Object.assign({}, source);
target.a.b = 2;
console.log(source.a.b); // 2 (هر دو به همان شیء داخلی اشاره میکنند)
۳. بازنویسی ویژگیها با ترتیب منابع
هنگام استفاده از چندین منبع، Object.assign در جاوااسکریپت ویژگیها را به ترتیب از چپ به راست کپی میکند. اگر یک ویژگی در منابع بعدی نیز وجود داشته باشد، مقدار قبلی را بازنویسی میکند.
javascript
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const obj3 = { c: 5, d: 6 };
const result = Object.assign({}, obj1, obj2, obj3);
console.log(result); // { a: 1, b: 3, c: 5, d: 6 }
۴. رفتار با مقادیر null و undefined در منابع
اگر یکی از پارامترهای source برابر با null یا undefined باشد، Object.assign در جاوااسکریپت آن را نادیده میگیرد و خطایی رخ نمیدهد. اما خود target هرگز نمیتواند null یا undefined باشد (در غیر این صورت TypeError صادر میشود).
javascript
const target = { a: 1 };
const result = Object.assign(target, null, undefined, { b: 2 });
console.log(result); // { a: 1, b: 2 }
۵. تبدیل نوع (Type Coercion) برای منابع اولیه
اگر یک منبع از نوع اولیه (primitive) مانند رشته، عدد یا بولین باشد، Object.assign در جاوااسکریپت آن را به یک شیء wrapper متناظر تبدیل میکند (مانند new String(..)) و سپس ویژگیهای قابل شمارش آن شیء را کپی میکند. اما از آنجایی که اعداد و بولینها ویژگیهای قابل شمارش خاصی ندارند، عملاً چیزی کپی نمیشود. رشتهها ویژگیهای قابل شمارش مانند length و اندیسها دارند که کپی میشوند.
javascript
const target = {};
Object.assign(target, "abc", 123, true);
console.log(target);
// { '0': 'a', '1': 'b', '2': 'c', length: 3 }
// عدد و بولین چیزی اضافه نمیکنند
۶. کپی کردن نمادهای قابل شمارش (Enumerable Symbols)
از ES6 به بعد، نمادها (Symbols) نیز میتوانند به عنوان کلید ویژگیها استفاده شوند. Object.assign در جاوااسکریپت نمادهای قابل شمارش را نیز کپی میکند.
javascript
const sym = Symbol('test');
const source = { [sym]: 'value', normal: 1 };
const target = {};
Object.assign(target, source);
console.log(target[sym]); // 'value'
موارد استفاده عملی از Object.assign
حالا که فهمیدیم Object.assign در جاوااسکریپت چگونه کار میکند، بیایید مهمترین کاربردهای عملی آن را بررسی کنیم.
۱. کپی کردن (کلون کردن) اشیاء
یکی از رایجترین استفادههای Object.assign در جاوااسکریپت، ساخت یک کپی سطحی از یک شیء است. با ارسال یک شیء خالی به عنوان target و شیء اصلی به عنوان source، یک شیء جدید با همان ویژگیها به دست میآوریم.
javascript
const original = { name: 'Ali', age: 30 };
const clone = Object.assign({}, original);
console.log(clone); // { name: 'Ali', age: 30 }
console.log(original === clone); // false (اشاره به حافظه متفاوت)
این روش برای کپی کردن اشیاء ساده (بدون ویژگیهای تو در تو) بسیار مناسب است.
Python Structural Pattern Matching
۲. ادغام چندین شیء (Merging Objects)
با استفاده از Object.assign در جاوااسکریپت میتوانید چندین شیء را به راحتی در یک شیء واحد ادغام کنید. ویژگیهای اشیاء بعدی، ویژگیهای مشابه در اشیاء قبلی را بازنویسی میکنند.
javascript
const defaultConfig = { theme: 'light', language: 'en' };
const userConfig = { theme: 'dark', notifications: true };
const merged = Object.assign({}, defaultConfig, userConfig);
console.log(merged);
// { theme: 'dark', language: 'en', notifications: true }
این الگو در پیکربندی برنامهها، تنظیمات کاربر و APIها بسیار رایج است.
۳. مقداردهی اولیه با مقادیر پیشفرض (Default Values)
یکی از هوشمندانهترین کاربردهای Object.assign در جاوااسکریپت، تنظیم مقادیر پیشفرض برای پارامترهای یک تابع است که به صورت شیء دریافت میشوند.
javascript
function createUser(options) {
const defaults = {
name: 'Guest',
age: 0,
isAdmin: false
};
const settings = Object.assign({}, defaults, options);
console.log(settings);
}
createUser({ name: 'Sara' });
// { name: 'Sara', age: 0, isAdmin: false }
توجه کنید که ترتیب مهم است: ابتدا defaults و سپس options، تا مقادیر ارائهشده توسط کاربر جایگزین مقادیر پیشفرض شوند.
۴. تبدیل آرایهگونهها (Array-like Objects) به شیء واقعی
در جاوااسکریپت، برخی از اشیاء مانند arguments داخل توابع یا NodeList حاصل از document.querySelectorAll آرایهگونه هستند (دارای ویژگی length و اندیسها) اما متدهای آرایه را ندارند. با استفاده از Object.assign در جاوااسکریپت میتوانیم آنها را به یک شیء ساده تبدیل کنیم.
javascript
function convert() {
const argsObject = Object.assign({}, arguments);
console.log(argsObject); // { '0': 'a', '1': 'b', '2': 'c', length: 3 }
}
convert('a', 'b', 'c');
هر چند برای کار با آرایهها بهتر است از Array.from یا عملگر spread استفاده کنید، اما این روش نیز کاربرد دارد.
۵. کپی کردن نمادها (Symbols)
اگر شیء شما دارای کلیدهای Symbol است و میخواهید آنها را نیز کپی کنید، Object.assign در جاوااسکریپت به طور خودکار این کار را انجام میدهد (به شرطی که نمادها قابل شمارش باشند).
javascript
const sym = Symbol('id');
const obj = { [sym]: 123, name: 'Test' };
const copy = Object.assign({}, obj);
console.log(copy[sym]); // 123
۶. ترکیب با Object.create برای کپی کردن ویژگیها از زنجیره پروتوتایپ
هر چند Object.assign در جاوااسکریپت ویژگیهای به ارث رسیده را کپی نمیکند، اما میتوان با ترکیب آن با Object.create، یک شیء جدید ساخت که هم ویژگیهای یک شیء خاص را داشته باشد و هم از یک پروتوتایپ مشخص به ارث ببرد.
javascript
const parent = { inherited: 'value' };
const child = Object.create(parent);
child.own = 123;
const shallowCopy = Object.assign({}, child);
console.log(shallowCopy); // { own: 123 } (inherited کپی نشد)
تفاوت Object.assign با عملگر Spread (…)
یکی از سوالات متداول در مورد Object.assign در جاوااسکریپت این است که تفاوت آن با عملگر spread شیء ({...obj}) چیست. در اکثر موارد، این دو معادل هستند، اما تفاوتهای ظریفی وجود دارد.
شباهتها
- هر دو یک کپی سطحی (shallow copy) از ویژگیهای قابل شمارش و متعلق به خود انجام میدهند.
- هر دو با نمادهای قابل شمارش کار میکنند.
تفاوتها
| ویژگی | Object.assign | Spread operator |
|---|---|---|
| نوع خروجی | شیء target را تغییر میدهد و برمیگرداند | همیشه یک شیء جدید میسازد |
| تغییر target | target مستقیماً تغییر میکند (mutate) | شیء اصلی تغییری نمیکند |
| نحو برای چندین منبع | Object.assign(target, src1, src2) | { ...src1, ...src2 } |
| قابلیت استفاده در عبارات | فقط به صورت تابع | میتواند در هر جایی که شیء لفظی (object literal) مجاز است استفاده شود |
| عملکرد (Performance) | در برخی موتورها کمی سریعتر (به دلیل بازنویسی مستقیم) | معمولاً مشابه یا کمی کندتر |
مثال عملی که تفاوت را نشان میدهد:
javascript
const obj = { a: 1 };
const assignResult = Object.assign(obj, { b: 2 });
console.log(obj); // { a: 1, b: 2 } (تغییر کرده)
const obj2 = { a: 1 };
const spreadResult = { ...obj2, b: 2 };
console.log(obj2); // { a: 1 } (تغییر نکرده)
بنابراین اگر میخواهید شیء اصلی را تغییر ندهید، عملگر spread یا Object.assign({}, ...) مناسبتر است.
Polyfill برای مرورگرهای قدیمی
اگرچه Object.assign در جاوااسکریپت از ES6 است و امروزه تقریباً همه مرورگرهای مدرن و Node.js از آن پشتیبانی میکنند، اما در پروژههایی که نیاز به پشتیبانی از Internet Explorer یا مرورگرهای بسیار قدیمی دارند، ممکن است به polyfill نیاز داشته باشید. در زیر یک پیادهسازی ساده و استاندارد از polyfill برای Object.assign در جاوااسکریپت ارائه شده است:
javascript
if (typeof Object.assign !== 'function') {
Object.assign = function(target) {
'use strict';
if (target === null || target === undefined) {
throw new TypeError('Cannot convert undefined or null to object');
}
const to = Object(target);
for (let i = 1; i < arguments.length; i++) {
const nextSource = arguments[i];
if (nextSource !== null && nextSource !== undefined) {
for (const key in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, key)) {
to[key] = nextSource[key];
}
}
}
}
return to;
};
}
این polyfill فقط ویژگیهای قابل شمارش و متعلق به خود را کپی میکند. توجه داشته باشید که نمادها (Symbols) را پوشش نمیدهد (چون در محیطهای قدیمی Symbol وجود ندارد).
نکات پیشرفته و بهترین روشها
۱. کپی عمیق (Deep Copy) با Object.assign امکانپذیر نیست
برای کپی عمیق اشیاء تو در تو، نمیتوانید فقط به Object.assign در جاوااسکریپت تکیه کنید. راهحلهای زیر را در نظر بگیرید:
- استفاده از
JSON.parse(JSON.stringify(obj))(با محدودیتهایی مانند از دست رفتن توابع، تاریخ و نمادها) - استفاده از کتابخانههایی مانند Lodash با
_.cloneDeep - نوشتن یک تابع بازگشتی سفارشی
۲. از Object.assign برای کپی کردن ویژگیهای غیرقابل شمارش استفاده نکنید
اگر نیاز به کپی کردن ویژگیهای غیرقابل شمارش یا توصیفگرهای ویژگی (property descriptors) دارید، به جای آن از Object.getOwnPropertyDescriptors و Object.defineProperties استفاده کنید.
javascript
const source = {};
Object.defineProperty(source, 'hidden', { value: 42, enumerable: false });
const copy = Object.assign({}, source);
console.log(copy.hidden); // undefined
// روش صحیح برای کپی کردن توصیفگرها
const descriptors = Object.getOwnPropertyDescriptors(source);
const deepCopy = Object.defineProperties({}, descriptors);
console.log(deepCopy.hidden); // 42
۳. مراقب mutating شدن target باشید
اگر نمیخواهید شیء هدف تغییر کند، همیشه از یک شیء خالی به عنوان اولین آرگومان استفاده کنید:
javascript
// بد (target تغییر میکند)
const config = { a: 1 };
Object.assign(config, { b: 2 });
// خوب (ایجاد شیء جدید)
const newConfig = Object.assign({}, config, { b: 2 });
۴. Object.assign در chain کردن متدها
از آنجا که Object.assign در جاوااسکریپت شیء target را برمیگرداند، میتوان آن را با سایر متدها زنجیرهای کرد:
javascript
const obj = Object.assign({}, defaults, userPrefs, { version: 2 });
۵. استفاده از Object.assign برای شبیهسازی آرایهها (توصیه نمیشود)
از لحاظ فنی، میتوانید از Object.assign در جاوااسکریپت برای کپی کردن آرایهها استفاده کنید، اما نتیجه یک آرایه نیست بلکه یک شیء با اندیسهای عددی و خاصیت length است. برای کپی آرایه از slice، concat یا عملگر spread استفاده کنید.
javascript
const arr = [1, 2, 3];
const objCopy = Object.assign({}, arr);
console.log(objCopy); // { '0': 1, '1': 2, '2': 3 }
console.log(Array.isArray(objCopy)); // false
اشتباهات رایج و دامها
۱. فراموش کردن ماهیت کپی سطحی
بسیاری از توسعهدهندگان تصور میکنند Object.assign در جاوااسکریپت یک کپی عمیق انجام میدهد، در حالی که اینطور نیست. تغییر در اشیاء تو در تو در کپی، روی شیء اصلی نیز تأثیر میگذارد.
SvelteKit Server Load Functions
۲. استفاده از Object.assign روی آرگومانهای null یا undefined به عنوان target
اگر target برابر null یا undefined باشد، Object.assign در جاوااسکریپت یک TypeError صادر میکند.
javascript
Object.assign(null, { a: 1 }); // TypeError
۳. کپی کردن ویژگیهای به ارث رسیده
همانطور که اشاره شد، ویژگیهای موجود در زنجیره پروتوتایپ کپی نمیشوند. برای کپی آنها باید از حلقه for...in همراه با hasOwnProperty یا روشهای دیگر استفاده کرد.
۴. بازنویسی ناخواسته ویژگیها
اگر ترتیب منابع را اشتباه بدهید، ممکن است مقادیر پیشفرض جایگزین مقادیر سفارشی شوند. همیشه منابعی که اولویت بالاتری دارند را در انتها قرار دهید.
۵. کپی کردن getterها به عنوان مقدار (نه به عنوان تابع)
اگر یک شیء منبع دارای ویژگی با getter باشد، Object.assign در جاوااسکریپت مقدار حاصل از getter را در زمان اجرا محاسبه کرده و آن مقدار را در target کپی میکند. خود getter (تابع) کپی نمیشود.
javascript
const source = {
get value() { return 42; }
};
const target = Object.assign({}, source);
console.log(target.value); // 42
// اما target اکنون یک ویژگی معمولی با مقدار 42 دارد، نه getter
کارایی (Performance) Object.assign
از نظر کارایی، Object.assign در جاوااسکریپت در موتورهای مدرن بسیار بهینه است. برای کپی کردن تعداد زیادی ویژگی، معمولاً از حلقههای دستی سریعتر است زیرا در سطح موتور پیادهسازی شده است. با این حال، برای اشیاء بسیار بزرگ (دهها هزار ویژگی)، ممکن است تفاوتهای جزئی وجود داشته باشد. در بیشتر کاربردهای روزمره، تفاوت ناچیز است و خوانایی کد اهمیت بیشتری دارد.
مقایسه سرعت (مفهومی)
javascript
// روش دستی با حلقه
function manualAssign(target, source) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
return target;
}
// Object.assign سریعتر است (در عمل)
مثالهای جامع و واقعی
مثال ۱: مدیریت وضعیت (State Management) ساده
در کتابخانههای کوچک مدیریت وضعیت، اغلب از Object.assign در جاوااسکریپت برای بهروزرسانی ناقص (partial update) وضعیت استفاده میشود:
javascript
let state = { user: null, posts: [], loading: false };
function setState(updates) {
state = Object.assign({}, state, updates);
}
setState({ loading: true });
console.log(state.loading); // true
setState({ user: { name: 'Ali' }, loading: false });
console.log(state.user); // { name: 'Ali' }
مثال ۲: کپی کردن یک شیء با متدها
اگر شیء شما دارای متد (تابع) است، Object.assign در جاوااسکریپت آنها را نیز کپی میکند (چون توابع نیز مقادیر هستند و قابل کپی شدن).
javascript
const calculator = {
add(a, b) { return a + b; },
mult(a, b) { return a * b; }
};
const clone = Object.assign({}, calculator);
console.log(clone.add(2, 3)); // 5
مثال ۳: ترکیب تنظیمات پیشفرض با تنظیمات کاربر در یک اپلیکیشن
javascript
const defaultSettings = {
theme: 'light',
language: 'en',
notifications: true,
privacy: {
shareData: false
}
};
function applySettings(userSettings) {
// کپی سطحی - توجه که privacy در userSettings اگر باشد، کل شیء privacy بازنویسی میشود
const final = Object.assign({}, defaultSettings, userSettings);
// اما اگر فقط بخواهیم privacy. shareData را تغییر دهیم، باید دستی عمیق کپی کنیم
if (userSettings.privacy) {
final.privacy = Object.assign({}, defaultSettings.privacy, userSettings.privacy);
}
return final;
}
const user = { theme: 'dark', privacy: { shareData: true } };
const settings = applySettings(user);
console.log(settings.privacy); // { shareData: true } (ویژگیهای دیگر defaultSettings.privacy از دست رفتهاند؟ بله! بنابراین به درمان عمیق نیاز داشت)
این مثال نشان میدهد که برای اشیاء تو در تو، تنها Object.assign در جاوااسکریپت کافی نیست.
Object.assign و Object.create: ترکیب قدرتمند
گاهی اوقات میخواهیم یک شیء جدید بسازیم که از یک پروتوتایپ خاص ارثببری، اما همزمان ویژگیهای یک شیء دیگر را نیز داشته باشد. ترکیب Object.create و Object.assign در جاوااسکریپت این امکان را فراهم میکند:
javascript
const proto = { greet() { console.log('Hello'); } };
const properties = { name: 'Ali', age: 30 };
const obj = Object.assign(Object.create(proto), properties);
obj.greet(); // Hello
console.log(obj.name); // Ali
در اینجا Object.create(proto) یک شیء جدید با پروتوتایپ proto میسازد، سپس Object.assign ویژگیهای properties را به آن اضافه میکند.
نتیجهگیری نهایی
در این مقاله از دانا پدیا، به طور جامع و تخصصی به بررسی Object.assign در جاوااسکریپت پرداختیم. آموختیم که Object.assign در جاوااسکریپت یک متد قدرتمند برای کپی سطحی ویژگیهای قابل شمارش و متعلق به خود از یک یا چند شیء منبع به یک شیء هدف است. این متد کاربردهای گستردهای از جمله کپی کردن اشیاء، ادغام تنظیمات، مقداردهی اولیه با مقادیر پیشفرض و تبدیل آرایهگونهها دارد. همچنین با تفاوتهای آن با عملگر spread، محدودیتهای کپی سطحی، و نحوه پیادهسازی polyfill آشنا شدیم.
Object.assign در جاوااسکریپت یک ابزار ضروری در جعبه ابزار هر توسعهدهنده جاوااسکریپت است. با این حال، باید محدودیتهای آن (کپی سطحی، نادیده گرفتن ویژگیهای غیرقابل شمارش و به ارث رسیده) را به خاطر داشت و در مواقع نیاز از روشهای جایگزین مانند JSON.parse/stringify برای کپی عمیق یا Object.getOwnPropertyDescriptors برای کپی کامل استفاده کرد. ما در دانا پدیا توصیه میکنیم که در پروژههای خود، جایی که نیاز به کپی یا ادغام اشیاء دارید، ابتدا از Object.assign در جاوااسکریپت استفاده کنید و فقط در صورت نیازهای خاص به سراغ راهحلهای پیچیدهتر بروید. با رعایت اصول و نکات ذکر شده در این مقاله، میتوانید کدهای تمیزتر، کارآمدتر و کمخطاتری بنویسید.

سوالات متداول (FAQ)
سوال ۱: آیا Object.assign یک کپی عمیق (deep copy) ایجاد میکند؟
خیر، Object.assign در جاوااسکریپت فقط یک کپی سطحی (shallow copy) انجام میدهد. به این معنی که اگر یک ویژگی از نوع شیء یا آرایه باشد، مرجع آن کپی میشود، نه یک نمونه جدید. برای کپی عمیق باید از روشهای دیگری مانند JSON.parse(JSON.stringify(obj)) (با محدودیتهای خاص) یا کتابخانههایی مثل Lodash استفاده کنید.
سوال ۲: تفاوت Object.assign با عملگر spread {…} چیست؟
هر دو برای کپی سطحی اشیاء استفاده میشوند. تفاوت اصلی این است که Object.assign شیء target را تغییر میدهد (mutate میکند) در حالی که spread همیشه یک شیء جدید ایجاد میکند. همچنین spread نمیتواند به عنوان یک عبارت در همه جا استفاده شود (فقط درون object literals). از نظر عملکرد، معمولاً تفاوت قابل توجهی وجود ندارد.
سوال ۳: آیا Object.assign ویژگیهای به ارث رسیده از پروتوتایپ را کپی میکند؟
خیر، Object.assign در جاوااسکریپت فقط ویژگیهای متعلق به خود (own properties) و قابل شمارش (enumerable) را کپی میکند. ویژگیهایی که از طریق زنجیره پروتوتایپ به دست میآیند، نادیده گرفته میشوند.
سوال ۴: اگر چندین منبع داشته باشیم و یک کلید تکراری وجود داشته باشد، چه اتفاقی میافتد؟
آخرین منبع (راستترین) برنده است. Object.assign در جاوااسکریپت ویژگیها را به ترتیب از چپ به راست کپی میکند، بنابراین اگر یک کلید در منابع بعدی نیز وجود داشته باشد، مقدار قبلی را بازنویسی میکند.
سوال ۵: آیا Object.assign با Symbolها کار میکند؟
بله، اگر نماد (Symbol) قابل شمارش باشد (که به طور پیشفرض هستند)، Object.assign در جاوااسکریپت آن را نیز کپی میکند. این یکی از ویژگیهای ES6 است.
سوال ۶: چه اتفاقی میافتد اگر target یا هر source برابر null یا undefined باشد؟
اگر target null یا undefined باشد، Object.assign در جاوااسکریپت یک TypeError پرتاب میکند. اما اگر یک source null یا undefined باشد، به سادگی نادیده گرفته میشود و خطایی رخ نمیدهد.
سوال ۷: آیا میتوان از Object.assign برای کپی کردن آرایهها استفاده کرد؟
از نظر فنی بله، اما نتیجه یک آرایه نخواهد بود (چون Array.isArray روی خروجی false برمیگرداند). برای کپی آرایه بهتر است از slice()، concat() یا عملگر spread استفاده کنید.
سوال ۸: چگونه میتوانم یک polyfill برای مرورگرهای قدیمی (مثل IE) تهیه کنم؟
میتوانید از polyfill ارائه شده در این مقاله استفاده کنید یا از کتابخانههایی مثل core-js بهره ببرید. polyfill بالا ویژگیهای اصلی را پوشش میدهد، اما نمادها (Symbols) را شامل نمیشود (چون در IE اصلاً Symbol وجود ندارد).
سوال ۹: آیا Object.assign در Node.js پشتیبانی میشود؟
بله، از Node.js نسخه ۴ به بالا (و تمام نسخههای جدیدتر) Object.assign در جاوااسکریپت به طور کامل پشتیبانی میشود.
سوال ۱۰: بهترین جایگزین برای Object.assign وقتی نیاز به کپی عمیق داریم چیست؟
بسته به نیاز شما: برای اشیاء ساده بدون توابع، JSON.parse(JSON.stringify(obj)) سریع و آسان است. برای اشیاء پیچیده با توابع، تاریخ، RegExp و غیره، از _.cloneDeep از Lodash یا نوشتن تابع بازگشتی سفارشی استفاده کنید.