[JS] 참조에 의한 객체 복사

이의섭·2021년 12월 27일
0
post-thumbnail

객체 vs 원시 타입

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

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


message라는 변수에는 원시 타입의 문자열 "Hello"가 들어가 있고, 그 값을 phrase가 값을 복사해서 각각 문자열 "Hello"가 저장됩니다.

반면 객체는 변수안에 객체 값이 그대로 저장되는것이 아니라, 객체가 저장되어있는 "메모리 주소"의 주소값을 변수에 저장(참조)합니다.

let user = { name: "uiseop" };
let admin = user;

user라는 변수는 name프로퍼티를 갖는 객체가 있는 "주소값"을 저장하고 있는 것 입니다.

여기서 adminuser가 갖고 있는 값을 받아오는데 이는 그 객체가 있는 주소값이기때문에 결국 그림과같이 같은곳을 가리키는 "화살표"가 생긴것으로 이해할 수 있겠습니다.

같은 곳을 가리키고 있기 때문에 그 객체에 접근해서 객체를 조작하면 같은 주소를 참조하고 있는 모든 변수의 값이 "변경"되는 것 처럼 보이는 결과가 나타나는 이유 입니다.

참조하는 객체 값이 비어있을 경우

let a = {};
let b = {};
console.log(a === b); // false

변수 ab에는 각각 빈 객체가 저장되어 있는 메모리의 주소값을 갖고있기때문에 이 둘을 에 일치·동등 비교하면 거짓이 반환됩니다.

가변값

let obj1 = {
  a: 1,
  b: "bbb"
};

obj1이라는 변수안에 참조타입인 객체를 할당했습니다. 이럴땐 원시타입과는 다르게 객체를 위한 변수(프로퍼티)영역이 별도로 생성된다는 점 입니다.

원시값처럼 데이터 영역에 값을 할당해서 그 주소를 불러오는 것이 아니라 새로운 영역에 또 변수(프로퍼티)를 위한 영역이 생성되고 그 영역 안에서 프로퍼티 a프로퍼티 b가 저장되고 프로퍼티의 값들은 원시타입과 동일하게 데이터 영역에 저장되어있는것을 확인하실 수 있습니다.

변수 영역에 저장된 값은 모두 가변값이고, 데이터 영역에 저장된 값은 모두 불변값이라고 메모리와 데이터에서 확인했습니다. 바로 이 부분 때문에 흔히 참조타입인 객체는 가변값이다. 라고 하는 것 입니다. 예시로 확인해 보죠

예시

let obj1 = {
  a: 1,
  b: "bbb"
};
obj1.a = 2;

obj1에 저장된 프로퍼티 a값을 변경해보았습니다. 그러면 데이터 영역에 2가 있으면 해당 공간을 재사용할것이고, 없다면 새로운 공간을 만들고 그 주소를 변수영역에 전달하게 될 것입니다.

그림에서 볼 수 있듯이 obj1이 차지하고 있는 변수 영역 1002의 값은 변경하지 않았는데 값이 정상적으로 변경된 것 을 확인하실 수 있습니다.

원시 타입처럼 복사하는 방법?

Object.assign

객체와 똑같으면서 독립적인 객체를 만들려면 내장 메서드인 Object.assign(dest, src1,src2,src3...)를 사용하면 됩니다.

"dest"는 목표로 하는 객체(보통 빈 객체를 사용), "src"에는 복사하고자 하는 객체 입니다. "src"의 프로퍼티들을 "dest"에 복사되어 마지막으로 "dest"를 반환하게 됩니다.

let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// permissions1과 permissions2의 프로퍼티를 user로 복사합니다.
Object.assign(user, permissions1, permissions2);
// now user = { name: "John", canView: true, canEdit: true }

하지만 이렇게 복사하는 방식을 "얕은 복사(Shallow Copy)"라고 합니다.
객체 프로퍼티의 값에 "원시 타입"의 자료형들이 있다면 이 얕은 복사로도 독립적인 새로운 객체를 만들 수 있지만, 프로퍼티의 값에 원시 타입이 아닌 "객체 타입"의 참조값이 들어가 있으면 복사된 값에도 참조값이 들어가게 되어 완전히 독립적인 객체가 생성되지 않습니다.

때문에 이 문제를 해결하기 위해서는 모든 프로퍼티를 확인해서 그 값이 "객체"일 경우 객체의 구조도 복사해주는 반복문을 사용해야합니다.

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

💬요약

객체는 값 자체가 할당되는것이 아니라 메모리상 위치한 주소를 할당하는 by reference방식으로 할당과 복사가 이뤄집니다.
때문에 객체를 완벽하게 복사하기 위한 "깊은 복사(Deep Copy)" 방식을 사용해야한다는것을 기억하길 바랍니다.

profile
사용자 중심 생각하는 프론트엔드 개발자가 되고 싶은..

0개의 댓글