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: 0
const 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: 0
const s = new Set(['one', 'two', 'three', 'three', 'four']);
console.log(s.has('two')); // true
console.log(s.size); // 4
const 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
// one
const 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, 4
const 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 => c
class 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 suo2
const 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
결국 경우에 따라 다르다. 일부 정보는 실제로 적절하게 비공개되어야 한다.(코드에서, 디버거에서 비공개로 사용할 수 없음을 기억하자). 다른 정보는 "사용하지 말라"라는 규칙으로 표시하면 괜찮을 것이다.