기본형 : Number, String, Boolean, Null ...
참조형 : Array, Function, Date, RegExp...
기본형 데이터와 참조형 데이터는 변수를 할당하는 과정에서 차이가 발생합니다.
기본형은 변수 영역에서 식별자를 할당 → 데이터 영역에 값을 할당 → 데이터 영역에 할당한 값의 주소를 식별자에 준다.
참조형은 변수 영역에서 식별자를 할당 → 객체의 변수(프로퍼티) 영역을 따로 할당하고 그 주소를 식별자에 할당 → 값을 객체의 변수(프로퍼티)영역에 식별자를 할당 → 데이터 영역에 값을 할당하고 그 주소를 객체의 변수 영역에 있는 식별자에 할당
기본형은 값이 담긴 주소값을 바로 복제하게 됩니다. 하지만 참조형은 값이 담긴 주소값(기본형)의 묶음을 가리키는 주소값을 복제하게 됩니다.
데이터 영역에서 할당된 값이 바뀌냐, 바뀌지 않느냐의 차이입니다.
기본형은 불변인데, 변수 영역에서 데이터가 바뀌는 것이 아니라 데이터 영역에서 값이 새로 할당되고 변수 영역에서 데이터의 주소값이 바뀌기 때문에 불변 입니다.
하지만 참조형은 예를 들어, 객체를 선언했을 때, 객체 변수(프로퍼티) 영역이 별도로 존재하게 됩니다. 이로 인해 참조형에서 복사하고 복사본의 값을 변경하게되면 원본까지 값이 바뀌는 현상이 발생합니다.
//기본형 데이터
var a = 10;
var b = a;
//참조형 데이터
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
// 데이터 영역에서 값이 새로 할당되면서 a는 10으로 바뀌지 않음
b = 15;
// obj1.c의 값도 20으로 바뀌게 됨
obj2.c = 20;
console.log(obj1.c === obj2.c); // true
위의 코드에서와 같이 참조형이 가변인 이유로 쉽게 얘기해서 원본의 객체의 값까지 바뀌게 되는 현상이 발생합니다. 원본객체와 복사된 객체가 서로 같은 주소를 참조하지 않는 불변객체가 필요하게 됩니다. 방법은 원본과 복사본이 같은 객체의 변수 영역 주소를 사용하지 않아야 합니다.
중첩 객체일 떄, 내부 객체의 주소도 같이 복사가 됩니다. 따라서 객체를 복사할 때, 내부 객체의 주소 또한 새로 할당이 되어야 합니다. 그렇지 않으면 복사된 내부 객체가 같은 주소를 사용하기 때문에 두 개 다 같은 값으로 변경됩니다.
깊은 복사를 위해서는 내부 객체의 주소 또한 새로 할당을 해야합니다.
위의 코드에서 obj1.c
를 바꾸지 않기 위해서는 얕은 복사를 통해 해결할 수 있습니다.
var a = 10;
var b = a;
//참조형 데이터
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
// 데이터 영역에서 값이 새로 할당되면서 a는 10으로 바뀌지 않음
b = 15;
// obj1.c의 값도 20으로 바뀌게 됨
obj2 = {c: 20, d: 'ddd'}; // 객체 자체를 새로 할당해 값을 변경했기 때문에 obj2가 obj1과 같은 참조값을 가지지 않음.
console.log(obj1.c === obj2.c); // false
하지만 위 방법(얕은 복사)은 아래와 같은 중첩 객체가 있는 경우에는 내부 객체의 주소는 새로 할당을 하지 않습니다.
var copyObject = function (target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result; // 객체 자체를 새로 할당 후 return
}
var user = {
name: 'hdlee',
urls: {
portfolio: 'http://github.com/abc',
blog: 'http://blog.com',
facebook: 'http://facebook.com/abc',
}
};
var user2 = copyObject(user); // user.url에 객체의 프로퍼티 영역의 주소와 user.url의 주소가 같음
user2.name = 'brother';
console.log(user.name === user2.name); // flase
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // true
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // true
위 코드의 문제를 해결하기 위해 깊은복사가 필요합니다.
var copyObjectDeep = function(target) {
var result = {};
if (typeof target === 'object' && target !== null) {
for (var prop in target) {
result[pop] = copyObjectDeep(target[prop]); // 내부 객체또한 새로운 조소로 할당
}
} else {
result = target;
}
return result;
}
var user2 = copyObjectDeep(user);
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // false
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // false