on your mark
[JAVASCRIPT.INFO] 4.2 참조에 의한 객체 복사, 4.3 가비지 컬렉션 본문
4.2 참조에 의한 객체 복사
주제 탐색
객체와 원시 타입의 근본적인 차이 중 하나는 객체는 '참조에 의해(by reference)' 저장되고 복사된다는 것이다. (원시값은 '값 그대로' 저징, 할당되고 복사됨)
객체의 경우 변수엔 객체가 그대로 저장되는 것이 아니라, 객체가 저장되어 있는 '메모리 주소'인 객체에 대한 '참조값'이 저장된다.
let user = { name : 'John' };
let admin = user;
admin.name = 'Pete';
alert(user.name); // 'Pete'
참조에 의한 비교
let a = {}; let b = a; // 참조에 의한 복사 alert(a == b); //true, 두 변수는 같은 객체를 참조한다. alert(a === b); //true
let a = {}; let b = {}; alert( a== b ); // false / 독립된 두 객체이기 때문에 false
객체 복사, 병합과 Object.assign
- 자바스크립트는 객체 복제 내장 메서드를 지원하지 않기 때문에 객체 자체 복사는 어려운 편. 정말 복사가 필요한 상황이라면 새로운 객체를 만든 다음 기존 객체의 프로퍼티들을 순회해 원시 수준까지 프로퍼티를 복사하면 된다.
let user = {
name: "John",
age: 30,
}
let clone = {};
for(let key in user) {
clone[key] = user[key];
}
// 이제 clone은 완전히 독립적인 복제본이 되었다.
clone.name = "Pete";
alert(user.name); // John
Ojbect.assign을 사용하는 방법도 있다.
Object.assign(dest, [src1, src2, src3...]);
첫 번째 인수
dest
는 목표로 하는 객체이다이어지는 인수
src1, ..., srcN
는 복사하고자 하는 객체이다.객체
src1, ..., srcN
의 프로퍼티를dest
에 복사한다.dest
를 제외한 인수의 프로퍼티 전부가 첫 번째 인수(객체)로 복사된다.마지막으로
dest
를 반환한다.
let user = { name: "John" }; let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; Object.assign(user, permissions1, permissions2); // user = {name: "John", canView: true, canEdit: true}
let user = { name: "John", age: 30, } let clone = Object.assign({}, user);
예시를 실행하면
user
에 있는 모든 프로퍼티가 빈 배열에 복사되고 변수에 할당됨
중첩 객체 복사
- 프로퍼티는 다른 객체 대한 참조 값일 수도 있다. 이러한 경우 깊은 복사(deep cloning) 을 사용해 복사해야 한다.
user[key]
의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용해야 한다. - 자바스크립트 라이브러리 lodash의 메서드 _.cloneDeep(obj)를 사용하면 알고리즘을 직접 구현하지 않고도 깊은 복사를 처리할 수 있다.
4.3 가비지 컬렉션
더는 쓸모 없어지게 된 것들을 어떻게 처리할까?
가비지 컬렉션 기준
자바스크립트는 도달 가능성(reachability) 이라는 개념을 사용해 메모리 관리를 수행한다. '도달 가능한(reachable)' 값은 어떻게든 접근하거나 사용할 수 있는 값을 의미한다. 도달 가능한 값으 메모리에서 삭제 되지 않는다.
- 아래 값들은 태생부터 도달 가능하기 때문에, 명백한 이유 없이는 삭제되지 않는다. 이런 값들은 루트(root)라고 부른다.
- 현재 함수의 지역 변수와 매개 변수
- 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수
- 전역 변수
- 루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 된다.
전역 변수에 객체가 저장되어 있고, 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값이 된다.
간단한 예시
let user = {
name: "John"
}
user
는 {name: 'John'}
이라는 객체를 참조한다.
user = null;
user
값을 다른 값으로 덮어쓰면 참조가 사라진다. 이제 John은 도달할 수 없는 상태가 된다. John에 접근할 방법도, John을 참조하는 것도 모두 사라진다. 가비지 컬렉터는 John에 저장된 데이터를 삭제하고, John을 메모리에서 삭제한다.
참조 두개
let user = {
name: "John",
}
let admin = user;
user = null;
전역변수 admin
을 통하면 여전히 객체 John에 접근할 수 있기 때문에 위의 경우는 John이 메모리에서 삭제되지 않는다. 이 상태에서 admin
을 다른 값(null 등)으로 덮어쓰면 John은 메모리에서 삭제될 수 있다.
연결된 객체
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman,
}
}
let family = marry({name: "John"}, {name:"Ann"});
함수 marry
는 매개변수로 받은 두 객체를 서로 참조하게 하면서, 두 객체를 포함하는 새로운 객체를 반환한다.
delete family.father;
delete family.mother.husband;
참조 2개를 지운 경우, John은 도달가능한 상태에서 벗어나게 된다. 외부로 나가는 참조는 도달가능한 상태에 영향을 주지 않고, 들어오는 참조만이 도달 가능한 상태에 영향을 주기 때문에 John은 메모리에서 제거된다. John에 저장된 데이터(프로퍼티) 역시 메모리에서 사라진다.
도달할 수 없는 섬
객체들이 연결되어 섬 같은 구조를 만드는데, 이 섬에 도달할 방법이 없는 경우, 섬을 구성하는 객체 전부가 메모리에서 삭제된다.
family = null;
John과 Ann은 서로 참조하고 있고, 두 객체 모두 외부에서 들어오는 참조를 갖고 있지만, "family"
객체와 루트의 연결이 사라지면서 루트 객체를 참조하는 것이 아무것도 없게 된다. 섬 전체가 도달할 수 없는 상태가 되고, 섬을 구성하는 객체 전부가 메모리에서 제거된다.
내부 알고리즘
'mark-and-sweep'이라 불리는 가비지 컬렉션 기본 알고리즘
- 루트를 mark
- 루트가 참조하고 있는 것들을 mark
- 도달 가능한 모든 객체를 방문할 때까지, mark한 객체가 참조하는 객체를 계속해서 mark
- 방문할 수 없었던 객체는 메모리에서 삭제
'WEB > Javascript' 카테고리의 다른 글
[JAVASCRIPT.INFO] 4.5 new 연산자와 생성자 함수 (0) | 2022.12.28 |
---|---|
[JAVASCRIPT.INFO] 4.4 메서드와 this (0) | 2022.12.25 |
[JAVASCRIPT.INFO] 4.1 객체 (0) | 2022.12.25 |
[JAVASCRIPT.INFO] 1. 소개 / 2. 자바스크립트 기본 (0) | 2022.12.23 |
[Javascript] 클로저(Closure) (0) | 2022.05.10 |