Object.assign()

Trwa tłumaczenie tego artykułu.

Metoda Object.assign() kopiuje wszystkie ważne, przeliczalne właściwości z jednego lub więcej obiektów źródłowych do obiektu docelowego. Zwraca obiekt docelowy.

Składnia

Object.assign(cel, ...zrodla)

Parameters

cel
Obiekt docelowy.
zrodla
Obiekt(y) żródłowe.

Zwracana wartość

Obiekt docelowy.

Opis

Właściwowści w obiektcie docelowym zostaną nadpisane przez te z obiektów źródłowych jeżeli mają taką samą nazwę. Właściwości obiektów podanych poźniej będą również nadpisywać tez obiektów podanych  wcześniej.

Metoda Object.assign() jedynie kopiuje przeliczalne własne właściwości ze źródeł do celu. Używa [[Get]] na źródle oraz [[Set]] na obiekcie docelowym, więc wywoła ich getter i setter. Dlatego też mowa tu o przyisaniu właściwości bardziej niż o kopiowaniu czy tworzeniu nowych. Metoda ta może być więc niestosowna do przyłączania nowych właściwości do prototypu, w przypadku gdy źródła przyłączenia zawierają metody getter. Do kopiowania całych definicji właściwości, wliczając ich przeliczalność, powinniśmy użyć Object.getOwnPropertyDescriptor() oraz Object.defineProperty().

Zarówno właściwości String jak i Symbol są kopiowane.

W przypadku błędu, maprzykład gdy wartość jest zabezpieczona przed zapisem, zostanie wyrzucony TypeError, a obiekt docelowy pozostanie niezmieniony.

Zauważ, że Object.assign() nie zwraca błędu w przypadku użycia null lub undefined jako żródło.

Przykłady

Klonowanie obiektu

var obiekt = { a: 1 };
var kopia = Object.assign({}, obiekt);
console.log(kopia); // { a: 1 }

Ostrzeżenie przed Głębokim Klonowaniem

W przypadku głębokiego klonowania musimy użyć innych alternatyw, ponieważ Object.assign() kopiuje jedynie wartości właściwości, jeżeli wartość źródłowa była odniesieniem do obiektu, skopiowane zostanie jedynie odniesienie do tego samego obiektu.

function test() 
{
  'use strict';

  // Obiekt a, właściwości b, c oraz d są odniesieniami do obiektów.
  let a = { b: {c: 4} , d: { e: {f: 1} } };
  // Kopiujemy właściwości obiektu a do g.
  let g = Object.assign({}, a);
  // Klonujemy głęboko a do h.
  let h = JSON.parse(JSON.stringify(a));

  console.log(JSON.stringify(g.d)); // { e: { f: 1 } }
  g.d.e = 32;
  console.log(JSON.stringify(g)); // { b: { c: 4 }, d: { e: 32 } }
  console.log(JSON.stringify(a)); // { b: { c: 4 }, d: { e: 32 } }
  console.log(JSON.stringify(h)); // { b: { c: 4 }, d: { e: { f: 1 } } }
  h.d.e = 54;
  console.log(JSON.stringify(g)); // { b: { c: 4 }, d: { e: 32 } }
  console.log(JSON.stringify(a)); // { b: { c: 4 }, d: { e: 32 } }
  console.log(JSON.stringify(h)); // { b: { c: 4 }, d: { e: 54 } }
}

test();

Łączenie obiektów

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 }, obiekt docelowy również został zmieniony.

Łączenie obiektów z tymi samymi właściowściami

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 }

Właściwość jest nadpisywana przez ostatni obiekt w liście parametrów, który ma taką samą właściwość.

Kopiowanie właściwości-symboli

var o1 = { a: 1 };
var o2 = { [Symbol('foo')]: 2 };

var obj = Object.assign({}, o1, o2);
console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]

Właściwości nieprzeliczalne oraz te z łańcucha prototypów nie są kopiowane

var obj = Object.create({ foo: 1 }, // foo jest właściwością prototypu obiektu obj
{ 
  bar: 
  {
    value: 2  // bar jest nieprzeliczalną właściwością
  },
  baz: 
  {
    value: 3,
    enumerable: true  // baz jest własną, przeliczalną właściwością obiektu obj
  }
});

var kopia = Object.assign({}, obj);
console.log(kopia); // { baz: 3 }

Wartości podstawowe zostaną włożone do ich wrapperów

var v1 = 'abc';
var v2 = true;
var v3 = 10;
var v4 = Symbol('foo');

var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 
// Wartości podstawowe będą we wrapperach, null i undefined zostaną zignorowane.
// Zauważ, że jedynie wrapper string'a ma przeliczalne właściwości:
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

Wyjątki przerwą wykonywanie kopiowania

var cel = Object.defineProperty({}, 'foo', 
{
  value: 1,
  writable: false
}); // cel.foo jest właściwością tylko do odczytu

Object.assign(cel, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" is read-only
// Wyjątek został wyrzucony podczas próby zapisania cel.foo

console.log(cel.bar);  // 2, pierwsze źródło zostało skopiowane pomyślnie
console.log(cel.foo2); // 3, pierwsza właściwość drugiego źródła zostało skopiowana pomyślnie
console.log(cel.foo);  // 1, tutaj został wyrzucony wyjątek
console.log(cel.foo3); // undefined, kopiowanie dobiegło końca, foo3 nie zostanie skopiowane
console.log(cel.baz);  // undefined, trzecie źródło również nie zostanie skopiowane

Kopiowanie operatorów pamięci

var obj = 
{
  foo: 1,
  get bar() 
  {
    return 2;
  }
};

var kopia = Object.assign({}, obj); 
console.log(kopia); 
// { foo: 1, bar: 2 }, wartość kopia.bar jest wartością zwracaną przez metodę getter właściwości obj.bar

// Funkcja przypisania kopiująca całe deskryptory:
function completeAssign(cel, ...zrodla) 
{
  zrodla.forEach(zrodlo=> 
  {
    let deskryptory = Object.keys(zrodlo).reduce((deskryptory, klucz) => 
    {
      deskryptory[klucz] = Object.getOwnPropertyDescriptor(zrodlo, klucz);
      return deskryptory;
    }, {});
    // domyślnie Object.assign kopiuje również przeliczalne symbole
    Object.getOwnPropertySymbols(zrodlo).forEach(sym => 
    {
      let deskryptor = Object.getOwnPropertyDescriptor(zrodlo, sym);
      if (deskryptor.enumerable) 
      {
        deskryptory[sym] = deskryptor;
      }
    });
    Object.defineProperties(cel, deskryptory);
  });
  return target;
}

var kopia = completeAssign({}, obj);
console.log(kopia);
// { foo:1, get bar() { return 2 } }

Polyfill

Ten polyfill nie wspiera właściwości-symboli, ponieważ ES5 i tak ich nie ma.

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;
  };
}

Specyfikacje

Specification Status Comment
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Object.assign' in that specification.
Standard Initial definition.
ECMAScript Latest Draft (ECMA-262)
The definition of 'Object.assign' in that specification.
Draft  

Kompatybilność z przeglądarką

Feature Chrome Firefox (Gecko) Internet Explorer Edge Opera Safari
Basic support 45 34 (34) No support (Yes) 32 9
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support No support 45 34.0 (34) No support No support (Yes)

Zobacz również

Autorzy i etykiety dokumentu

 Autorzy tej strony: drzazga, JacobDesight
 Ostatnia aktualizacja: drzazga,