惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
人人都是产品经理
人人都是产品经理
C
Check Point Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
Cloudbric
Cloudbric
博客园 - Franky
阮一峰的网络日志
阮一峰的网络日志
O
OpenAI News
V2EX - 技术
V2EX - 技术
H
Hacker News: Front Page
Hacker News: Ask HN
Hacker News: Ask HN
Google DeepMind News
Google DeepMind News
PCI Perspectives
PCI Perspectives
爱范儿
爱范儿
Hacker News - Newest:
Hacker News - Newest: "LLM"
Martin Fowler
Martin Fowler
S
Schneier on Security
Webroot Blog
Webroot Blog
Know Your Adversary
Know Your Adversary
云风的 BLOG
云风的 BLOG
H
Help Net Security
I
InfoQ
Simon Willison's Weblog
Simon Willison's Weblog
雷峰网
雷峰网
J
Java Code Geeks
V
Visual Studio Blog
MyScale Blog
MyScale Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
Google Online Security Blog
Google Online Security Blog
Spread Privacy
Spread Privacy
有赞技术团队
有赞技术团队
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
V
V2EX
Apple Machine Learning Research
Apple Machine Learning Research
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
S
SegmentFault 最新的问题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
TaoSecurity Blog
TaoSecurity Blog
WordPress大学
WordPress大学
S
Securelist
P
Proofpoint News Feed

Dmitri Pavlutin Blog

Pure Functions in JavaScript: A Beginner's Guide Record Type in TypeScript: A Quick Intro How to Write Comments in React: The Good, the Bad and the Ugly 4 Ways to Create an Enum in JavaScript React forwardRef(): How to Pass Refs to Child Components TypeScript Function Types: A Beginner's Guide How to Use v-model to Access Input Values in Vue Mastering Vue refs: From Zero to Hero Environment Variables in JavaScript: process.env 5 Must-Know Differences Between ref() and reactive() in Vue How to Destructure Props in Vue (Composition API) Triangulation in Test-Driven Development How to Use nextTick() in Vue Programming to Interface Vs to Implementation A Smarter JavaScript Mapper: array.flatMap() Array Grouping in JavaScript: Object.groupBy() How to Access ES Module Metadata using import.meta JSON Modules in JavaScript How to Trim Strings in JavaScript TypeScript Function Overloading How to Debounce and Throttle Callbacks in Vue How to Show/Hide Elements in Vue Sparse vs Dense Arrays in JavaScript How to Fill an Array with Initial Values in JavaScript Covariance and Contravariance in TypeScript What are Higher-Order Functions in JavaScript? How to Use TypeScript with React Components Index Signatures in TypeScript How to Use React useReducer() Hook unknown vs any in TypeScript A Guide to React Context and useContext() Hook How to Use Promise.any() 2 Ways to Remove a Property from an Object in JavaScript 'return await promise' vs 'return promise' in JavaScript How to Use Promise.allSettled() How to Use fetch() with JSON JavaScript Promises: then(f,f) vs then(f).catch(f) What is a Promise in JavaScript? How to Use Promise.all() A Simple Guide to Component Props in React Don't Stop Me Now: How to Use React useTransition() hook A Simple Explanation of JavaScript Variables: const, let, var ES Modules Dynamic Import How to Memoize with React.useMemo() How to Cleanup Async Effects in React Why Math.max() Without Arguments Returns -Infinity How to Debounce and Throttle Callbacks in React Don't Confuse Function Expressions and Function Declarations in JavaScript How to Use ES Modules in Node.js Solving a Mystery Behavior of parseInt() in JavaScript How to Use Array Reduce Method in JavaScript 3 Ways to Merge Arrays in JavaScript A Guide to Jotai: the Minimalist React State Management Library The Difference Between Values and References in JavaScript How to Implement a Queue in JavaScript A Helpful Algorithm to Determine "this" value in JavaScript React useRef() Hook Explained in 3 Steps 7 Interview Questions on "this" keyword in JavaScript. Can You Answer Them? How to Greatly Enhance fetch() with the Decorator Pattern 7 Interview Questions on JavaScript Closures. Can You Answer Them? What's a Method in JavaScript? array.sort() Does Not Simply Sort Numbers in JavaScript How to Solve the Infinite Loop of React.useEffect() The New Array Method You'll Enjoy: array.at(index) What's the Difference between DOM Node and Element? Why Promises Are Faster Than setTimeout()? Everything About Callback Functions in JavaScript How React Updates State 5 Mistakes to Avoid When Using React Hooks 5 Best Practices to Write Quality JavaScript Variables Type checking in JavaScript: typeof and instanceof operators 3 Ways to Check if a Variable is Defined in JavaScript React Forms Tutorial: Access Input Values, Validate, Submit Forms Prototypal Inheritance in JavaScript How to Timeout a fetch() Request How to Learn JavaScript If You're a Beginner A Simple Explanation of React.useEffect() A Simple Explanation of JavaScript Iterators How to Use React Controlled Inputs Everything about null in JavaScript How to Use Fetch with async/await Getting Started with Arrow Functions in JavaScript An Interesting Explanation of async/await in JavaScript Front-end Architecture: Stable and Volatile Dependencies Is it Safe to Compare JavaScript Strings? How to Access Object's Keys, Values, and Entries in JavaScript What Actually is a String in JavaScript? 3 Ways to Shallow Clone Objects in JavaScript (w/ bonuses) Checking if an Array Contains a Value in JavaScript JavaScript Event Delegation: A Beginner's Guide How to Parse URL in JavaScript: hostname, pathname, query, hash 3 Ways to Detect an Array in JavaScript How to Get the Screen, Window, and Web Page Sizes in JavaScript 3 Ways to Check If an Object Has a Property/Key in JavaScript How to Compare Objects in JavaScript Object.is() vs Strict Equality Operator in JavaScript Own and Inherited Properties in JavaScript 5 Differences Between Arrow and Regular Functions How to Use Object Destructuring in JavaScript Your Guide to React.useCallback()
When to Use Map instead of Plain JavaScript Object
Dmitri Pavlutin · 2019-10-09 · via Dmitri Pavlutin Blog

