객체와 원시 타입의 근본적인 차이 중 하나는 객체는 참조에 의해(by reference)
저장되고 복사되는 반면 원시 타입은은 값에 의해(by value)
저장되고 복사된다는 것 입니다.
let message = "Hello";
let phrase = message;
message
라는 변수에는 원시 타입의 문자열 "Hello"
가 들어가 있고, 그 값을 phrase
가 값을 복사해서 각각 문자열 "Hello"
가 저장됩니다.
반면 객체는 변수안에 객체 값이 그대로 저장되는것이 아니라, 객체가 저장되어있는 "메모리 주소"
의 주소값을 변수에 저장(참조)
합니다.
let user = { name: "uiseop" };
let admin = user;
user
라는 변수는 name프로퍼티를 갖는 객체가 있는 "주소값"
을 저장하고 있는 것 입니다.
여기서 admin
은 user
가 갖고 있는 값을 받아오는데 이는 그 객체가 있는 주소값이기때문에 결국 그림과같이 같은곳을 가리키는 "화살표"
가 생긴것으로 이해할 수 있겠습니다.
같은 곳을 가리키고 있기 때문에 그 객체에 접근해서 객체를 조작하면 같은 주소를 참조하고 있는 모든 변수의 값이 "변경"
되는 것 처럼 보이는 결과가 나타나는 이유 입니다.
let a = {};
let b = {};
console.log(a === b); // false
변수 a
와 b
에는 각각 빈 객체가 저장되어 있는 메모리의 주소값을 갖고있기때문에 이 둘을 에 일치·동등 비교하면 거짓이 반환됩니다.
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(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)"
방식을 사용해야한다는것을 기억하길 바랍니다.