Метод 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  

Підтримка веб-переглядачами

FeatureChromeEdgeFirefoxInternet ExplorerOperaSafari
Basic support45 Yes34 No329
FeatureAndroid webviewChrome for AndroidEdge mobileFirefox for AndroidIE mobileOpera AndroidiOS Safari
Basic support No45 Yes Yes No No Yes

Див. також

Мітки документа й учасники

 Зробили внесок у цю сторінку: asmforce, GeekEdem, romkor
 Востаннє оновлена: asmforce,