프로그래밍 언어에서 Collection 이란 단어는 '프로그래밍 언어가 제공하는 값을 담을 수 있는 컨테이너' 라고 생각합니다.
메이저 프로그래밍 언어에는 여러가지 데이터 컬렉션들이 존재합니다.
JavaScript 에서는 다음과 같은 컬렉션들이 존재합니다.
Indexed Collection - Arrays, Typed Array
Keyed Collection - Objects, Map, Set, Weak Map, Weak Set
JavaScript 에서는 ES5 때 까지는 Object와 Array만이 존재 하였습니다.
하지만 ES6 부터 Map, Set, WeakMap, WeakSet, Typed Array를 추가 하였는데요.
이 포스팅에서는 Typed Array 를 제외한 ES6에서 나온 Collection 들에 대해 이야기 하려 합니다.
특정 상황에서 ES6의 컬렉션들을 사용할 시 기존의 Object, Array를 사용하는 것 보다 최적화된 구현체를 제공합니다.
이미 존재하는 값을 추가하려고 하면 아무 일도 일어나지 않습니다.
const mySet = new Set("abcd");
mySet.size; // 4
mySet.add("a");
mySet.size; // 4
바로 어떤 데이터가 자신의 멤버인지 확인하는 작업을 빨리 처리하려는 목적입니다.
const mySet = new Set("abcd");
const myArray = [..."abcd"];
myArray.indexOf("a") !== -1 // true slow
mySet.has("a") // true fast
Set은 인덱스 값으로 데이터를 조회하는 일을 할 수 없습니다.
myArray[0]; // "a"
mySet[0]; // undefined
let mySet = new Set; // 그냥 new 생성자로 빈 Set을 선언할 수 있습니다.
let iterSet = new Set([1, 2, 3]); // 생성할때 iterable한 객체를 초기 값으로 줄 수 있습니다.
console.log(iterSet.size); // size로 Set의 크기를 알 수 있습니다.
console.log(iterSet.has(1)); // has로 Set에 값이 존재하는지 알 수 있습니다.
mySet.add(1).add(2).add(3); // add로 Set에 값을 추가합니다. 이것은 체이닝 될 수 있습니다.
mySet.delete(1).delete(2); // delete로 Set에 값을 삭제할 수 있습니다. 역시 체이닝 될 수 있습니다.
iterSet.forEach((value, value, iter) => v); // Array.prototype.forEach 와 같은 forEach를 제공합니다.
iterSet.clear(); // Set의 모든 값을 삭제합니다.
map
, filter
, some
, every
등의 내장함수 미구현set.addAll(iterable)
, set.removeAll(iterable)
등등물론 이것들은 ES6의 다른 문법들을 통해서 구현 가능합니다.
Map은 Key - Value 의 쌍으로 이루어진 컬렉션입니다.
하지만 JavaScript 에는 Key - Value로 이루어진 Object가 있습니다.
그런데 왜 Map이 나온 것 일까요?
size
나 length
같은 메서드가 없습니다.)여기서 가장 큰 문제점은 일반 객체는 iterable
하지 않기 때문에 iterable
을 사용하는 모든 문법에 객체를 사용할 수 없습니다.
하지만 직면한 문제가 위의 기능들을 필요로 하지 않는다면, 일반 객체를 사용하는 것이 올바른 선택일 수 있습니다.
obj.key
또는 obj[key]
같은 구문을 사용할 수 없습니다. 객체의 값을 가져오기 위해서는 map.get(key)
구문을 이용해야하며 해시 테이블의 멤버 데이터는 속성과 달리 프로퍼티 체인(property chain)을 통해 상속되지 않습니다.const myMap = new Map; // new 생성자로 선언해서 사용할 수 있습니다.
myMap.set('yesdoing', 'looser');
const person = { age: '111', gender: 'none', name: 'yesdoing'};
const whoami = {};
myMap.set(person, whoami); // 객체를 키와 값으로 받을 수 있습니다.
for(const [key, value] of myMap) { // destructuring으로 값을 가져와서 쓸 수 있어요.
console.log(`${key} = ${value}`); // yesdoing = looser [object Object] = [object Object]
}
myMap.get(person); // {}
myMap.forEach(value, key) => { // 다른 forEach와의 호완을 위해서 value, key 순입니다.
console.log(`${key} = ${value}`); // yesdoing = looser [object Object] = [object Object]
}, myMap);
myMap.clear(); // map에 있는 엔트리를 전부 삭제합니다.
console.log(myMap.size); // 0
Map과 Set이 참조하는 객체들은 강하게 연결되어 있습니다. 이 것은 JavaScript의 가비지 컬렉션이 메모리 수거를 못하게 막는 원인이 됩니다. 만약 크기가 큰 Map과 Set의 객체가 더 이상 쓰이지 않는다면 가비지 컬렉션에서 이것을 가져가기 위해 비싼 비용을 치뤄야 합니다.
이것을 해결하기 위해 ES6에서는 Weak Map, Weak Set이 나왔습니다.
이 컬렉션들은 더 이상 사용되지 않을 때, 메모리에서 쉽게 삭제 되기 위해 '약한' 결합을 유지합니다.
Weak Map은 Map과 비슷합니다. 단지 메서드가 몇개 없고, 가비지 컬렉션의 처리가 다릅니다.
const yesdoing = new WeakMap(); // WeakMap을 생성합니다.
const age = {}; // 키는 반드시 객체여야 합니다.
const job = {}; // 키는 반드시 객체여야 합니다.
yesdoing.set(age, 11111); // 키 - 값을 설정합니다.
yesdoing.set(job, 'air'); // 값으로는 어떤 타입이라도 들어올 수 있습니다.
yesdoing.has(job); // True
yesdoing.delete(job) // key를 삭제합니다.
Weak Set 역시 Set과 비슷하지만 메서드가 몇개 없습니다.
const yesdoing = new WeakSet(); // WeakMap을 생성합니다.
const age = {}; // 값은 반드시 객체여야 합니다.
yesdoing.add(age); // 값을 추가합니다.
yesdoing.has(age); // True
yesdoing.delete(age) // 값을 삭제합니다.
Map과 Set은 키 / 값의 쌍으로 이루어 진 ES6에서 새로나온 컬렉션입니다. 하지만 객체 리터럴은 여전히 많은 상황에서 컬렉션으로 활용 될 수 있습니다. 새로운 ES6 컬렉션들이 필요한 상황이 아니라면 굳이 새로운 컬렉션으로 교체할 필요는 없습니다.
좋은 글 감사합니다.
많은 도움이 되었습니다 :)