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 with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* `core-js`의 `Object.assign` 폴리필
* `Object.defineProperties()`
* 속성의 소유권과 열거 가능성 (en-US)
* 객체 리터럴에서의 전개 구문