The plain JavaScript object { key: 'value' } holds structured data. Mostly it does this job well enough.

But the plain object has a limitation: its keys have to be strings (or rarely used symbols). What happens if you use numbers as keys? Let's try an example:


const names = {

1: 'One',

2: 'Two',

};

Object.keys(names); // => ['1', '2']


The numbers 1 and 2 are keys in names object. Later, when the object's keys are accessed, it turns out the numbers were converted to strings.

Implicit conversion of keys is tricky because you lose the consistency of the types.

A lot of plain object's issues (keys to string conversion, impossibility to use objects like keys, etc) are solved by Map object. This post describes the use cases when it's better to use maps instead of plain objects.

1. The map accepts any key type

As presented above, if the object's key is not a string or symbol, JavaScript implicitly transforms it into a string.

Contrary, the map accepts keys of any type: strings, numbers, boolean, symbols. Moreover, the map preserves the key type. That's the map's main benefit.

For example, if you use a number as a key inside a map, it will remain a number:


const numbersMap = new Map();

numbersMap.set(1, 'one');

numbersMap.set(2, 'two');

[...numbersMap.keys()]; // => [1, 2]


1 and 2 are keys in numbersMap. The type of the keys remains the same.

You can also use booleans as keys inside a map:


const booleansMap = new Map();

booleansMap.set(true, "Yep");

booleansMap.set(false, "Nope");

[...booleansMap.keys()]; // => [true, false]


booleansMap uses booleans true and false as keys.

Inside a plain object, the use of booleans as keys is impossible. These keys would be transformed into strings: 'true' or 'false'.

Can you use further an entire object as a key? Yes, you can. Just be aware of memory leaks.

1.1 Object as key

Let's say you need to store some object-related data, without attaching this data on the object itself.

Doing so using plain objects is not possible. But there's a workaround: an array of object-value tuples.


const foo = { name: 'foo' };

const bar = { name: 'bar' };

const kindOfMap = [

[foo, 'Foo related data'],

[bar, 'Bar related data'],

];


kindOfMap is an array holding pairs of an object and associated value.

The downside of this approach is the O(n) complexity of accessing the value by key. You have to loop through the entire array to get the desired value:


function getByKey(kindOfMap, key) {

for (const [k, v] of kindOfMap) {

if (key === k) {

return v;

}

}

return undefined;

}

getByKey(kindOfMap, foo); // => 'Foo related data'


WeakMap (a specialized version of Map) is a better solution:

  • WeakMap accepts objects as keys
  • Allows straightforward access of value by the key, with O(1) complexity

The above code refactored to use WeakMap becomes trivial:


const foo = { name: 'foo' };

const bar = { name: 'bar' };

const mapOfObjects = new WeakMap();

mapOfObjects.set(foo, 'Foo related data');

mapOfObjects.set(bar, 'Bar related data');

mapOfObjects.get(foo); // => 'Foo related data'


