컬렉션(Collection)
Array
와 Object
를 떠올리게 된다.
Array
), 형식화 배열 (Typed Array
)Object
), Map
, Set
, WeakMap
, WeakSet
Set Collection
let newSetData = new Set("abcd"); // Set(4) { "a", "b", "c", "d" }
newSetData.add("e")
// Set 객체에 값을 추가한다.
// 이미 같은 값이 있다면 중복이므로 요청이 무시된다.
newSetData.size // 5 - Set 객체 내 요소들의 총 개수를 출력한다.
newSetData.has("d")
// true - Set 객체 내에 입력한 요소가 있는지 확인한다.
// 있으면 true를, 없으면 false를 반환한다.
newSetData.forEach((currentValue, currentKey, set) => console.log(currentValue, currentKey, set));
// "a", "a", Set(4) { "a", "b", "c", "d" }
// "b", "b", Set(4) { "a", "b", "c", "d" }
// "c", "c", Set(4) { "a", "b", "c", "d" }
// "d", "d", Set(4) { "a", "b", "c", "d" }
// Array에 쓰이는 메소드인 forEach와 약간 비슷하며, 인자로 콜백 함수를 받는다.
// 콜백 함수의 인자는 3개이다.
// 1번째와 2번째 인자는 각각 객체의 현재값, 객체의 현재 key name을 가리키는데
// Set 객체는 key를 가지지 않으므로 currentValue와 동일한 내용을 가지게 된다.
// 3번째 인자는 현재 값이 포함되어 있는 set 객체 자체를 받아온다.
newSetData.delete("e")
// Set 객체에 있는 일부 요소를 삭제한다.
newSetData.clear();
// newSetData 안에 있는 모든 요소를 삭제한다.
Map Collection
JavaScript의 기존 객체와 동일하게 key와 value의 쌍으로 이루어진 집합이다.
Map 객체 역시 Map 생성자 함수를 통해 생성된다.
Map 객체가 **일반 객체(Object)**와 다른 점이 몇 가지가 있다.
Map 객체는 기본적으로 다음과 같은 메소드들을 제공한다.
let newMapData = new Map([["name", "Piana"],["lang", "KO"]]);
// Map(2) {'name' => 'Piana', 'lang' => 'KO'}
newMapData.set("city", "Seoul")
// Map 객체에 key와 value를 추가한다.
// 이미 같은 key가 있다면 중복이므로 해당 key의 value를 덮어쓰기 한다.
// 만약 key가 객체일 경우, 고유한 객체로 판단하지 않으므로 새로 값이 추가된다.
newMapData.size // 3 - Map 객체 내 요소들의 총 개수를 출력한다.
newMapData.has("name")
// true - Map 객체 내에 입력한 key가 있는지 확인한다.
// 있으면 true를, 없으면 false를 반환한다.
newMapData.get("name")
//
// "Piana" - Map 객체 내에 입력한 key가 있는지 확인한다.
// 있으면 key의 value를, 없으면 undefined를 반환한다.
newMapData.keys()
// MapIterator {'name', 'lang', 'city'}
// Map 객체에 저장된 모든 key를 반환하되, MapIterator라는 객체 안에 담아 반환한다.
newMapData.values("name")
// MapIterator {'Piana', 'KO', 'Seoul'}
// Map 객체에 저장된 모든 value를 반환하되, MapIterator라는 객체 안에 담아 반환한다.
newMapData.entries()
// MapIterator {'name' => 'Piana', 'lang' => 'KO', 'city' => 'Seoul'}
// Map 객체에 저장된 모든 key와 value를 반환하되,
// MapIterator라는 객체 안에 담아 반환한다.
newMapData.forEach((currentValue, currentKey, map) => console.log(currentValue, currentKey, map));
// Piana name Map(3) {'name' => 'Piana', 'lang' => 'KO', 'city' => 'Seoul'}
// KO lang Map(3) {'name' => 'Piana', 'lang' => 'KO', 'city' => 'Seoul'}
// Seoul city Map(3) {'name' => 'Piana', 'lang' => 'KO', 'city' => 'Seoul'}
// Array에 쓰이는 메소드인 forEach와 약간 비슷하며, 인자로 콜백 함수를 받는다.
// 콜백 함수의 인자는 3개이다.
// 1번째 인자는 map 객체의 현재 value을 가리킨다.
// 2번째 인자는 map 객체의 현재 key를 가리킨다.
// 3번째 인자는 현재 값이 포함되어 있는 Map 객체 자체를 받아온다.
newMapData.delete("city")
// Map 객체에 있는 일부 요소를 확인하고 있으면 그 요소를 삭제한다.
// 삭제되면 true로, 아니면 false를 반환한다.
newMapData.clear();
// newMapData안에 있는 모든 요소를 삭제한다.
const user1 = {"name": "Kim", "count": 1}
const user2 = {"name": "Lee", "count": 3}
const user3 = {"name": "Choi", "count": 2}
let shippingList = new Map([[user1, "Seoul"]])
shippingList.set([user2, "Busan"])
shippingList.set([user3, "Gwangju"])
shippingList.values()
// MapIterator {'Seoul', 'Busan', 'Gwangju'}
WeakSet과 WeakMap
Weak라는 단어는 ‘약한’ 이라는 의미를 담고 있다.
이 두 객체는 Set과 Map 객체의 일부 기능만을 사용해 축약한 버전이라고 접근하면 쉽다.
WeakSet과 WeakMap을 사용하는 이유는 외부에서 key로 쓰일 객체를 선언해 Map과 Set 객체에 key로 쓰게 되면, key로 쓰인 객체가 외부에서는 더 이상 사용되지 않더라도 Map과 Set에는 아직 해당 key가 남아있게 된다. (물론 .delete()를 이용하면 해당 key를 지울 수 있다.)
let john = { name: "John" };
let map = new Map();
map.set(john, "..."); // Map 객체에 key로서 john을 배치했다.
john = null; // 참조할 변수 john의 값을 null로 재할당해 덮어썼다.
// 이 경우, 외부에서의 john은 사용될 일이 없지만,
// key로 배정한 john은 여전히 Map 객체에 남아있다.
for(let obj of map.keys()){
alert(JSON.stringify(obj));
} // { name: "John" }
// 따라서 외부에서 john의 데이터가 null로 더 이상 이용되지 않더라도, Map 객체
// 내부에서는 john의 데이터가 key로 남아있어 메모리에 지워지지 않고 존재하게 된다.
이러다보니 메모리에는 그 객체의 데이터가 남아있을 수 밖에 없는데, WeakSet과 WeakMap 객체를 사용하면 외부에서 key로 쓰일 객체가 더 이상 사용되지 않을 시, 가비지 컬렉션에 따라 그 객체 정보를 지우게 된다.
let john = { name: "John" };
let map = new WeakMap();
map.set(john, "...");
john = null; // 참조할 변수의 값을 null로 재할당해 덮어썼다.
// 이 경우, 외부에서 john이 사용될 일이 없어지면서, key로 배정한 john도
// 이 영향으로 참조할 객체가 사라지면서 WeakMap과 메모리에 자동으로 사라지게 된다.
for(let obj of map.keys()){
alert(JSON.stringify(obj));
}
// Uncaught TypeError: map.keys is not a function or its return value is not iterable
이로서 불필요한 메모리 할당 문제를 없애기 위한 방법으로 Map과 Set 객체의 기능을 간추린 WeakSet과 WeakMap을 써 볼 수 있다.
WeakMap과 WeakSet은 기본적으로 Map과 Set 객체의 내용과 기본적으로는 동일하다. 하지만, 몇 가지 제약 사항들이 있다.
WeakMap
WeakMap의 key는 반드시 객체여야 한다.
WeakMap의 메소드는 아래의 메소드들만 사용할 수 있다.
const newWeakMapData = new WeakMap(); // new 연산자를 통해 WeakMap을 선언.
// WeakMap {{…} => 'Kim', {…} => 'Korea'}
const name = { "name ": "First Name" }; // Map의 key는 반드시 객체여야 한다.
const region = { "region" : "Asia" };
newWeakMapData.set(name, "Kim");
newWeakMapData.set(region, "Korea");
// .set 메소드를 통해 key와 value를 지정하는 건 동일하다.
// key는 반드시 객체여야 하지만, value는 어떤 자료형이든 가능하다.
newWeakMapData.has(name); // true
newWeakMapData.has(region); // "Korea"
newWeakMapData.delete(name); // true
newWeakMapData; // WeakMap {{…} => 'Korea'}
WeakSet
WeakSet의 Value는 반드시 객체여야 한다.
WeakSet의 메소드는 아래의 메소드들만 사용할 수 있다.
const newWeakSetData = new WeakSet(); // new 연산자를 통해 WeakSet을 선언.
// WeakSet {}
const name = { name : "Cloe" }
const region = { region : "England" };
const age = { age : 15 };
newWeakSetData.add(name);
newWeakSetData.add(region);
newWeakSetData.add(age);
// .add 메소드를 통해 value를 추가하되, 반드시 그 값은 객체여야 한다.
// WeakSet {{…}, {…}, {…}}
newWeakSetData.has(name); // true
newWeakSetData.delete(age); // true
newWeakSetData; // WeakSet {{…}, {…}}
Set.prototype.forEach() - JavaScript | MDN
[ES6+] Map(), Set() 객체의 특징과 사용법