Trace the fixed program
Count the three fixed labels markup, toString and markup. Keep groups in the order their keys first appear.
const labels=['markup','toString','markup'];const counts=new Map();
for(const label of labels)counts.set(label,(counts.get(label)??0)+1);
console.log(JSON.stringify([...counts]));
[["markup",2],["toString",1]]| Input label | Count after input | Key order |
|---|---|---|
| markup | 1 | markup |
| toString | 1 | markup, toString |
| markup | 2 | markup, toString |
Explain the result
Map.get reads stored entries rather than prototype properties. Missing keys yield undefined; ?? supplies zero before the increment. Updating markup does not move its insertion position. Ordinary objects need an explicit own-property or null-prototype strategy instead.
Test the alternative
On an ordinary empty object, toString is inherited and is a function, not a missing counter. Checking its type proves the collision without relying on an engine-specific function string.
const counts={};const value=counts['toString']??0;
console.log(JSON.stringify({own:Object.hasOwn(counts,'toString'),type:typeof value,zero:value===0}));
{"own":false,"type":"function","zero":false}Work the boundary case
Does the numeric key 1 share a Map entry with the string key "1"?
Show the worked answer
No. These are distinct keys. Map also supports non-string keys; this lesson counts the supplied string labels only.
const counts=new Map([[1,'number'],['1','text']]);
console.log(JSON.stringify({size:counts.size,number:counts.get(1),text:counts.get('1')}));
{"size":2,"number":"number","text":"text"}