원시형과 객체의 차이는 참조에 의해 저장되고 복사된다는 것이다.
원시형은 값 그대로 저장, 할당되고 복사된다.
let message = "Hello!"
let pharse = message;
let user = {
name: 'Hun'
}
let admin = user;
따라서 객체에 접근하거나 객체를 조작할 때 여러 변수를 사용할 수 있다.
let user = {
name: 'Hun'
}
let admin = user;
admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨
console.log(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함
let a = {};
let b = a; // 참조에 의한 복사
// console.log( a == b ); // true, 두 변수는 같은 객체를 참조
// console.log( a === b ); // true
다른 예시로 두 객체가 비어있는 상태를 비교해보면 false가 나온다.
그 이유는 생긴건 같아 보이지만 실제로 바라보고 있는 주소가 다르기 때문이다.
let a = {};
let b = {}; // 독립된 두 객체
console.log( a == b ); // false
console.log( a === b ); // false
기존에 있던 객체와 똑같으면서 독립적인 객체를 만들고 싶다면 새로운 객체를 만든 다음 기존의 객체의 프로퍼티들을 반복문으로 원시 수준까지 복사하면 된다.
let user = {
name: "Hun",
age: 27
};
let clone = {}; // 새로운 빈 객체
// 빈 객체에 user 프로퍼티들을 반복문으로 넣는다.
for (let key in user) {
clone[key] = user[key];
}
clone.name = "Pete"; // clone의 데이터를 변경한다.
console.log( user.name ); // 기존 객체에는 여전히 Hun이 있다.
다른 방식으로 Object.assign(dest, [src1, src2, src3...])을 이용하는 방법이 있다.
첫 번째 인수 dest는 목표로 하는 객체이며 이어지는 인수들은 복사하고자 하는 객체이다.
dest를 제외한 인수(객체)의 프로퍼티들은 전부 첫 번째 인수로 복사되며 마지막에 dest를 반환한다.
이를 이용하면 반복문 없이 간단하게 객체를 복사할 수 있다.
let user = {
name: "Hun",
age: 27
};
let permissions1 = {
canView: true
};
let permissions2 = {
canEdit: true
};
// permissions1과 permissions2의 프로퍼티를 user로 복사한다.
Object.assign(user, permissions1, permissions2);
// user = { name: "Hun", age: 27,canView: true, canEdit: true }
프로퍼티가 다른 객체에 대한 참조 값일 경우 프로퍼티를 복사하는 것만으론 객체를 복사할 수 없다.
let user = {
name: "Hun",
sizes: {
height: 182,
width: 50
}
};
이 문제를 해결하려면 user[key]의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용해야 한다.
이런 방식을 '깊은 복사(deep cloning)'라고 하며 javascript library lodash의 메소드인 .cloneDepp(obj)를 사용하면
알고리즘을 구현하지 않고도 깊은 복사 처리를 할 수 있다.
javascript는 눈에 보이지 않는 곳에서 메모리 관리를 수행한다.
원시 값, 객체, 함수 등 우리가 만든 모든 것들은 메모리를 차지하는데 이때 더는 쓸모 없어지게 된 것들은 어떻게 처리가 될까?
자바스크립트 엔진 내에선 가비지 컬렉터(garbage collector)가 끊임없이 동작한다. 가비지 컬렉터는 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제한다.
이런 값은 루트(root) 라고 부른다.
전역 변수에 객체가 저장되어있다고 가정해 보자. 이 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값이 된다.
이 객체가 참조하는 다른 모든 것들도 도달 가능하다고 여겨지는데 예시를 통해 살펴보자.
let user = {
name: "Hun"
};
user = null;
user가 name: "Hun"을 바라보고 있다가 null로 값을 덮어쓰면서 바라보지 않는 상태가 되었다.
가비지 컬렉터는 name: "Hun"을 메모리에서 삭제한다.
만약 다른 변수도 참조를 하고 있다면 아직 바라보고 있기에 가비지 컬렉터가 삭제하지 않는다.
연결된 객체로 또 하나의 예시를 보자.
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
let family = marry({
name: "John"
}, {
name: "Ann"
});
// 현재 모든 객체가 도달 가능한 상태인데 참조 2개를 지워보자
delete family.father;
delete family.mother.husband;
최종적으로 name: "John"을 바라보게 되는 것이 없으므로 가비지 컬렉션이 정리해준다.
'가비지 컬렉션’은 대개 다음 단계를 거쳐 수행된다.