- Published on
Javascript Set 그리고 Map
- Author
- Name
- yceffort
근데 사실.. 컬렉션 필요없지 않을까
자바스크립트에서 일반적인 Object는 key-value쌍을 끊임 없이 추가할 수 있는 형태로 구성되어 있다. 그래서 사실 컬렉션이 필요하지 않은 것 처럼 보일 수도 있다. 그러나 이따금씩 object로 부족할 때가 있다.
- key 충돌 위험이 존재하는 경우
- 문자열/심볼 이외의 키 값이 필요한 경우
- 객체에 얼마나 많은 속성이 있는지 알아낼 수 있는 효과적인 방법이 필요한 경우
- 객체가 iterable하지 않음. 따라서
for..of
나...
를 사용할 수 없음
es6에 추가된 컬렉션들은 따라서 멤버 데이터를 드러내기 위해 property를 사용하지 않는다. (obj.key
, obj[key]
불가능.) 그리고 이들에는 자유롭게 메소드를 추가할 수 있다.
Set
Set
은 value로 이루어진 컬렉션이다. 그리고 수정가능하다. 배열과 같을 것 같지만, 다르다.
let faces = new Set('😀 😁 😂 🤣 😃 😄 😅')
faces.size // 8
faces.add('😂')
- 일단 (당연하게도) Set에는 같은 value가 중복으로 포함될 수 없다. 기존에 있는걸 추가해도 아무런 변화가 없다.
- Set은 어떤데이터가 자신의 멤버인지 빠르게 확인하기 위한 목적으로 사용한다
- 그러나 Set은 index로 값을 조회할 수는 없다.
set 으로 할 수 있는 것
-
new Set()
: 비어있는 set 생성 -
set.size
: set 데이터 개수 조회 -
set.has(value)
:value
가set
에 존재하는지 조회 -
set.add(value)
-
set.delete(value)
-
sets[Symbol.iterator]()
: set 안의 값을 순회할 수 있는 새로운 이터레이러를 리턴한다. set을 iterable하게 만들어 준다.let faces = new Set('😀 😁 😂 🤣 😃 😄 😅') iteratorFaces = faces[Symbol.iterator]() for (let i of iteratorFaces) { console.log(i) }
set.forEach(f)
set.clear
set.keys()
set.values()
set.entries()
Map
Map은 잘 알려진 것처럼, key-value pair로 이루어진 컬렉션이다.
Map으로 할 수 있는 것
new Map
new Map(pairs)
map.has(key)
map.size
mag.get(key)
map.set(key, value)
map.delete(key)
map.clear()
map[Symbol.iterator]()
===map.entries()
map.forEach(f)
map.keys()
map.values()
javascript 가 다른점
아래의 코드를 보자.
let messi = new Set()
const 리오넬메시 = { name: '리오넬메시' }
const 라이오넬멧시 = { name: '리오넬메시' }
messi.add(리오넬메시)
messi.add(라이오넬멧시)
console.log(messi.size) //2 ????
리오넬메시
와 라이오넬멧시
는 내부의 값이 같아 보이기 때문에, set에 한개의 값만 추가 될 것 같지만 사실은 그렇지 않다. 자바스크립트에서는 두개의 값을 다르게 본다. 이유는 자바스크립트가 값을 비교할 때 두가지 다른 방법을 사용하기 때문이다.
- string, number같은 primitive는 값을 비교한다
- array, date, object 등은 reference를 비교한다. (메모리의 같은 위치를 참조하고 있는가?)
const 리오넬메시 = { name: '리오넬메시' }
const 라이오넬멧시 = { name: '리오넬메시' }
const 메석대 = 리오넬메시
메석대 === 리오넬메시 // true
메석대 == 라이오넬멧시 // false
리오넬메시 === 라이오넬멧시 //false
본질적으로 같은 메모리를 참조하는 값 끼리만 true를 반환하는 것을 볼 수 있다. (두 object를 비교하는 방법은 여기를 참조)
다시 Set
으로 돌아와서, javascript는 저 두 값을 제대로 비교하지 못하기 때문에 set에 두개의 값이 들어가게 된다. 물론 해시코드를 사용하면 가능하지만, javascipt에는 그런거 없다
또 하나 다른 점이라고 한다면, map
과 set
에 추가한 순서가 곧 순회하는 순서와 같다는 것이다. 이 역시 다른 언어들과는 다른 점이다.
WeakMap, WeakSet
WeakMap
은new
.has()
.get()
.set()
.delete()
만 지원한다WeakSet
은new
.has()
.add()
.delete()
만 지원한다.WeakSet
과WeakMap
의 key는 반드시object
여야 한다.
그렇다. 열거형이 존재하지 않는다. 그 이유는 참조하고 있는 오브젝트가 사라지면 해당 key, value가 사라지는 WeakMap
, WeakSet
의 특징 때문이다.
let john = { name: 'John' }
// 객체에 접근 가능, 해당 객체는 메모리에서 참조되고 있음.
// 참조를 null로 overwrite
john = null
// 객체는 메모리에서 이제 삭제됨
let john = { name: 'John' }
let array = [john]
john = null // 참조를 null로 overwrite
// john은 객체안에 살아 있기 때문에 가비지 컬렉팅이 되지 않음.
// 그래서 array[0]으로 접근 가능
array[0]
// { name: "John" }
이는 기존 Map, Set에서도 동일하다.
let john = { name: 'John' }
let map = new Map()
map.set(john, '윅')
john = null // 참조를 null로 overwrite
// john은 맵안에서 살아있기 때문에
// map.keys() 로 접근 가능
그러나 WeakMap, WeakSet은 다르다
let john = { name: 'John' }
let weakMap = new WeakMap()
weakMap.set(john, '윅')
john = null // 참조를 null로 overwrite
// john은 메모리에서 사라짐 (가비지 콜렉팅 당함)
그럼 도대체 이것은 언제 쓸까? 객체가 사라지면 자동으로 가비지 콜렉팅 해준다는 특성을 활용해, 아래와 같은 것이 가능하다.
let john = { name: 'John' }
// map: 유저 => 방문횟수
let visitsCountMap = new Map()
visitsCountMap.set(john, 123)
// john이 사라짐
john = null
// 그러나 Map에서는 계속 남아 있으므로, 따로 처리를 해주어야 함.
// 또한 john은 map에서 key로 사용하고 있으므로 메모리에서도 존재함.
console.log(visitsCountMap.size) // 1
그러나 여기서 WeakMap을 사용하면, 자동으로 가비지 콜렉팅이 되므로 Map에 남아있는 key에 대해서 까지 신경쓰지 않아도 된다.