Map

// Map {} - 일반 객체 리터럴과 비슷하다.
// Set [] - 일반 배열 리터럴과 비슷하다.

//  사실 Map, Set 없을 때도 개발 잘 했다.
//  그런데 왜 Map, Set이 추가되었을까?

// 자바스크립트의 고질적인 문제
//  하나의 기능이 있다면, 그 기능이 자유도가 너무 높다.
//  {} <- 객체만봐도, 사람마다 이 객체를 활용하는 방법이 무궁무진하다.
//  그런데 다른 언어를 보면 Map, Set 처럼 조금 제한되어있는 경우가 많다.

// 자유도가 높으면 어떤 문제가 발생할까?
//  남이 짜놓은 코드를 분석하기가 너무 어렵다.
//  이 객체를 도대체 어떤 목적으로 썼는지, 그거를 분석하기가 너무 어렵다.
//  그래서 Map, Set이라는 전문적인 객체를 만들었다고 보면 된다.

// 일반 객체
//  {a: 'b'}
//  a가 key, b가 값
const m = new Map();
console.log(m); // Map(0) {}

// Map 인스턴스에 원하는 프로퍼티키, 값 추가
m.set('a', 'b');
console.log(m); // Map(1) { 'a' => 'b' }

m.set('c', 'd');
console.log(m); // Map(2) { 'a' => 'b', 'c' => 'd' }

// Map 인스턴스에서 값 꺼내오기
console.log(m.get('a')); // b

// ---------------------------------------------------
// Map의 신기한 부분
//  key를 객체로도 줄 수 있다. 이 부분이 일반 객체랑 다른점이다.
//  일반 객체는 key가 Symbol 아니면 string, 이 2개중에 하나만 됐다.
//  반면에 Map 객체는 key, value를 어떤 값이던지 자유롭게 설정할 수 있다.
m.set({a: 'b'}, {c: 'd'});
console.log(m); // Map(3) { 'a' => 'b', 'c' => 'd', { a: 'b' } => { c: 'd' } }

// 다만 위와 같이 객체를 key로 지정하면 get으로 가지고오려할 때, 안될 것이다.
console.log(m.get({a: 'b'})); // undefined // 왜냐면 여기서 get에 넘긴 객체랑 위에서 set에서 사용한 객체는 참조값이 다르다.
// 겉 모습은 같아보여도 서로 다른 객체인 것이다.
console.log({a: 'b'} !== {a: 'b'}); // true

// 때문에 객체를 key로 사용할거라면 미리 따로 변수에 저장을 하고 사용해야된다.
const obj = {key: 'key'};
m.set(obj, 123);
console.log(m.get(obj)); // 123
console.log(m.size); // 4 // 프로퍼티 키, 값 갯수
// 원래 일반 각체에선 해당 객체의 크기에 관련된 값을 가져올 수 있는게 없다.
// 하지만 Map 객체에서는 size라는 프로퍼티를 활용해 해당 객체의 크기를 가져올 수 있다.

// ------------------------------------------------------------------------
// 일반 객체는 반복문 돌리기 어렵다. for in
const obj2 = {normal: 'obj'};
for (let i in obj2) {
    console.log(obj2[i]); // obj
}
// 이렇게 하면 문제가 obj2의 prototype 속성까지 다 나와버린다.
for (let i in obj2) {
    if (obj2.hasOwnProperty(i)) {
        console.log(obj2[i]); // obj
    }
}
// 그래서 위와 같은 보호장치를 하나 더 추가해주는 경우가 많았다.
// 그런데 위와 같이 하면 너무 지저분하다.
// 그래서 이런 반복문도 개선을한게 Map이라고 보면된다.

for (const [k, v] of m) {
    console.log(k, v);
    // a b
    // c d
    // {a: 'b'} {c: 'd'}
    // {key: 'key} 123
}

// ---------------------------------------------------------------
// 그리고 배열도 아닌데 forEach 메서드를 사용할 수 있다.
m.forEach((v, k) => {
    console.log(k, v);
    // a b
    // c d
    // {a: 'b'} {c: 'd'}
    // {key: 'key} 123
})

// 이렇게 반복문도 훨씬 강화가 됐다.

// ---------------------------------------------------------------
console.log(m.has('c')); // true
console.log(m.has('z')); // false

m.delete('c');
console.log(m.has('c')); // false
console.log(m.has(obj)); // true

m.clear();
console.log(m.has(obj)); // false
console.log(m.size); // 0