안녕 ES6. (3/4) Map, Set, 그리고 WeakMap, WeakSet

Map, Set

Map과 Set은 일반적인 알고리즘을 구현하는데 효과적으로 사용될 수 있는 자료구조이다. 가장쉬운 예제를 통해서 Map의 사용법을 살펴보자.

Map ans Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var testMap = new Map();
testMap.set('my whatever key', 'my whatever value');
console.log(testMap.get('my whatever key'));
// can loop
for (let key of testMap.keys()) {
// key
}
for (let val of testMap.values()) {
// values
}
// the length of the object
console.log('length of testMap is ' + testMap.size);
// key value pair together
for (let [key , val] of testMap.entries()) {
// key ==> val
}
// or use forEach, the return order is reversed
testMap.forEach((val , key) => {
// key ==> val
});
// delete
testMap.delete('my whatever key');
// dump all
testMap.clear();
// check with KEY
if (!testMap.has('my whatever key')) {
console.log('key no longer exists');
}

for … of 구문 또는 forEach 구문을 사용해 쉬게 key와 value를 탐색할 수 있다. 만일, Map을 초기화시에 값을 할당하려면 아래와 같이 2중 배열과 같은 형태로 값을 제공할 수 있다.

1
2
3
4
var myMap = new Map([
['key1' , 'value1'],
['key2' , 'value2']
]);

반면, Set은 중복을 허용하지 않는 순서가 보장된 값들의 집합이다. 또한, 예제를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
var mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");
mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2
for (let item of mySet) console.log(item);
// 1
// "some text"

Set은 배열과 유사하게 사용된다. 실제, Set의 배열은 상호 변환이 가능하다. Set은 배열을 생성자로 받을 수 있고, 배열은 Set으로 부터 생성될 수 있다. 이 경우, Set은 중복된 값을 허용하지 않으므로 제거된다. 통상, 배열을 특정요소의 집합으로 많이 사용하였으나 Set은 몇가지 이점을 제공한다.

  • 중복이 허용되지 않는 경우 배열보다 Set을 사용하는것이 편리하다.
  • 배열에서는 splice를 사용해 값을 잘라내야하는 반면, Set은 요소의 값으로 삭제하는 기능을 제공한다.
  • indexOf 메소드로 값을 확인하는것 보다 has 가 더 빠르다. 또한, NaN는 배열에서 indexOf로 찾을 수 없다.

    WeakMap과 WeakSet

    WeakMap은 오브젝트만을 키로 허용하고 임의의 값을 허용한다. WeakSet도 오브젝트만을 요소로 허용한다. 또한, 객체에 대한 참조가 더이상 존재하지 않을경우 가비지 컬렉션의 대상이 된다. 즉, 언제든지 오브젝트가 GC의 대상이 될수 있기 때문에 WeakMap은 키들의 열거형을 지원하지 않는다.

    아래 예제의 접근법은 생성자 함수에 private 데이터를 제공하게 해준다. ‘let age’는 블록스코프를 가지고 외부에서 접근할 수 없는 값이다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function Person(name) {
    let age = 20; // this is private
    this.name = name; // this is public
    this.greet = function () {
    // here we can access both name and age
    console.log(`name: ${this.name}, age: ${age}`);
    };
    }
    let joe = new Person('Joe');
    joe.greet();
    // here we can access name but not age

하지만, 이경우 모든 Person인스턴스는 age를 생성하게 되고, 메모리를 낭비하는 결과를 초래한다.

WeakMap과 WeakSet을 사용하면 이러한 메모리 낭비를 막고 성능을 개선할 수 있다. let을 이용해 private한 WeakMap을 만들고, 이 WeakMap을 이용해 this와 연관된 private데이터를 조회할 수 있다. 이렇게 하면 모든 인스턴스가 하나의 WeakMap을 공유하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let Person = (function () {
let privateProps = new WeakMap();
class Person {
constructor(name) {
this.name = name; // this is public
privateProps.set(this, {age: 20}); // this is private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
}
}
return Person;
})();
let joe = new Person('Joe');
joe.greet();
// here we can access joe's name but not age

출처: stackoverflow