├── .npmrc ├── .gitattributes ├── tsconfig.json ├── .gitignore ├── index.d.ts ├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── package.json ├── license ├── index.js ├── readme.md ├── index.test.ts └── core-js.test.js /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | Desktop.ini 4 | ._* 5 | Thumbs.db 6 | *.tmp 7 | *.bak 8 | *.log 9 | *.lock 10 | logs 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/ban-types -- It matches WeakMap’s 2 | export default class ManyKeysWeakMap extends WeakMap {} 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | env: {} 2 | 3 | # FILE GENERATED WITH: npx ghat fregante/ghatemplates/node 4 | # SOURCE: https://github.com/fregante/ghatemplates 5 | # OPTIONS: {"exclude":["jobs.Build"]} 6 | 7 | name: CI 8 | on: 9 | - pull_request 10 | - push 11 | jobs: 12 | Lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: 16 19 | - name: install 20 | run: npm ci || npm install 21 | - name: XO 22 | run: npx xo 23 | Test: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: install 28 | run: npm ci || npm install 29 | - name: build 30 | run: npm run build --if-present 31 | - name: Vitest 32 | run: npx vitest 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "many-keys-weakmap", 3 | "version": "2.0.0", 4 | "description": "A `WeakMap` subclass with support for multiple keys for one entry.", 5 | "keywords": [ 6 | "any", 7 | "key", 8 | "keys", 9 | "many", 10 | "map", 11 | "multi", 12 | "multimap", 13 | "multiple" 14 | ], 15 | "repository": "fregante/many-keys-weakmap", 16 | "funding": "https://github.com/sponsors/fregante", 17 | "license": "MIT", 18 | "author": "Federico Brigante (https://fregante.com)", 19 | "type": "module", 20 | "exports": "./index.js", 21 | "main": "./index.js", 22 | "types": "index.d.ts", 23 | "files": [ 24 | "index.js", 25 | "index.d.ts" 26 | ], 27 | "scripts": { 28 | "test": "xo && vitest && tsc --noEmit" 29 | }, 30 | "devDependencies": { 31 | "@sindresorhus/tsconfig": "^3.0.1", 32 | "typescript": "^4.9.5", 33 | "vitest": "^0.28.5", 34 | "xo": "^0.53.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Federico Brigante (https://fregante.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const baseMap = Symbol('baseMap'); 2 | 3 | class Value { 4 | constructor(value) { 5 | this.value = value; 6 | } 7 | } 8 | 9 | function getLastMap({[baseMap]: map}, keys, create) { 10 | if (!Array.isArray(keys)) { 11 | throw new TypeError('The keys parameter must be an array'); 12 | } 13 | 14 | for (const key of keys) { 15 | if (!map.has(key)) { 16 | if (create) { 17 | map.set(key, new WeakMap()); 18 | } else { 19 | return undefined; 20 | } 21 | } 22 | 23 | map = map.get(key); 24 | } 25 | 26 | return map; 27 | } 28 | 29 | export default class ManyKeysWeakMap extends WeakMap { 30 | constructor() { 31 | super(); 32 | this[baseMap] = new WeakMap(); 33 | 34 | const [pairs] = arguments; // WeakMap compat 35 | if (pairs === null || pairs === undefined) { 36 | return; 37 | } 38 | 39 | if (typeof pairs[Symbol.iterator] !== 'function') { 40 | throw new TypeError(typeof pairs + ' is not iterable (cannot read property Symbol(Symbol.iterator))'); 41 | } 42 | 43 | for (const [keys, value] of pairs) { 44 | this.set(keys, value); 45 | } 46 | } 47 | 48 | set(keys, value) { 49 | const lastMap = getLastMap(this, keys, true); 50 | lastMap.set(Value, value); 51 | return this; 52 | } 53 | 54 | get(keys) { 55 | const lastMap = getLastMap(this, keys); 56 | return lastMap ? lastMap.get(Value) : undefined; 57 | } 58 | 59 | has(keys) { 60 | const lastMap = getLastMap(this, keys); 61 | return Boolean(lastMap) && lastMap.has(Value); 62 | } 63 | 64 | delete(keys) { 65 | const lastMap = getLastMap(this, keys); 66 | return Boolean(lastMap) && lastMap.delete(Value); 67 | } 68 | 69 | get [Symbol.toStringTag]() { 70 | return 'ManyKeysWeakMap'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # many-keys-weakmap [![][badge-gzip]][link-bundlephobia] 2 | 3 | [badge-gzip]: https://img.shields.io/bundlephobia/minzip/many-keys-weakmap.svg?label=gzipped 4 | [link-bundlephobia]: https://bundlephobia.com/result?p=many-keys-weakmap 5 | 6 | > A `WeakMap` subclass with support for multiple keys for one entry. 7 | 8 | A `ManyKeysWeakMap` object is identical to a regular `WeakMap`, which the exception that it only supports a _sequence of keys_ as key, instead of a single key. This will let you attach a value to a specific combination of keys, instead of a single key. 9 | 10 | ```js 11 | const regularMap = new WeakMap(); 12 | const obj = {}; 13 | regularMap.set(obj, true); 14 | 15 | const manyKeysWeakMap = new ManyKeysWeakMap(); 16 | const date = new Date(); 17 | manyKeysWeakMap.set([obj, date], true); 18 | ``` 19 | 20 | Or: 21 | 22 | ```js 23 | const handlers = new ManyKeysWeakMap(); 24 | handlers.set([element, sub1], fn1); 25 | const someOptions = {passive: true}; 26 | handlers.set([element, sub2, someOptions], fn2); 27 | ``` 28 | 29 | The number of keys allowed is unlimited and their order is relevant. 30 | 31 | 32 | ## Install 33 | 34 | ``` 35 | $ npm install many-keys-weakmap 36 | ``` 37 | 38 | 39 | ## Usage 40 | 41 | It should work exactly the same as a `WeakMap`, except that the `key` must always be an array. 42 | 43 | ```js 44 | const ManyKeysWeakMap = require('many-keys-weakmap'); 45 | 46 | const groups = new ManyKeysWeakMap(); 47 | groups.set([header, admin], true); 48 | groups.set([target, tools], [1, 'any value is supported']); 49 | 50 | const key1 = {}; 51 | const keyA = {}; 52 | const keyB = {}; 53 | const data = new ManyKeysWeakMap([ 54 | [[key1], 'value'], 55 | [[keyA, keyB], new Date()] 56 | ]); 57 | 58 | data.get([key1]); 59 | // => 'value' 60 | 61 | data.get([keyA, keyB]); 62 | // => date Object 63 | 64 | data.get([{}]); 65 | // => undefined 66 | 67 | for (const [keys, value] of data) { 68 | console.log(keys); 69 | console.log(value); 70 | } 71 | // => [key1] 72 | // => 'value' 73 | // => [key1, key2] 74 | // => date Object 75 | ``` 76 | 77 | # Related 78 | 79 | - [many-keys-map](https://github.com/fregante/many-keys-map) - A `WeakMap` subclass with support for multiple keys for one entry. 80 | -------------------------------------------------------------------------------- /index.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/ban-types, no-new */ 2 | import {test, assert} from 'vitest'; 3 | import ManyKeysWeakMap from './index.js'; 4 | 5 | // AVA shim 6 | const t = { 7 | is: assert.equal, 8 | true: assert.isTrue, 9 | false: assert.isFalse, 10 | }; 11 | 12 | const W = {}; 13 | const O = {}; 14 | const T = {}; 15 | 16 | test('Basics', () => { 17 | const map = new ManyKeysWeakMap(); 18 | t.true(map instanceof WeakMap); 19 | t.is(map.get.length, 1); 20 | t.is(map.set.length, 2); 21 | t.is(map.delete.length, 1); 22 | }); 23 | 24 | test('Types', () => { 25 | new ManyKeysWeakMap(); 26 | 27 | // @ts-expect-error -- Must be object 28 | new ManyKeysWeakMap(); 29 | // @ts-expect-error -- Must be object 30 | new ManyKeysWeakMap(); 31 | // @ts-expect-error -- Must be object 32 | new ManyKeysWeakMap(); 33 | 34 | const map = new ManyKeysWeakMap(); 35 | map.set([W, O], 1); 36 | map.set([W, O], 'string'); 37 | 38 | // @ts-expect-error -- Must match type provided in generic 39 | map.set([W, O], {}); 40 | // @ts-expect-error -- Must match type provided in generic 41 | map.set([W, O], Symbol('na')); 42 | 43 | const value: number | string | undefined = map.get([W, O]); 44 | 45 | // Shush "unused" vars 46 | new WeakMap([[O, value]]); 47 | }); 48 | 49 | test('Set', () => { 50 | const map = new ManyKeysWeakMap(); 51 | map.set([W], 'first'); 52 | map.set([W], 'second'); 53 | map.set([O, W], 'third'); 54 | map.set([O, W, T], 'fourth'); 55 | 56 | // Also make sure that the same map is returned 57 | t.is(map.set([W, O], 0), map); 58 | }); 59 | 60 | test('Get', () => { 61 | const map = new ManyKeysWeakMap([ 62 | [[W], 'first'], 63 | [[O, W], 'second'], 64 | [[O, W, T], 'third'], 65 | ]); 66 | 67 | t.is(map.get([W]), 'first'); 68 | t.is(map.get([O, W]), 'second'); 69 | t.is(map.get([O, W, T]), 'third'); 70 | t.is(map.get([O]), undefined); 71 | t.is(map.get([O, T]), undefined); 72 | t.is(map.get([O, T, W]), undefined); 73 | 74 | map.set([O, W], 'one'); 75 | map.set([W, O], 'two'); 76 | t.is(map.get([O, W]), 'one'); 77 | t.is(map.get([W, O]), 'two'); 78 | }); 79 | 80 | test('Has', () => { 81 | const map = new ManyKeysWeakMap([ 82 | [[W], 'first'], 83 | [[O, W], 'second'], 84 | [[O, W, T], 'third'], 85 | ]); 86 | 87 | t.true(map.has([W])); 88 | t.true(map.has([O, W])); 89 | t.true(map.has([O, W, T])); 90 | t.false(map.has([O])); 91 | t.false(map.has([O, T])); 92 | t.false(map.has([O, T, W])); 93 | }); 94 | 95 | test('Delete', () => { 96 | const object = {}; 97 | 98 | const map = new ManyKeysWeakMap([ 99 | [[W], 'first'], 100 | [[O, W], 'second'], 101 | [[O, W, T], 'third'], 102 | [[object], 'fourth'], 103 | [[object, object], 'fifth'], 104 | ]); 105 | 106 | t.true(map.delete([W])); 107 | t.false(map.delete([W])); 108 | t.true(map.delete([O, W])); 109 | t.true(map.delete([O, W, T])); 110 | t.true(map.delete([object, object])); 111 | t.false(map.delete([object, object])); 112 | t.true(map.delete([object])); 113 | }); 114 | 115 | test('All types of keys', () => { 116 | const map = new ManyKeysWeakMap(); 117 | 118 | let key = {}; 119 | t.is(map.set([key], 'object').get([key]), 'object'); 120 | t.true(map.delete([key])); 121 | 122 | key = []; 123 | t.is(map.set([key], 'array').get([key]), 'array'); 124 | t.true(map.delete([key])); 125 | }); 126 | -------------------------------------------------------------------------------- /core-js.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright(c) 2014 - 2019 Denis Pushkarev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | */ 22 | 23 | // Source 24 | // https://github.com/zloirock/core-js/blob/3dfb876e2188c9d04f957ebfd76861591d80abf7/tests/tests/es.weak-map.js 25 | 26 | import {test, assert} from 'vitest'; 27 | import ManyKeysWeakMap from './index.js'; 28 | 29 | // AVA shim 30 | const t = { 31 | ...assert, 32 | is: assert.equal, 33 | true: assert.isTrue, 34 | false: assert.isFalse, 35 | }; 36 | 37 | function createIterable(elements, methods) { 38 | const iterable = { 39 | called: false, 40 | received: false, 41 | [Symbol.iterator]() { 42 | iterable.received = true; 43 | let index = 0; 44 | const iterator = { 45 | next() { 46 | iterable.called = true; 47 | return { 48 | value: elements[index++], 49 | done: index > elements.length, 50 | }; 51 | }, 52 | }; 53 | if (methods) { 54 | for (const key of Object.keys(methods)) { 55 | iterator[key] = methods[key]; 56 | } 57 | } 58 | 59 | return iterator; 60 | }, 61 | }; 62 | return iterable; 63 | } 64 | 65 | test('ManyKeysWeakMap', () => { 66 | t.is(typeof ManyKeysWeakMap, 'function'); 67 | t.is(ManyKeysWeakMap.name, 'ManyKeysWeakMap'); 68 | t.is(ManyKeysWeakMap.length, 0); 69 | t.true('delete' in ManyKeysWeakMap.prototype, 'delete in ManyKeysWeakMap.prototype'); 70 | t.true('get' in ManyKeysWeakMap.prototype, 'get in ManyKeysWeakMap.prototype'); 71 | t.true('has' in ManyKeysWeakMap.prototype, 'has in ManyKeysWeakMap.prototype'); 72 | t.true('set' in ManyKeysWeakMap.prototype, 'set in ManyKeysWeakMap.prototype'); 73 | t.true(new ManyKeysWeakMap() instanceof ManyKeysWeakMap, 'new ManyKeysWeakMap instanceof ManyKeysWeakMap'); 74 | let object = {}; 75 | t.is(new ManyKeysWeakMap(createIterable([ 76 | [[object], 42], 77 | ])).get([object]), 42, 'Init from iterable'); 78 | let weakmap = new ManyKeysWeakMap(); 79 | const frozen = Object.freeze({}); 80 | weakmap.set([frozen], 42); 81 | t.is(weakmap.get([frozen]), 42, 'Support frozen objects'); 82 | weakmap = new ManyKeysWeakMap(); 83 | weakmap.set([frozen], 42); 84 | t.is(weakmap.has([frozen]), true, 'works with frozen objects, #1'); 85 | t.is(weakmap.get([frozen]), 42, 'works with frozen objects, #2'); 86 | weakmap.delete([frozen]); 87 | t.is(weakmap.has([frozen]), false, 'works with frozen objects, #3'); 88 | t.is(weakmap.get([frozen]), undefined, 'works with frozen objects, #4'); 89 | let done = false; 90 | try { 91 | // eslint-disable-next-line no-new 92 | new ManyKeysWeakMap(createIterable([null, 1, 2], { 93 | return() { 94 | done = true; 95 | return done; 96 | }, 97 | })); 98 | } catch {/* */} 99 | 100 | t.true(done, '.return #throw'); 101 | t.true(!('clear' in ManyKeysWeakMap.prototype), 'should not contains `.clear` method'); 102 | const array = []; 103 | done = false; 104 | array['@@iterator'] = undefined; 105 | array[Symbol.iterator] = function () { 106 | done = true; 107 | return Array.prototype[Symbol.iterator].call(this); 108 | }; 109 | 110 | // eslint-disable-next-line no-new 111 | new ManyKeysWeakMap(array); 112 | t.true(done); 113 | object = {}; 114 | new ManyKeysWeakMap().set([object], 1); 115 | 116 | t.deepEqual(Object.getOwnPropertyNames(object), []); 117 | t.deepEqual(Object.getOwnPropertySymbols(object), []); 118 | 119 | t.deepEqual(Reflect.ownKeys(object), []); 120 | 121 | const Subclass = class extends ManyKeysWeakMap {}; 122 | t.true(new Subclass() instanceof Subclass, 'correct subclassing with native classes #1'); 123 | t.true(new Subclass() instanceof ManyKeysWeakMap, 'correct subclassing with native classes #2'); 124 | object = {}; 125 | t.is(new Subclass().set([object], 2).get([object]), 2, 'correct subclassing with native classes #3'); 126 | }); 127 | 128 | test('ManyKeysWeakMap#delete', () => { 129 | t.is(typeof ManyKeysWeakMap.prototype.delete, 'function'); 130 | t.is(ManyKeysWeakMap.prototype.delete.name, 'delete'); 131 | t.is(ManyKeysWeakMap.prototype.delete.length, 1); 132 | t.false(Object.prototype.propertyIsEnumerable.call(ManyKeysWeakMap.prototype, 'delete')); 133 | const a = {}; 134 | const b = {}; 135 | const weakmap = new ManyKeysWeakMap(); 136 | weakmap.set([a], 42); 137 | weakmap.set([b], 21); 138 | t.true(weakmap.has([a]) && weakmap.has([b]), 'ManyKeysWeakMap has values before .delete()'); 139 | weakmap.delete([a]); 140 | t.true(!weakmap.has([a]) && weakmap.has([b]), 'ManyKeysWeakMap hasn`t value after .delete()'); 141 | t.doesNotThrow(() => !weakmap.delete([1]), 'return false on primitive'); 142 | const object = {}; 143 | weakmap.set([object], 42); 144 | Object.freeze(object); 145 | t.true(weakmap.has([object]), 'works with frozen objects #1'); 146 | weakmap.delete([object]); 147 | t.true(!weakmap.has([object]), 'works with frozen objects #2'); 148 | }); 149 | 150 | test('ManyKeysWeakMap#get', () => { 151 | t.is(typeof ManyKeysWeakMap.prototype.get, 'function'); 152 | t.is(ManyKeysWeakMap.prototype.get.name, 'get'); 153 | t.is(ManyKeysWeakMap.prototype.get.length, 1); 154 | t.false(Object.prototype.propertyIsEnumerable.call(ManyKeysWeakMap.prototype, 'get')); 155 | const weakmap = new ManyKeysWeakMap(); 156 | t.is(weakmap.get([{}]), undefined, 'ManyKeysWeakMap .get() before .set() return undefined'); 157 | let object = {}; 158 | weakmap.set([object], 42); 159 | t.is(weakmap.get([object]), 42, 'ManyKeysWeakMap .get() return value'); 160 | weakmap.delete([object]); 161 | t.is(weakmap.get([object]), undefined, 'ManyKeysWeakMap .get() after .delete() return undefined'); 162 | t.doesNotThrow(() => weakmap.get([1]) === undefined, 'return undefined on primitive'); 163 | object = {}; 164 | weakmap.set([object], 42); 165 | Object.freeze(object); 166 | t.is(weakmap.get([object]), 42, 'works with frozen objects #1'); 167 | weakmap.delete([object]); 168 | t.is(weakmap.get([object]), undefined, 'works with frozen objects #2'); 169 | }); 170 | 171 | test('ManyKeysWeakMap#has', () => { 172 | t.is(typeof ManyKeysWeakMap.prototype.has, 'function'); 173 | t.is(ManyKeysWeakMap.prototype.has.name, 'has'); 174 | t.is(ManyKeysWeakMap.prototype.has.length, 1); 175 | t.false(Object.prototype.propertyIsEnumerable.call(ManyKeysWeakMap.prototype, 'has')); 176 | const weakmap = new ManyKeysWeakMap(); 177 | t.true(!weakmap.has([{}]), 'ManyKeysWeakMap .has() before .set() return false'); 178 | let object = {}; 179 | weakmap.set([object], 42); 180 | t.true(weakmap.has([object]), 'ManyKeysWeakMap .has() return true'); 181 | weakmap.delete([object]); 182 | t.true(!weakmap.has([object]), 'ManyKeysWeakMap .has() after .delete() return false'); 183 | t.doesNotThrow(() => !weakmap.has([1]), 'return false on primitive'); 184 | object = {}; 185 | weakmap.set([object], 42); 186 | Object.freeze(object); 187 | t.true(weakmap.has([object]), 'works with frozen objects #1'); 188 | weakmap.delete([object]); 189 | t.true(!weakmap.has([object]), 'works with frozen objects #2'); 190 | }); 191 | 192 | test('ManyKeysWeakMap#set', () => { 193 | t.is(typeof ManyKeysWeakMap.prototype.set, 'function'); 194 | t.is(ManyKeysWeakMap.prototype.set.name, 'set'); 195 | t.is(ManyKeysWeakMap.prototype.set.length, 2); 196 | t.false(Object.prototype.propertyIsEnumerable.call(ManyKeysWeakMap.prototype, 'set')); 197 | const weakmap = new ManyKeysWeakMap(); 198 | const object = {}; 199 | weakmap.set([object], 33); 200 | t.is(weakmap.get([object]), 33, 'works with object as keys'); 201 | t.true(weakmap.set([{}], 42) === weakmap, 'chaining'); 202 | t.throws(() => new ManyKeysWeakMap().set([42], 42), TypeError, 'Invalid value used as weak map key'); 203 | const object1 = Object.freeze({}); 204 | const object2 = {}; 205 | weakmap.set([object1], 42); 206 | weakmap.set([object2], 42); 207 | Object.freeze(object); 208 | t.is(weakmap.get([object1]), 42, 'works with frozen objects #1'); 209 | t.is(weakmap.get([object2]), 42, 'works with frozen objects #2'); 210 | weakmap.delete([object1]); 211 | weakmap.delete([object2]); 212 | t.is(weakmap.get([object1]), undefined, 'works with frozen objects #3'); 213 | t.is(weakmap.get([object2]), undefined, 'works with frozen objects #4'); 214 | }); 215 | 216 | test('ManyKeysWeakMap#@@toStringTag', () => { 217 | t.is(ManyKeysWeakMap.prototype[Symbol.toStringTag], 'ManyKeysWeakMap', 'ManyKeysWeakMap::@@toStringTag is `ManyKeysWeakMap`'); 218 | t.is(String(new ManyKeysWeakMap()), '[object ManyKeysWeakMap]', 'correct stringification'); 219 | }); 220 | --------------------------------------------------------------------------------