Map Object
- Map Object
- key와 value의 컬렉션
- 형태
- [key, value] 형태처럼 대괄호 안에 key와 value를 작성
- 다양한 타입을 key로 사용할 수 있다.
const obj = new Map([ ["key", "value"], [{book: 200}, "오브젝트"], [100, "Number"] ]); for(let keyValue of obj) { console.log(keyValue); // [key, value], [{book: 200}, 오브젝트], [100, Number] }
- Map의 key 처리
- for - of 문에서 작성한 순서대로 읽혀진다.
- 순서 보장
- new Map()
- 형태: new Map()
- 파라미터: [이터러블 오브젝트](option)
-
반환: 생성한 Map 인스턴스
- Map 인스턴스를 생성하여 반환
- 파라미터에 이터러블 오브젝트 작성
const obj = new Map([ ["key": "value"], [100, "Number"] ]); console.log(obj); // {} console.log(typeof obj); // object
- Same-Value-Zero 비교 알고리즘
- key 값을 비교
const obj = new Map([ [100, "Number"], ["100", "String"] ]); for(let [key, value] of obj) { console.log(`${key}: ${value}`); // 100: Number, 100: String }
- key 값이 같으면 value가 대체됨
const obj = new Map([ [100, "Number1"], [100, "Number2"] ]); for(let [key, value] of obj) { console.log(`${key}: ${value}`); // 100: Number2 }
- key 값이 타입까지 같으면 value가 대체된다.
- key 값을 비교
- 잘못 작성한 형태
try { new Map(["one", 1]); } catch { console.log("[[one, 1]]"); } // [[one, 1]] 출력 - 대괄호가 1개 빠졌다. const obj = new Map([{five: 5}]); for(let [key, value] of obj) { console.log(`${key}: ${value}`); // undefined: undefined }
- new Map([“one”, 1]): 대괄호 2개를 작성해야 한다.
- const obj = new Map([{five: 5}]): key 만 작성하면, 에러가 발생하지 않지만, key와 value에 undefined가 설정된다.
- Map과 Object 비교
- Map Object 구조
const map = Map; const list = [1, 2]; const obj = new Map([ ["one", "첫 번째"], ["two", "두 번째"] ]);
- const map = Map;
- Map Object에 get Symbol(Symbol.species)가 있다. 따라서 constructor를 오버라이드 할 수 있다.
- prototype을 펼치면 Symbol.iterator가 있다. iterator 오브젝트이기 때문에 for-of 가능
- const list = [1, 2]; const obj = new Map([ [“one”, “첫 번째”], [“two”, “두 번째”] ]);
- 오른쪽 obj를 펼치면 [[Entries]]가 있다. 대괄호 2개는 엔진에서 설정하는 것을 뜻한다.
- [[Entries]]를 펼치면 0: {“one” => “첫 번째”} 형태
- 인덱스를 부혀하여 key로 사용하고 {“one”: “첫 번째”}를 value로 설정
- 이것은 배열 형태와 구조가 비슷, size가 length 기능
- 인덱스를 부여하여 저장하므로 작성한 순서로 읽혀진다.
- const map = Map;
- key
- Map: 타입 제약 없음
- Object: String, Symbol
- {key: value} 수
- Map: size 프로퍼티로 구함
- Object: 전체를 읽어 구해야 함
- 처리시간: MDN에 의하면 빈번하게 key, value를 추가/삭제할 때는 Map이 Object보다 좋은 경우가 있다고 한다.
- Map Object 구조
Map 사용 메서드
- set()
- 형태: Map.prototype.set()
- 파라미터: key, value
-
반환: key, value가 설정된 인스턴스
- Map 인스턴스에 key, value 설정
- key, value 순서로 파라미터 작성
- key, value를 설정한 인스턴스 반환
let obj = new Map(); obj.set("one", 100); obj.set({}, "오브젝트"); obj.set(function() {}, "Function"); obj.set(new Number("100"), "인스턴스"); obj.set(NaN, "Not a Number"); for(let [key, value] of obj) { console.log(`${key}: ${value}`); // one: 100, [object Object]: 오브젝트, function() {}: Function, 100: 인스턴스, NaN, Not a Number }
- key 값이 같으면 value가 바뀐다.
let obj = new Map(); const book = {}; obj.set(book, "첫 번째"); obj.set(book, "두 번째"); for(let [key, value] of obj) { console.log(`${key}: ${value}`); // [object Object]: 두 번째 }
- get()
- 형태: Map.prototype.get()
- 파라미터: key 값
-
반환: [key, value] 에서 value, undefined
- Map에서 key 값이 같은 value 반환
- key 값이 같지 않거나 타입이 다르면 undefined 반환
let obj = new Map([ ["one", 100], ["200", "String 타입"] ]); console.log(obj.get("one")); // 100 console.log(obj.get("two")); // undefined console.log(obj.get(200)); // undefined
- 오브젝트 설정과 추출
let obj = new Map(); const like = {sports: "스포츠"}; obj.set(like, {value: "농구"}); console.log(obj.get(like)); // {value: 농구}
- 같은 메모리 주소를 사용
- key 값이 같지 않거나 타입이 다르면 undefined 반환
- has()
- 형태: Map.prototype.has()
- 파라미터: key 값
-
반환: key가 존재하면 true, 아니면 false
- Map 인스턴스에서 key의 존재 여부를 반환
- key가 존재하면 true, 아니면 false
const obj = new Map([ ["one", 100] }); console.log(obj.has("one")); // true console.log(obj.has("two")); // false
- key가 존재하면 true, 아니면 false
Map의 Iterator object
- entries()
- 형태: Map.prototype.entries()
- 파라미터: 파라미터 없음
-
반환: 생성한 이터레이터 오브젝트 생성
- Map 인스턴스로 이터레이터 오브젝트 생성, 반환
- Map 인스턴스에 설정된 순서로 반환
- next()로 [key, value] 반환
const obj = new Map([ ["one", 100], ["two", 200] ]); const iter = obj.entries(); console.log(iter.next()); // {value: [one, 100], done: false} console.log(iter.next()); // {value: [two, 200], done: false} console.log(iter.next()); // {value: undefined, done: true}
- keys()
- 형태: Map.prototype.keys()
- 파라미터: 파라미터 없음
-
반환: 생성한 이터레이터 오브젝트
- Map 인스턴스의 key로 iterator object 생성, 반환
- value는 포함하지 않음
- Map 인스턴스에 설정된 순서로 반환
- next()로 key 반환
const obj = new Map([ ["one", 100], ["two", 200] ]); const iter = obj.keys(); console.log(iter.next()); // {value: one, done: false} console.log(iter.next()); // {value: two, done: false} console.log(iter.next()); // {value: undefined, done: true}
- values()
- 형태: Map.prototype.values()
- 파라미터: 파라미터 없음
-
반환: 생성한 이터레이터 오브젝트
- Map 인스턴스의 value로 iterator object 생성, 반환
- key는 포함하지 않음
- Map 인스턴스에 설정된 순서로 반환
- next()로 key 반환
const obj = new Map([ ["one", 100], ["two", 200] ]); const iter = obj.values(); console.log(iter.next()); // {value: 100, done: false} console.log(iter.next()); // {value: 200, done: false} console.log(iter.next()); // {value: undefined, done: true}
- Symbol.iterator()
- 형태: Map.prototype[Symbol.iterator]
- 파라미터: 파라미터 없음
-
반환: {done: true/false, value: 값]
- Map 인스턴스로 iterator object 생성, 반환
- Map.prototype.entries() 와 같음
- next()로 [key, value] 반환
const obj = new Map([ ["one", 100], ["two", 200] ]); const iter = obj[Symbol.iterator](); console.log(iter.next()); // {value: [one, 100], done: false} console.log(iter.next()); // {value: [two, 200], done: false} console.log(iter.next()); // {value: undefined, done: true}
콜백함수, 삭제, 지우기
- forEach()
- 형태: Map.prototype.forEach()
- 파라미터: callback 함수, this로 참조할 object(option)
-
반환: undefined
- Map 인스턴스를 반복하면서 callback 함수 호출
- map(), filter() 등의 callback 함수가 동반되는 메서드 사용 불가.
- callback 함수에 넘겨주는 파라미터
- value, key, Map 인스턴스 - key, value 순서가 아니다.
const obj = new Map([ ["one", 100], ["two", 200] ]); const callback = (value, key, map) => { console.log(`${key}: ${value}`); }; obj.forEach(callback); // one: 100 / two: 200
- 콜백 함수에서 this 사용
const obj = new Map([ ["one", 100], ["two", 200] ]); function callback(value, key, map) { console.log(`${key}, ${value}, ${this.check}`); } obj.forEach(callback, {check: 50}); // one, 100, 50 / two, 200, 50
- 콜백 함수를 Arrow function으로 작성하면 this는 window object로 두번째 파라미터를 사용하지 못한다.
- value, key, Map 인스턴스 - key, value 순서가 아니다.
- delete()
- 형태: Map.prototype.delete()
- 파라미터: key 값
-
반환: 삭제 성공: true / 실패: false
- Map 인스턴스에서 파라미터 값과 같은 entry 삭제
- 같은 key가 있으면 삭제 후 true 반환, 없으면 false 반환
const obj = new Map([ ["one", 100], [{}, "오브젝트"] ]); console.log(obj.delete("one")); // true console.log(obj.delete({})); // false const sports = {}; obj.set(sports, "스포츠"); console.log(obj.delete(sports)); // true
- obj.delete({}) 에서 [{}, “오브젝트”]에 {}는 다르다 - 참조하는 메모리 주소가 다르다.
- 같은 key가 있으면 삭제 후 true 반환, 없으면 false 반환
- clear()
- 형태: Map.prototype.clear()
- 파라미터: 없음
-
반환: 없음
- Map 인스턴스의 모든 entry를 지움
- Map 인스턴스를 삭제하는 것은 아니다.
- 따라서 [key, value]를 추가할 수 있다.
const obj = new Map([ ["one": 100, "two": 200] ]); console.log(obj.size); // 2 obj.clear(); console.log(obj.size); // 0 obj.set("add", "추가"); console.log(obj.size); // 1
- size 프로퍼티
- Map 인스턴스의 entry 수를 반환
- 개발자 코드로 수정할 수 없다.
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: "농구"};
- let sports = {like: “축구”} 에서 {like: “농구”}로 변경하면 참조하는 메모리가 변경되기 때문에 메모리 leak이 발생한다.
- memory leak 발생
- WeakMap의 object를 GC가 지운다.
- GC: Garbage Collection
- WeakMap 오브젝트 메소드는 CRUD와 관련된 메서드만 있다.
- WeakMap entry 열거 불가
-
이터레이션 불가
- new WeakMap()
- 형태: new WeakMap()
- 파라미터: [이터러블 오브젝트] (option)
- 반환: 생성한 WeakMap 인스턴스
- WeakMap 인스턴스 생성, 반환
- 파라미터 작성
- 대괄호 안에 이터러블 오브젝트 작성
const empty = new WeakMap(); const sports = {}; const obj = new WeakMap([ [sports, "sports object"] ]); console.log(typeof obj); // object
- WeakMap 오브젝트 구조
"use strict" const map = Map; const weakMap = WeakMap; const sports = {}; const obj = new WeakMap([ [sports, "종목"] ]);
- map 과 weakmap이 구조에서 크게 다르지 않는다.
- Map 오브젝트에 Symbol(Symbol.species)가 있지만 WeakMap은 없다.
- map.prototype에 SYmbol.iterator가 있지만 WeakMap은 없다.
- map.prototype에는 forEach()가 있지만 weakMap.prototype에는 forEach()가 없다.
- 오른쪽의 obj를 펼치면 [[Entries]]가 있다.
- [[Entries]]를 펼치면 0: {object=>”종목”} gudxodlek.
- [Object, “종목”] 형태로 작성한 것을 인덱스를 부여하여 배열로 만들고 엘리먼트에 {Object: “종목”} 형태로 변환
- Map 인스턴스와 구조가 같다.
- 대괄호 안에 이터러블 오브젝트 작성
WeakMap 오브젝트 메소드
- get()
- 형태: WeakMap.prototype.get()
- 파라미터: key (object)
- 반환: 엘리먼트 value
- WeakMap 인스턴스에서 key 값이 같은 value 반환
- 존재하지 않으면 undefined 반환
const fn = () => {} const obj = new WeakMap([ [fn, "함수"] ]); console.log(obj.get(fn)); // 함수
- 존재하지 않으면 undefined 반환
- set()
- 형태: WeakMap.prototype.set()
- 파라미터: key (object), value(임의의 값)
- 반환: key, value가 설정된 인스턴스
- WeakMap 인스턴스에 key, value 설정
const fn = function() {} const obj = new WeakMap([ [fn, "함수"] ]); console.log(obj.get(fn)); // 함수 obj.set(fn, "함수 변경"); console.log(obj.get(fn)); // 함수 변경
- 같은 메모리에 fn 변수이기 때문에 value 가 변경된다.
- 첫 번째 파라미터에 key로 사용할 오브젝트 작성 - string과 같은 프리미티브 값 사용 불가
- 두 번째 파라미터는 값
- 첫 번째 파라미터의 오브젝트에 대한 값보다는 오브젝트 구분 등의 용도, 오브젝트에 따라 연동되는 함수 등록
- has()
- 형태: WeakMap.prototype.has()
- 파라미터: key (object)
- 반환: 존재하면 true, 아니면 false
- WeakMap 인스턴스에서 key 값이 존재하면 true, 존재하지 않으면 false
const obj = {}; const weakObj = new WeakMap([ [obj, "오브젝트"] ]); console.log(weakObj.has(obj)); // true
- delete()
- 형태: WeakMap.prototype.delete()
- 파라미터: key (object)
- 반환: 삭제 성공 true, 실패 false
- WeakMap 인스턴스에서 key와 일치하는 entry 삭제
- 삭제 성공하면 true, 실패하면 false
const fn = () => {} const obj = new WeakMap([ [fn, "함수"] ]); console.log(obj.delete(fn)); // true console.log(obj.has(fn)); // false
- 삭제 성공하면 true, 실패하면 false
WeakMap의 GC
- Garbage Collection
- 참조하는 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} object는 GC 대상이되어 엔진이 주기적으로 GC 처리를 한다.
- ()=> {point: 1} 은 garbage가 된다.
- 두 개의 sports가 참조하는 주소가 다르므로 sports가 추가된다.
- WeakMap 인스턴스의 GC 상태
> [[Entries]] > 0: {() => {point: 2} => "변경후"} > 1: {() => {point: 1} => "변경전"}
- 변수값은 하나지만 WeakMap 인스턴스는 두 개가 있다.
- {point: 1}과 {point: 2}의 메모리 주소가 다르며 sports는 사람이 보는 것으로 WeakMap은 값인 메모리 주소가 다르므로 각각 저장
- 그래서 엔진은 key가 아닌 인덱스로 저장하여 관리
- 시간이 지나면 {point: 1}은 GC가 삭제한다.
- let sports = () => {point: 1}; obj.set(sports, “변경전”);
- WeakMap과 Map의 차이
- 참조하는 Object를 삭제하면 Map은 그대로 갖고있지만 WeakMap은 GC가 삭제한다.
** 출처1. 인프런 강좌_자바스크립트 ES6+