Метод Object.assign() призначено для копіювання у цільовий об'єкт всіх особистих (не успадкованих) перелічуваних властивостей одного або декількох об'єктів. Метод повертає цільовий об'єкт.
Синтаксис
Object.assign(target, ...sources)
Параметри
target- Цільовий об'єкт, до якого буде скопійовано знайдені властивості.
sources- Вхідні об'єкти (щонайменше один), властивості яких буде скопійовано.
Вертає
Цільовий об'єкт.
Опис
Метод опрацьовує всі перелічувані особисті властивості кожного джерела. Наявні властивості в цільовому об'єкті буде перезаписано, а відсутні — створено. За наявності однакових ключів у кількох джерелах одночасно, значення зрештою буде узято з того, яке вказано пізніше. Тобто пріоритет джерел зростає зліва направо.
Метод використовує [[Get]] для отримання значень з джерел та [[Set]] для встановлення значень властивостей цільового об'єкта, отже він здійснює виклики відповідних геттерів і сеттерів. Отже він не переносить і не створює визначень нових властивостей, а лише встановлює значення. Ця особливість робить метод непридатним для злиття нових властивостей у прототип, якщо джерела містять геттери. Для копіювання визначень властивосте разом з їх перелічуваністю у прототип натомість скористайтеся методами Object.getOwnPropertyDescriptor() та Object.defineProperty().
Копіюються властивості з ключами обох типів: як String, так і Symbol.
В разі помилки, як-от спроба зміни значення незаписуваної властивості, викидається помилка TypeError, а цільовий об'єкт може бути частково змінений, якщо деякі властивості було записано до її виникнення.
Зважте на та, що Object.assign() не викидає помилок, якщо серед джерел є значення null або undefined.
Приклади
Клонування об'єкта
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // виводить { a: 1 }
obj.a = 100;
console.log(copy); // виводить { a: 1 }
Заувага щодо створення повних копій
Метод Object.assign() копіює властивості, значення яких є об'єктами, за посиланнями. Отже для створення повної (чи то пак «глибокої») копії доведеться скористатись іншими засобами:
function test() {
'use strict';
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // виводить { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // виводить { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // виводить { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // виводить { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // виводить { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // виводить { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // виводить { a: 2, b: { c: 3}}
// Повна копія
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // виводить { a: 0, b: { c: 0}}
}
test();
Злиття об'єктів
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // виводить { a: 1, b: 2, c: 3 }
console.log(o1); // виводить { a: 1, b: 2, c: 3 }, позаяк змінено сам об'єкт o1
Злиття об'єктів з однаковими властивостями
var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };
var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // виводить { a: 1, b: 2, c: 3 }
Властивості, отримані з об'єктів, що розташовані в списку раніше, перезаписуються однойменними властивостями об'єктів, що в списку стоять пізніше.
Копіювання властивостей типу symbol
var o1 = { a: 1 };
var o2 = { [Symbol('foo')]: 2 };
var obj = Object.assign({}, o1, o2);
// Через ваду 1207182 у Firefox друга властивість (з ключем symbol) не виводиться
console.log(obj); // виводить { a : 1, [Symbol("foo")]: 2 }
Object.getOwnPropertySymbols(obj); // вертає [Symbol(foo)]
Властивості з ланцюжка прототипів та неперелічувані властивості скопіювати не можна
// властивість foo належатиме до ланцюжка прототипів об'єкта obj
var obj = Object.create({ foo: 1 }, {
// bar є неперелічуваною властивістю
bar: {
value: 2
},
// натомість baz є перелічуваною особистою властивістю
baz: {
value: 3,
enumerable: true
}
});
var copy = Object.assign({}, obj);
console.log(copy); // виводить { baz: 3 }
Прості величини буде перетворено на об'єкти
Якщо замість того чи того джерела передати не об'єкт, а просту величину, її буде перетворено на об'єкт:
var v1 = 'abc';
var v2 = true;
var v3 = 10;
var v4 = Symbol('foo');
var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
console.log(obj); // виводить { "0": "a", "1": "b", "2": "c" }
Втім, лише об'єкти-обгортки для рядків мають особисті перелічувані властивості.
Помилки (винятки) переривають копіювання
// Властивість target.foo доступна лише для читання
var target = Object.defineProperty({}, 'foo', {
value: 1,
writable: false
});
// Помилка виникне й буде викинуто відповідний виняток під час копіювання властивості foo
// TypeError: "foo" is read-only
Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
console.log(target.bar); // виводить 2, перше джерело було успішно скопійовано
console.log(target.foo2); // виводить 3, першу властивість другого джерела було успішно скопійовано
console.log(target.foo); // виводить 1, власне тут і трапилась помилка
console.log(target.foo3); // виводить undefined, копіювання припинилося раніше, ніж досягло властивості foo3
console.log(target.baz); // виводить undefined, третє джерело також не було скопійовано
Копіювання визначень властивостей
var obj = {
foo: 1,
get bar() {
return 2;
}
};
// Значенням copy.bar буде те, що поверне геттер властивості obj.bar
var copy = Object.assign({}, obj);
console.log(copy); // виводить { foo: 1, bar: 2 }
// Ця функція скопіює визначення властивостей, а не лише їх значення
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// Типово метод Object.assign копіює перелічувані властивості типу Symbol так само
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
// Буде скопійовано не значення властивості bar, а геттер для неї
var copy = completeAssign({}, obj);
console.log(copy); // виводить { foo:1, get bar() { return 2 } }
Запасний варіант (поліфіл)
Наведений поліфіл не підтримує властивостей типу symbol, позаяк ES5 не має такого типу взагалі:
if (typeof Object.assign != 'function') {
Object.assign = function(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
Специфікації
| Специфікація | Статус | Коментар |
|---|---|---|
| ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'Object.assign' in that specification. |
Standard | Первинне визначення. |
| ECMAScript Latest Draft (ECMA-262) The definition of 'Object.assign' in that specification. |
Living Standard |
Підтримка веб-переглядачами
| Feature | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|---|
| Basic support | 45 | Yes | 34 | No | 32 | 9 |
| Feature | Android webview | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
|---|---|---|---|---|---|---|---|
| Basic support | No | 45 | Yes | Yes | No | No | Yes |