[자바스크립트 ES6+ 기본] 22. WeakMap 오브젝트

WeakMap 오브젝트

  • WeakMap은 object만 key로 사용 가능

    • number 등의 프리미티브 타입 사용 불가
    • value는 제한 없음
  • Map에서 key로 참조한 object를 삭제하면

    • object를 사용할 수 없게 되지만
    • Map에 object가 남음
    • 메모리 릭(memory leak) 발생
let sports = {like: "축구"};
const obj = new Map([
  [sports, "like:축구"]
]);

sports = {like: "농구"};
  • WeakMap의 object를 GC가 지움

    • GC: Garbage Collection
    • 그래서 (약한, 부서지기 쉬운) WeakMap?
  • WeakMap 오브젝트 메소드

    • set(), get(), has(), delete()
    • CRUD와 관련된 메소드만 있음
  • WeakMap entry의 열거 불가

  • 이터레이션 불가


new WeakMap()

  • WeakMap 인스턴스 생성, 반환

  • 파라미터 작성

    • 대괄호[] 안에 이터러블 오브젝트 작성
const empty = new WeakMap();

const sports = {};
const obj = new WeakMap([
  [sports, "sports 오브젝트"]
]);
log(typeof obj);

// object
  • WeakMap 오브젝트 구조
const map = Map;
const weakmap = WeakMap;
/*
1. map과 weakmap이 구조에서 크게 다르지 않지만

2. Map 오브젝트에 Symbol(Symbol, species)가 있지만 WeakMap 오브젝트에는 없음

3. map.prototype에 Symbol.iterator가 있지만 weakmap.prototype에는 Symbol.iterator가 없음

4. map.prototype에는 forEach()가 있지만 weakmap.prototype에는 forEach()가 있음
*/

const sports = {};
const obj = new WeakMap([
  [sports, "종목"]
]);
/*
1. 오른쪽의 obj를 펼치면 [[Entries]]가 있음. 이것은 엔진에서 설정하는 것을 뜻함.

2. [[Entries]]를 펼치면 0: {Object => "종목"} 형태
- [Object, "종목"] 형태로 작성한 것을 인덱스를 부여하여 배열로 만들고, 엘리먼트에 {Object: "종목"} 형태로 변환함

3. Map 인스턴스와 구조가 같음
*/

WeakMap 오브젝트 메소드

get()

  • WeakMap 인스턴스에서
    • key 값이 같은 value 반환
    • 존재하지 않으면 undefined 반환
const fn = () => {};
const obj = new WeakMap([
  [fn, "함수"]
]);
log(obj.get(fn));

// 함수

set()

  • WeakMap 인스턴스에 key, value 설정
const fn = function(){};
const obj = new WeakMap([
  [fn, "함수"]
]);
log(obj.get(fn));

obj.set(fn, "함수 변경");
log(obj.get(fn));

// 함수
// 함수 변경
  • 첫 번째 파라미터에 key로 사용할 오브젝트 작성

    • string과 같은 프리미티브 값 사용 불가
  • 두 번째 파라미터는 값

    • 첫 번째 파라미터의 오브젝트에 대한 값?
    • 오브젝트 구분 등의 용도. 오브젝트에 따라 연동하는 함수 등록

has()

  • WeakMap 인스턴스에서
    • key의 존재 여부 반환
    • 존재하면 true, 아니면 false 반환
const obj = {};
const weakobj = new WeakMap([
  [obj, "오브젝트"]
]);
log(weakObj.has(obj));

// true

delete()

  • WeakMap 인스턴스에서
    • key와 일치하는 entry 삭제
    • 삭제를 성공하면 true 반환
    • 삭제를 실패하면 false 반환
const fn = function(){};
const obj = new WeakMap([
  [fn, "함수"]
]);
log(obj.delete(fn));
log(obj.has(fn));

// true
// false

가비지 컬렉션 처리

  • 참조하는 object가 바뀌면
    • 참조했던 오브젝트가 가비지 컬렉션 처리됨
let obj = new WeakMap();
let sports = () => {point: 1};
obj.set(sports, "변경전");

