Bản dịch này đang trong quá trình.

Object.assign() được sử dụng để sao chép các giá trị của tất cả thuộc tính có thể liệt kê từ một hoặc nhiều đối tượng nguồn đến một đối tượng đích. Nó sẽ  trả về đối tượng đích đó.

Cú pháp

Object.assign(target, ...sources)

Các tham số

target
Đối tượng đích
sources
Đối tượng nguồn

Giá trị return

Đối tượng đích

Mô tả

Các thuộc tính trong đối tượng đích sẽ bị ghi lại bởi các thuộc tính trong đối tượng nguồn nếu chúng có cùng key. Tương tự, các thuộc tính nguồn sau sẽ ghi đè lên những thuộc tính nguồn trước. 

Phương thức Object.assign() chỉ sao chép những giá trị liệt kê được và và các thuộc tính của bản thân nó đến đối tượng đích. Nó sử dụng  [[Get]] trên nguồn và [[Set]] trên đích, vì vậy nó sẽ gọi các hàm getter và setter.  Vì lý do đó nó chỉ định thuộc tính so với việc chỉ sao chép hoặc xác đinh các thuộc tính mới. Điều này có thể khiến nó không phù hợp khi gộp các thuộc tính mới vào một nguyên mẫu (prototype) nếu các nguồn gộp chứa các getter. Để sao chép các thuộc tính xác định, bao gồm cả khả năng đếm được vào trong các nguyên mẫu thì nên sử dụng Object.getOwnPropertyDescriptor()Object.defineProperty() để thay thế.

Các thuộc tính StringSymbol đều được sao chép.

Trong trường hợp có một lỗi, như việc một thuộc tính không được phép ghi đè, một  TypeError  sẽ sinh ra, và đối tượng đích có thể được thay đổi nếu có bất kỳ thuộc tính nào đã được thêm vào trước khi lỗi được sinh ra.

Chú ý rằng Object.assign() không ném ra một null hoặc undefined.

Ví dụ

Sao chép một object

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

Cảnh báo về Deep Clone

Với deep cloning, chúng ta cần sử dụng những lựa chọn khác khác bởi vì Object.assign() sao chép các giá trị thuộc tính. Nếu giá trị nguồn là tham chiếu đến một object,  nó chỉ sao chép gía trị tham chiếu đó. 

function test() {
  'use strict';

  let a = { b: {c: 4} , d: { e: {f: 1} } };
  let g = Object.assign({}, a);
  let h = JSON.parse(JSON.stringify(a));
  console.log(JSON.stringify(g.d)); // { e: { f: 1 } }
  g.d.e = 32;
  console.log('g.d.e set to 32.'); // g.d.e set to 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('h.d.e set to 54.'); // h.d.e set to 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();

Gộp các object

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 }, object đích tự nó bị thay đổi.

Gộp các đối tượng với cùng giá trị

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 }

Các giá trị được ghi đè bởi các đối tượng khác mà chúng có chung các thuộc tính sau đó theo thứ tự các tham số.

Sao chép thuộc tính symbol-typed

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 trên Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]

Các thuộc tính trên chuỗi nguyên mẫu và các thuộc tính không có khả năng đếm được thì không thể sao chép. 

var obj = Object.create({ foo: 1 }, { // foo ở trên mắt xích prototype của obj.
  bar: {
    value: 2  // bar chứa thuộc tính không liệt kê được. 
  },
  baz: {
    value: 3,
    enumerable: true  // baz chứa thuộc tính liệt kê được.
  }
});

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

Các giá trị nguyên thủy sẽ được gói thành các đối tượng.

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

var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 
// Sự nguyên bản sẽ bị gói lại, null và undefined sẽ bị bỏ qua.
// Ghi chú,chỉ có string wrapper mới có thuộc tính liệt kê được. 
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

Các ngoại lệ sẽ làm gián đoạn quá trình sao chép.

var target = Object.defineProperty({}, 'foo', {
  value: 1,
  writable: false
}); // target.foo chỉ read-only

Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" là read-only
// Trường hợp ngoại lệ được tạo ra khi gán target.foo

console.log(target.bar);  // 2, nguồn thứ nhất được sao chép thành công
console.log(target.foo2); // 3, đặc tính thứ nhất của nguồn thứ 2 được chép thành công.
console.log(target.foo);  // 1, ngoại lệ được ném ra
console.log(target.foo3); // undefined, phương thức gán đã hoàn tất, foo3 sẽ không bị sao chép
console.log(target.baz);  // undefined, nguồn thứ ba cũng không bị sao chép

Sao chép các trình truy cập (accessor)

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

var copy = Object.assign({}, obj); 
console.log(copy); 
// { foo: 1, bar: 2 }, giá trị của copy.bar là giá trị return của getter của obj.bar.

// Đây là function gán sao chép toàn bộ các mô tả. 
function completeAssign(target, ...sources) {
  sources.forEach(source => {
    let descriptors = Object.keys(source).reduce((descriptors, key) => {
      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
      return descriptors;
    }, {});
    // Mặc định thì Object.assign sao chép cả Symbol thống kê được luôn
    Object.getOwnPropertySymbols(source).forEach(sym => {
      let descriptor = Object.getOwnPropertyDescriptor(source, sym);
      if (descriptor.enumerable) {
        descriptors[sym] = descriptor;
      }
    });
    Object.defineProperties(target, descriptors);
  });
  return target;
}

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

Polyfill

 polyfill  không hỗ trợ các thuộc tính symbol, kể từ ES5 thì cũng không còn symbol nữa:

if (typeof Object.assign != 'function') {
  Object.assign = function(target, varArgs) { // .length của function là 2
    'use strict';
    if (target == null) { // TypeError nếu undefined hoặc 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) { // Bỏ qua nếu undefined hoặc 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;
  };
}

Đặc tính kỹ thuật

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  

Tương thích trình duyệt

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

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)

See also

Document Tags and Contributors

 Những người đóng góp cho trang này: kamui23, phithu, viiiprock
 Cập nhật lần cuối bởi: kamui23,