Object.assign()

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 2015 年 9 月以來,該特性已在各大瀏覽器中可用。

Object.assign() 靜態方法將一個或多個 *源物件* 的所有 可列舉自有屬性 複製到一個 *目標物件*。它返回修改後的目標物件。

試一試

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// Expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget === target);
// Expected output: true

語法

js
Object.assign(target)
Object.assign(target, source1)
Object.assign(target, source1, source2)
Object.assign(target, source1, source2, /* …, */ sourceN)

引數

目標

目標物件 — 要應用源屬性的物件,在修改後返回。如果目標提供了原始值,它將被轉換為物件。

source1, …, sourceN

源物件 — 包含要應用的屬性的物件。

返回值

目標物件。

異常

TypeError

在以下情況之一中丟擲

  • target 引數是 nullundefined
  • 目標物件的屬性賦值失敗;例如,因為目標物件的屬性不可寫,或者因為其 setter 丟擲了錯誤。

描述

如果目標物件中的屬性與源物件中的屬性具有相同的 ,則源物件中的屬性會覆蓋目標物件中的屬性。後面的源物件中的屬性會覆蓋前面的源物件中的屬性。

Object.assign() 方法只從源物件複製 *可列舉* 和 *自有* 屬性到目標物件。它在源上使用 `[[Get]]`,在目標上使用 `[[Set]]`,因此它會呼叫 gettersetter。因此,它 *賦值* 屬性,而不是複製或定義新屬性。這可能使其不適合將新屬性合併到原型中,如果合併源包含 getter 的話。

要將屬性定義(包括它們的列舉性)複製到原型,請改用 Object.getOwnPropertyDescriptor()Object.defineProperty()

StringSymbol 屬性都會被複制。

如果發生錯誤,例如屬性不可寫,則會丟擲 TypeError,並且如果任何屬性在錯誤丟擲之前被新增,target 物件也會被更改。

注意: Object.assign() 不會因 nullundefined 源而丟擲錯誤。

示例

克隆物件

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

深克隆警告

對於 深克隆,我們需要使用 structuredClone() 等替代方法,因為 Object.assign() 僅複製屬性值。

如果源值是對物件的引用,它只複製引用值。

js
const obj1 = { a: 0, b: { c: 0 } };
const obj2 = Object.assign({}, obj1);
console.log(obj2); // { a: 0, b: { c: 0 } }

obj1.a = 1;
console.log(obj1); // { a: 1, b: { c: 0 } }
console.log(obj2); // { a: 0, b: { c: 0 } }

obj2.a = 2;
console.log(obj1); // { a: 1, b: { c: 0 } }
console.log(obj2); // { a: 2, b: { c: 0 } }

obj2.b.c = 3;
console.log(obj1); // { a: 1, b: { c: 3 } }
console.log(obj2); // { a: 2, b: { c: 3 } }

// Deep Clone
const obj3 = { a: 0, b: { c: 0 } };
const obj4 = structuredClone(obj3);
obj3.a = 4;
obj3.b.c = 4;
console.log(obj4); // { a: 0, b: { c: 0 } }

合併物件

js
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };

const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.

合併具有相同屬性的物件

js
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

在引數順序中,後面的物件會覆蓋具有相同屬性的物件。

複製 Symbol 型別屬性

js
const o1 = { a: 1 };
const o2 = { [Symbol("foo")]: 2 };

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

原型鏈上的屬性和不可列舉屬性無法複製

js
const obj = Object.create(
  // foo is on obj's prototype chain.
  { foo: 1 },
  {
    bar: {
      value: 2, // bar is a non-enumerable property.
    },
    baz: {
      value: 3,
      enumerable: true, // baz is an own enumerable property.
    },
  },
);

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

原始值將被包裝成物件

js
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo");

const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// Primitives will be wrapped, null and undefined will be ignored.
// Note, only string wrappers can have own enumerable properties.
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

// Primitives as the target are also wrapped to objects
const number = Object.assign(3, { a: 1 });
console.log(number); // Number {3, a: 1}
console.log(typeof number); // object
console.log(number.a); // 1

// null and undefined as targets throw TypeError
try {
  Object.assign(null, { a: 1 });
} catch (e) {
  console.log(e.message); // "Cannot convert undefined or null to object"
}

異常會中斷正在進行的複製任務

js
const target = Object.defineProperty({}, "foo", {
  value: 1,
  writable: false,
}); // target.foo is a read-only property

Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" is read-only
// The Exception is thrown when assigning target.foo

console.log(target.bar); // 2, the first source was copied successfully.
console.log(target.foo2); // 3, the first property of the second source was copied successfully.
console.log(target.foo); // 1, exception is thrown here.
console.log(target.foo3); // undefined, assign method has finished, foo3 will not be copied.
console.log(target.baz); // undefined, the third source will not be copied either.

複製訪問器

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

let copy = Object.assign({}, obj);
console.log(copy);
// { foo: 1, bar: 2 }
// The value of copy.bar is obj.bar's getter's return value.

// This is an assign function that copies full descriptors
function completeAssign(target, ...sources) {
  sources.forEach((source) => {
    const descriptors = Object.keys(source).reduce((descriptors, key) => {
      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
      return descriptors;
    }, {});

    // By default, Object.assign copies enumerable Symbols, too
    Object.getOwnPropertySymbols(source).forEach((sym) => {
      const descriptor = Object.getOwnPropertyDescriptor(source, sym);
      if (descriptor.enumerable) {
        descriptors[sym] = descriptor;
      }
    });
    Object.defineProperties(target, descriptors);
  });
  return target;
}

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

規範

規範
ECMAScript® 2026 語言規範
# sec-object.assign

瀏覽器相容性

另見