2023.06.16
// Create a map
console.log('Creating');
const m = new Map();
// Add some entries
console.log('Adding four entries');
m.set(60, 'sixty');
m.set(4, 'four');
// `set` returns the map, so you can chain `set` calls together
m.set(50, 'fifty').set(3, 'three');
// See how many entries are in the map
console.log(`Entries: ${m.size}`); // Entries: 4
// Get an entry's value
let value = m.get(60);
console.log(`60: ${value}`); // 60: sixty
console.log(`3: ${m.get(3)}`); // 3: three
// `get` returns `undefined` if no entry has the given key
console.log(`14: ${m.get(14)}`); // 14: undefined
// Keys are not strings
console.log('Look for key "4" instead of 4:');
console.log(`"4": ${m.get('4')}`); // "4": undefined (the key is 4, not "4")
console.log('Look for key 4:');
console.log(`4: ${m.get(4)}`); // 4: four
// Update an entry
console.log('Updating entry for key 3 to THREE');
m.set(3, 'THREE');
console.log(`3: ${m.get(3)}`); // 3: THREE
console.log(`Entries: ${m.size}`); // Entries: 4 (still)
// Delete an entry
console.log('Deleting the entry for key 4');
m.delete(4);
// Check if an entry exists
console.log(`Entry for 7? ${m.has(7)}`); // Entry for 7? false
console.log(`Entry for 3? ${m.has(3)}`); // Entry for 3? true
// Keys don't have to be all of the same type
m.set('testing', 'one two three');
console.log(m.get('testing')); // one two three
// Keys can be objects
const obj1 = {};
m.set(obj1, 'value for obj1');
console.log(m.get(obj1)); // value for obj1
// Different objects are always different keys, even if they have
// the same properties
const obj2 = {};
m.set(obj2, 'value for obj2');
console.log(`obj1: ${m.get(obj1)}`); // obj1: value for obj1
console.log(`obj2: ${m.get(obj2)}`); // obj2: value for obj2
// null, undefined, and NaN are perfectly valid keys
m.set(null, 'value for null');
m.set(undefined, 'value for undefined');
m.set(NaN, 'value for NaN');
console.log(`null: ${m.get(null)}`); // null: value for null
console.log(`undefined: ${m.get(undefined)}`); // undefined: value for undefined
console.log(`NaN: ${m.get(NaN)}`); // NaN: value for NaN
// Delete all entries
console.log('Clearing the map');
m.clear();
console.log(`Entries now: ${m.size}`); // Entries now: 0const m = new Map([
  ['one', 'uno'],
  ['two', 'due'],
  ['three', 'tre'],
]);
for (const [key, value] of m) {
  console.log(`${key} => ${value}`);
}
// one => uno
// two => due
// three => tre
// 물론, 디스트럭처링을 사용할 필요는 없다. 루프 본문에서 배열을 사용할 수 있다.
for (const entry of m) {
  console.log(`${entry[0]} => ${entry[1]}`);
}class MyMap extends Map {
  filter(predicate, thisArg) {
    const newMap = new (this.constructor[Symbol.species] || MyMap)();
    for (const [key, value] of this) {
      if (predicate.call(thisArg, key, value, this)) {
        newMap.set(key, value);
      }
    }
    return newMap;
  }
}
// Usage:
const m1 = new MyMap([
  ['one', 'uno'],
  ['two', 'due'],
  ['three', 'tre'],
]);
const m2 = m1.filter((key) => key.includes('t'));
for (const [key, value] of m2) {
  console.log(`${key} => ${value}`);
}
// two => due
// three => tre
console.log(`m2 instanceof MyMap? ${m2 instanceof MyMap}`);
// m2 instanceof MyMap? true평균적으로 컬렉션의 요소수에 대해 하위 선형인 접근 시간을 제공하는 해시 테이블 또는 기타 메커니즘을 사용하여 구현해야 한다.
map.set(key, value);
// 위 코드는 평균적으로 아래 코드보다 빨라야 한다.
if (!array.some((e) => e.key === key)) {
  array.push({ key, value });
}// Create a set
console.log('Creating');
const s = new Set();
// Add some entries
console.log('Adding four entries');
s.add('two');
s.add('four');
// The `add` method returns the set, so you can chain `add` calls together
s.add('one').add('three');
// Check if an entry exists
console.log(`Has "two"? ${s.has('two')}`); // Has "two"? true
// See how many entries are in the set
console.log(`Entries: ${s.size}`); // Entries: 4
// Adding the same values again doesn't add them
s.add('one').add('three');
console.log(`Entries: ${s.size}`); // Entries: 4 (still)
// Delete an entry
console.log('Deleting entry "two"');
s.delete('two');
console.log(`Has "two"? ${s.has('two')}`); // Has "two"? false
// Clear the set (delete all entries)
console.log('Clearing the set');
s.clear();
console.log(`Entries: ${s.size}`); // Entries: 0const s = new Set(['one', 'two', 'three', 'three', 'four']);
console.log(s.has('two')); // true
console.log(s.size); // 4const s = new Set(['one', 'two', 'three']);
for (const value of s) {
  console.log(value);
}
s.add('one'); // Again
for (const value of s) {
  console.log(value);
}
// one
// two
// three
s.delete('one');
s.add('one');
for (const value of s) {
  console.log(value);
}
// two
// three
// oneconst a1 = [1, 2, 3, 4, 1, 2, 3, 4];
const a2 = Array.from(new Set(a1));
console.log(a2.length); // 4
console.log(a2.join(', ')); // 1, 2, 3, 4const s = new Set(['a', 'b', 'c']);
for (const value of s) {
  // or `of s.values()`
  console.log(value);
}
// a
// b
// c
for (const key of s.keys()) {
  console.log(key);
}
// a
// b
// c
for (const [key, value] of s.entries()) {
  console.log(`${key} => ${value}`);
}
// a => a
// b => b
// c => cclass MySet extends Set {
  addAll(iterable) {
    for (const value of iterable) {
      this.add(value);
    }
    return this;
  }
}
// Usage
const s = new MySet();
s.addAll(['a', 'b', 'c']);
s.addAll([1, 2, 3]);
for (const value of s) {
  console.log(value);
}
// a
// b
// c
// 1
// 2
// 3해시 테이블 또는 다른 메커니즘을 사용하여 평균적으로 컬렉션에 있는 요소의 수의 선형이 아닌 접근 시간을 제공해야 한다.
set.add(value);
// 위 코드는 평균적으로 아래 코드보다 빨라야 한다.
if (!array.includes(value)) {
  array.push(value);
}const Example = (() => {
  const privateMap = new WeakMap();
  return class Example {
    constructor() {
      privateMap.set(this, 0);
    }
    incrementCounter() {
      const result = privateMap.get(this) + 1;
      privateMap.set(this, result);
      return result;
    }
    showCounter() {
      console.log(`Counter is ${privateMap.get(this)}`);
    }
  };
})();
const e1 = new Example();
e1.incrementCounter();
console.log(e1); // (some representation of the object)
const e2 = new Example();
e2.incrementCounter();
e2.incrementCounter();
e2.incrementCounter();
e1.showCounter(); // Counter is 1
e2.showCounter(); // Counter is 3(async () => {
  const statusDisplay = document.getElementById('status');
  const personDisplay = document.getElementById('person');
  try {
    // DOM 요소와 관련된 정보를 보유할 위크맵
    const personMap = new WeakMap();
    await init();
    async function init() {
      const peopleList = document.getElementById('people');
      const people = await getPeople();
      // 이 루프에서 div를 키로 사용하여 위크맵의 각 div와 관련된 사람을 저장한다.
      for (const person of people) {
        const personDiv = createPersonElement(person);
        personMap.set(personDiv, person);
        peopleList.appendChild(personDiv);
      }
    }
    async function getPeople() {
      // 서버 또는 이와 유사한 것에서 사람 정보를 가져오는 작업
      return [
        { name: 'Joe Bloggs', position: 'Front-End Developer' },
        { name: 'Abha Patel', position: 'Senior Software Architect' },
        { name: 'Guo Wong', position: 'Database Analyst' },
      ];
    }
    function createPersonElement(person) {
      const div = document.createElement('div');
      div.className = 'person';
      div.innerHTML = '<a href="#show" class="remove">X</a> <span class="name"></span>';
      div.querySelector('span').textContent = person.name;
      div.querySelector('a').addEventListener('click', removePerson);
      div.addEventListener('click', showPerson);
      return div;
    }
    function stopEvent(e) {
      e.preventDefault();
      e.stopPropagation();
    }
    function showPerson(e) {
      stopEvent(e);
      // 위크맵에서 클릭한 요소를 찾아 사람을 표시한다.
      const person = personMap.get(this);
      if (person) {
        const { name, position } = person;
        personDisplay.textContent = `${name}'s position is: ${position}`;
      }
    }
    function removePerson(e) {
      stopEvent(e);
      this.closest('div').remove();
    }
  } catch (error) {
    statusDisplay.innerHTML = `Error: ${error.message}`;
  }
})();위크맵 키/값 쌍의 키로 사용되는 객체가 해당 위크맵 내에서 시작하는 참조 체인을 따라야 도달할 수 있는 경우 해당 키/값 쌍은 접근할 수 없으며 위크맵에서 자동으로 제거된다.
const SingleUseObject = (() => {
  const used = new WeakSet();
  return class SingleUseObject {
    constructor(name) {
      this.name = name;
    }
    use() {
      if (used.has(this)) {
        throw new Error(`${this.name} has already been used`);
      }
      console.log(`Using ${this.name}`);
      used.add(this);
    }
  };
})();
const suo1 = new SingleUseObject('suo1');
const suo2 = new SingleUseObject('suo2');
suo1.use(); // Using suo1
try {
  suo1.use();
} catch (e) {
  console.error('Error: ' + e.message); // Error: suo1 has already been used
}
suo2.use(); // Using suo2const Thingy = (() => {
  const known = new WeakSet();
  let nextId = 1;
  return class Thingy {
    constructor(name) {
      this.name = name;
      this.id = nextId++;
      Object.freeze(this);
      known.add(this);
    }
    action() {
      if (!known.has(this)) {
        throw new Error('Unknown Thingy');
      }
      // Code here knows that this object was created
      // by this class
      console.log(`Action on Thingy #${this.id} (${this.name})`);
    }
  };
})();
// In other code using it:
// Using real ones
const t1 = new Thingy('t1');
t1.action(); // Action on Thingy #1 (t1)
const t2 = new Thingy('t2');
t2.action(); // Action on Thingy #2 (t2)
// Trying to use a fake one
const faket2 = Object.create(Thingy.prototype);
faket2.name = 'faket2';
faket2.id = 2;
Object.freeze(faket2);
faket2.action(); // Error: Unknown Thingy결국 경우에 따라 다르다. 일부 정보는 실제로 적절하게 비공개되어야 한다.(코드에서, 디버거에서 비공개로 사용할 수 없음을 기억하자). 다른 정보는 "사용하지 말라"라는 규칙으로 표시하면 괜찮을 것이다.