Object.assign()

Object.assign() 메서드는 출처 객체들의 모든 열거 가능자체 속성 (en-US)을 복사해 대상 객체에 붙여넣습니다. 그 후 대상 객체를 반환합니다.

시도해보기

구문

js
Object.assign(target, ...sources);

매개변수

target

목표 객체. 출처 객체의 속성을 복사해 반영한 후 반환할 객체입니다.

sources

출처 객체. 목표 객체에 반영하고자 하는 속성들을 갖고 있는 객체들입니다.

반환 값

목표 객체.

설명

목표 객체의 속성 중 출처 객체와 동일한 를 갖는 속성의 경우, 그 속성 값은 출처 객체의 속성 값으로 덮어씁니다. 출처 객체들의 속성 중에서도 키가 겹칠 경우 뒤쪽 객체의 속성 값이 앞쪽 객체의 속성 값보다 우선합니다.

Object.assign() 메서드는 출처 객체의 '열거 가능한 자체 속성'만 목표 객체로 복사합니다. 출처 객체에서 속성 값을 가져올 땐 [[Get]], 목표 객체에 속성 값을 지정할 땐 [[Set]]을 사용하므로 각각 접근자설정자를 호출합니다. 그러므로 Object.assign()은 속성을 단순히 복사하거나 새로 정의하는 것이 아니라, 할당(assign)하는 것입니다. 따라서 출처 객체가 접근자를 포함하는 경우, 프로토타입에 새로운 속성을 추가하는 용도로는 적합하지 않을 수도 있습니다.

열거성을 포함한 속성 정의를 프로토타입으로 복사하려면 Object.getOwnPropertyDescriptor()Object.defineProperty()를 사용하세요.

Object.assign()String 키와 Symbol 키 속성 모두 복사합니다.

오류(대상 객체의 속성이 쓰기 불가한 상태 등)를 마주치면 TypeError가 발생합니다. 그러나 오류가 발생하기 전에 이미 추가/변경한 다른 속성의 값은 대상 객체에 유지됩니다.

참고: Object.assign()의 출처에 null 또는 undefined를 제공해도 예외는 발생하지 않습니다.

예제

객체 복제

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

깊은 복사 주의점

Object.assign()은 속성의 값을 복사하기 때문에, 깊은 복사를 수행하려면 다른 방법을 사용해야 합니다.

만약 출처 값이 객체에 대한 참조라면 참조 값만 복사합니다.

js
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();

객체 병합

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 }, 목표 객체 자체가 변경됨.

같은 속성을 가진 객체 병합

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 }

같은 키를 가진 속성의 경우 매개변수 순서에서 더 뒤에 위치한 객체의 값으로 덮어씁니다.

심볼 속성 복사

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: 1 },
  {
    // foo는 obj의 프로토타입 체인에 있음
    bar: {
      value: 2, // bar는 열거 불가능
    },
    baz: {
      value: 3,
      enumerable: true, // baz는 열거 가능한 자체 속성
    },
  },
);

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);
// 원시 값은 래핑하고, null과 undefined는 무시
// 참고: 문자열 래퍼만 자체 열거형 속성을 가질 수 있음
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

예외로 인한 복사 작업 중단

js
const target = Object.defineProperty({}, "foo", {
  value: 1,
  writable: false,
}); // target.foo는 읽기 전용 속성

Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" is read-only
// target.foo에 할당할 때 예외 발생

console.log(target.bar); // 2, 첫 번째 출처 객체는 성공적으로 복사함
console.log(target.foo2); // 3, 두 번째 출처 객체 중 첫 번째 속성도 성공적으로 복사함
console.log(target.foo); // 1, 여기에서 예외가 발생했음
console.log(target.foo3); // undefined, assign 메서드가 종료됨, foo3은 복사되지 않음
console.log(target.baz); // undefined, 세 번째 출처 객체도 복사되지 않음

접근자 복사

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

let copy = Object.assign({}, obj);
console.log(copy);
// { foo: 1, bar: 2 }, copy.bar의 값은 obj.bar의 접근자가 반환한 값

// 속성의 온전한 기술자를 복사해 할당하는 함수
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은 열거 가능한 심볼도 복사함
    Object.getOwnPropertySymbols(source).forEach((sym) => {
      let 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 } }

명세

Specification
ECMAScript Language Specification
# sec-object.assign

브라우저 호환성

BCD tables only load in the browser

같이 보기