surf core js : 객체 복사

Dino_·2021년 7월 26일
0

surf javascript

목록 보기
12/28
post-thumbnail

객체와 원시 타입의 근본적인 차이 중 하나는 객체는 ‘참조에 의해(by reference)’ 저장되고 복사된다는 것이다.

원시값(문자열, 숫자, 불린 값)은 ‘값 그대로’ 저장·할당되고 복사되는 반면에 말이다.

let message = "Hello!";
let phrase = message;

예시를 실행하면 두 개의 독립된 변수에 각각 문자열 "Hello!"가 저장된다.

그런데 객체의 동작방식은 이와 다르다!

변수엔 객체가 그대로 저장되는 것이 아니라, 객체가 저장되어있는 '메모리 주소’인 객체에 대한 '참조 값’이 저장된다.

let user = {
  name: "John"
};

객체는 메모리 내 어딘가에 저장되고, 변수 user엔 객체를 '참조’할 수 있는 값이 저장된다.

따라서 객체가 할당된 변수를 복사할 땐 객체의 참조 값(주소값)이 복사되고 객체는 복사되지 않는다.

let user = { name: "John" };

let admin = user; // 참조값을 복사함


let user = { name: 'John' };

let admin = user;

admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨

alert(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함

참조에 의한 비교

객체 비교 시 동등 연산자 ==와 일치 연산자 ===는 동일하게 동작한다.

비교 시 피연산자인 두 객체가 동일한 객체인 경우에 참을 반환한다.

let a = {};
let b = a; // 참조에 의한 복사

alert( a == b ); // true, 두 변수는 같은 객체를 참조합니다.
alert( a === b ); // true

다른 예시를 보면 두 객체 모두 비어있다는 점에서 같아 보이지만, 독립된 객체이기 때문에 일치·동등 비교하면 거짓이 반환된다.

let a = {};
let b = {}; // 독립된 두 객체

alert( a == b ); // false

객체 복사, 병합과 Object.assign

객체가 할당된 변수를 복사하면 동일한 객체에 대한 참조 값이 하나 더 만들어진다는 걸 항상 기억하자.

그렇다면 객체 값을 복사하는 방법은 없을까? 기존에 있던 객체와 똑같으면서 독립적인 객체를 만들고 싶다면 어떻게 해야할까?

새로운 객체를 만든 다음 기존 객체의 프로퍼티들을 순회해 원시 수준까지 프로퍼티를 복사하는 방법이 있다.

let user = {
  name: "John",
  age: 30
};

let clone = {}; // 새로운 빈 객체

// 빈 객체에 user 프로퍼티를 순회하며 값 복사
for (let key in user) {
  clone[key] = user[key];
}

// clone은 독립적으로 복제
clone.name = "Pete"; // clone의 데이터를 변경

alert( user.name ); // John

또한, Object.assign를 사용하는 방법도 있다.

/*
@Param1 : destination, 목표로 하는 객체 (데이터를 담을 객체)
@Param2 : source, 복사하고자 하는 객체
@Return : Param1 객체
*/

Object.assign(dest, [src1, src2, src3...])

assign 메서드를 사용해 여러 객체를 하나로 병합하는 예시

let user = { name: "John" };

let permissions1 = { canView: true };
let permissions2 = { canEdit: true };

// permissions1과 permissions2의 프로퍼티를 user로 복사
Object.assign(user, permissions1, permissions2);

//user = { name: "John", canView: true, canEdit: true }

목표 객체(user)에 동일한 이름을 가진 프로퍼티가 있는 경우엔 기존 값이 덮어씌워 진다.

let user = { name: "John" };

Object.assign(user, { name: "Pete" });

alert(user.name); // user = { name: "Pete" }
//간단하게 객체를 복사할 수 있다.
let user = {
  name: "John",
  age: 30
};

let clone = Object.assign({}, user);

중첩 객체 복사

지금까진 user의 모든 프로퍼티가 원시값인 경우만 가정했다.

그런데 프로퍼티는 다른 객체에 대한 참조 값일 수도 있다. 이 경우는 어떻게 해야 할까?

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

alert( user.sizes.height ); // 182

user.sizes는 객체이기 때문에 참조 값이 복사된다. clone.sizes = user.sizes로 프로퍼티를 복사하면 clone과 user는 같은 sizes를 공유하게 되버린다.

이 문제를 해결하려면 user[key]의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용해야 한다.

이런 방식을 '깊은 복사(deep cloning)'라고 한다.

깊은 복사 시 사용되는 표준 알고리즘인 Structured cloning algorithm을 사용하면 위 사례를 비롯한 다양한 상황에서 객체를 복제할 수 있다.

https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm

자바스크립트 라이브러리 lodash의 메서드인 _.cloneDeep(obj)을 사용하면 이 알고리즘을 직접 구현하지 않고도 깊은 복사를 처리할 수 있으므로 참고하자.

Reference

profile
호기심 많은 청년

0개의 댓글