sports = () => {point: 2};
obj.set(sports, "변경후");
  • let sports = () => {point: 1}; obj.set(sports, "변경전");

    • sports에 Function 오브젝트를 할당하고
    • 이것을 WeakMap 인스턴스에 key로 설정
  • sports = () => {point: 2};

    • 새로운 함수를 생성하여 할당
    • 바로 위의 sports가 참조하는 메모리 주소가 바뀜
    • sports가 참조하는 메모리 주소가 바뀌면 앞의 sports가 참조했던 오브젝트를 호출할 수 없게 됨
    • 이렇게 사용할 수 없게 된 {point: 1} 오브젝트는 GC 대상이 됨
    • 엔진이 주기적으로 GC 처리를 함
  • obj.set(sports, "변경후");

    • sports를 key로 하여 WeakMap에 설정
    • 앞에서 sports를 key로 하여 설정했으며 여기서도 sports를 key로 하여 설정하므로 값이 대체되어야 하지만
    • 두 개의 sports가 참조하는 주소가 다르므로 sports가 추가됨
  • WeakMap 인스턴스의 GC 상태

// WeakMap 인스턴스의 GC 상태
let obj = new WeakMap();
let sports = () => {point: 1};
obj.set(sports, "변경전");
/*
1. 아래에서 sports 변수에 {point: 2}를 할당하므로 sports가 참조하는 오브젝트가 바뀜
*/

sports = () => {point: 2};
obj.set(sports, "변경후");
/*
1. obj의 [[Entries]]를 펼치면 0과 1이 있음
- 변수값은 바뀌어 하나이지만 WeakMap 인스턴스에는 두 개가 있음

2. {point: 1}과 {point: 2}의 메모리 주소가 다르며 sports는 사람이 보는 것으로 WeakMap은 값인 메모리 주소가 다르므로 각각 저장함

3. 그래서 sports로 저장하지 않고 인덱스를 부여하여 저장하는 것
- 엔진은 인덱스가 key이며 sports는 프로퍼티 value에서 프로퍼티 키
*/

setTimeout(function(){
  console.log(obj.get(sports));
}, 2000);
/*
1. {point: 1}의 sports를 사용할 수 없으므로 GC가 {point: 1}의 sports를 메모리에서 지움. 또한 obj의 "변경전"도 삭제

2. 인덱스 1번에 0이 됨

3. Map 오브젝트에서 entry를 삭제해도 인덱스를 정리
*/

Map과 WeakMap 차이

  • 참조하는 object를 삭제하면

    • Map은 그대로 갖고 있지만
    • WeakMap은 GC 처리로 삭제됨
  • Map과 WeakMap 차이

// Map과 WeakMap 차이
let mapObj = new Map();
(function(){
  const obj = {key: "value"};
  mapObj.set(obj, "Map");
}());

let weakObj = new WeakMap();
(function(){
  const obj = {key: "value"};
  weakObj.set(obj, "WeakMap");
}());
  • let mapObj = new Map(); (function(){...}());

    • 즉시 실행 함수는 일회용으로 변수를 저장하지 않을 때 사용
    • 함수가 끝나면 obj 변수를 GC가 메모리에서 지움
    • Map은 obj 변수가 지워지더라도 Map에 설정된 obj를 지우지 않고 유지
  • const weakObj = new WeakMap(); (function(){...}());

    • 앞의 실행 환경과 같음
    • 다만, Map이 아닌 WeakMap에 저장
    • WeakMap은 obj 변수가 삭제되면 WeakMap에 설정된 obj를 삭제함
// Map과 WeakMap 차이
let mapObj = new Map();
(function(){
  const obj = {key: "value"};
  mapObj.set(obj, "Map");
}());
/*
1. mapObj를 펼치면 entry가 있음
*/

let weakObj = new WeakMap();
(function(){
  const obj = {key: "value"};
  weakObj.set(obj, "WeakMap");
}());
/*
1. weakObj를 펼치면 entry가 있음
*/

setTimeOut(function(){
  console.log(weakObj);
  console.log(mapObj);
  /*
  1. mapObj에는 entry가 있지만 weakObj에는 없음
  
  2. GC가 obj를 지우면서 WeakMap의 obj도 지우기 때문
  */
}, 1000);

좋은 웹페이지 즐겨찾기