The main difference between Map and WeakMap is the latter allowing garbage collection of keys (which are objects). This prevents memory leaks.

WeakMap, contrary to Map, accepts only objects as keys and has a reduced set of methods.

2. The map has no restriction over keys names

Any JavaScript object inherits properties from its prototype object. The same happens to plain objects.

The accidentally overwritten property inherited from the prototype is dangerous. Let's study such a dangerous situation.

First, let's ovewrite the toString() property in an object actor:


const actor = {

name: 'Harrison Ford',

toString: 'Actor: Harrison Ford'

};


Then, let's define a function isPlainObject() that determines if the supplied argument is a plain object. This function uses the method toString():


function isPlainObject(value) {

return value.toString() === '[object Object]';

}


Finally, lets' call isPlainObject(actor). Here's the problem: because toString property inside actor is a string (instead of an expected function), this call generates an error:


// Does not work!

isPlainObject(actor); // TypeError: value.toString is not a function


When the application input is used to create the keys names, you have to use a map instead of a plain object to avoid the problem described above.

The map doesn't have any restrictions on the keys names. You can use keys names like toString, constructor, etc. without consequences:


function isMap(value) {

return value.toString() === '[object Map]';

}

const actorMap = new Map();

actorMap.set('name', 'Harrison Ford');

actorMap.set('toString', 'Actor: Harrison Ford');

// Works!

isMap(actorMap); // => true


Regardless of actorMap having a property named toString, the method toString() works correctly.

2.1 Real world example

When the user input creates keys on objects? Let's analyze a case.

Imagine a User Interface that manages custom fields. The user can add a custom field by specifying its name and value:

Custom fields User Interface

It would be convenient to store the state of the custom fields into a plain object:


const userCustomFields = {

'color': 'blue',

'size': 'medium',

'toString': 'A blue box'

};


But the user can choose a custom field name like toString (as in the example), constructor, etc. As presented above, such keys names on the state object could potentially break the code that later uses this object.

Don't take user input to create keys on your plain objects!

Because the map has no restrictions over the keys names, the right solution is to bind the user interface state to a map.


const userCustomFieldsMap = new Map([

['color', 'blue'],

['size', 'medium'],

['toString', 'A blue box']

]);


There is no way to break the map, even using keys as toString, constructor, etc.

3. The map is iterable

To iterate plain object's properties are necessary static functions like Object.keys() or Object.entries() (available in ES2017) .

For example, let's iterate over the keys and values of colorsHex object:


const colorsHex = {

'white': '#FFFFFF',

'black': '#000000'

};

for (const [color, hex] of Object.entries(colorsHex)) {

console.log(color, hex);

}

// 'white' '#FFFFFF'

// 'black' '#000000'


Object.entries(colorsHex) returns an array of key-value pairs extracted from the object.

Access of keys-values of a map is more comfortable because the map is iterable. Anywhere an iterable is accepted, like for() loop or spread operator, use the map directly.

colorsHexMap keys-values are iterated directly by for() loop:


const colorsHexMap = new Map();

colorsHexMap.set('white', '#FFFFFF');

colorsHexMap.set('black', '#000000');

for (const [color, hex] of colorsHexMap) {

console.log(color, hex);

}

// 'white' '#FFFFFF'

// 'black' '#000000'


colorsHexMap is iterable. You can use it anywhere an iterable is accepted: for() loops, spread operator [...map].

Moreover, map.keys() returns an iterator over keys and map.values() over values.

4. Map's size

You cannot easily determine the number of properties in a plain object.

One workaround is to use a helper function like Object.keys():


const exams = {

'John Smith': '10 points',

'Jane Doe': '8 points',

};

Object.keys(exams).length; // => 2


Object.keys(exams) returns an array with keys of exams. The size of exams is the number of keys this array contains.

The map provides a better alternative. The property map.size indicates the number of keys-values.

Let's see how to use size on examsMap:


const examsMap = new Map([

['John Smith', '10 points'],

['Jane Doe', '8 points'],

]);

examsMap.size; // => 2


It's simple to determine the size of the map: examsMap.size.

5. Conclusion

Plain JavaScript objects do the job of holding structured data. But they have some limitations:

  1. Only strings or symbols can be used as keys
  • Own object properties might collide with property keys inherited from the prototype (e.g. toString, constructor, etc).
  • Objects cannot be used as keys

These limitations are solved by maps. Moreover, maps provide benefits like being iterators and allowing easy size look-up.

Anyways, don't consider maps as a replacement of plain objects, but rather a complement.

Do you know other benefits of maps over plain objects? Please write a comment below!