JavaScript ordinary objects {key: 'value'}
can be used to save structured data.
But one thing I find very annoying: the keys of the objects must be strings (or rarely used symbols).
What happens if numbers are used as keys? There is no error in this case:
const names = { 1: 'One', 2: 'Two', }; Object.keys(names); // => ['1', '2']
JavaScript just implicitly converts the object's keys to strings. This is a tricky thing because you lose type consistency.
In this article, I will explain how the JavaScript Map object available in ES2015 solves many of the problems of ordinary objects, including converting keys to strings.
As mentioned above, if the object's key is not a string or symbol, JavaScript will implicitly convert it to a string.
Fortunately, map has no problem with key types:
const numbersMap = new Map(); numbersMap.set(1, 'one'); numbersMap.set(2, 'two'); [...numbersMap.keys()]; // => [1, 2]
1
and 2
are in numbersMap
key. The type number of these keys remains unchanged.
You can use any key type in a map: numbers, booleans, and classic strings and symbols.
const booleansMap = new Map(); booleansMap.set(true, "Yep"); booleansMap.set(false, "Nope"); [...booleansMap.keys()]; // => [true, false]
booleansMap
There is no problem with using boolean values as keys.
Similarly, boolean keys don't work in normal objects.
Let's push the boundaries: can you use an entire object as a key in a map? sure!
Suppose you need to store some data related to the object, but do not attach the data to the object itself.
You cannot do this with ordinary objects.
One solution is to use a set of tuples of object values:
const foo = { name: 'foo' }; const bar = { name: 'bar' }; const kindOfMap = [ [foo, 'Foo related data'], [bar, 'Bar related data'], ];
kindOfMap
is an array containing a pair of objects and the associated value.
The biggest problem with this approach is that the time complexity of accessing the value by key is O(n). You have to loop through the entire array to get the desired value by key:
function getByKey(kindOfMap, key) { for (const [k, v] of kindOfMap) { if (key === k) { return v; } } return undefined; } getByKey(kindOfMap, foo); // => 'Foo related data'
With WeakMap (a specialized version of Map
) you don't need to bother with this. It accepts objects as keys. The main difference between
Map
and WeakMap
is that the latter allows objects that are keys to be garbage collected, thus preventing memory leaks.
The cost of refactoring the above code to use WeakMap
is 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'
as opposed to Map
, WeakMap
Only accepts objects as keys and has a reduced method set.
Any object in JavaScript inherits properties from its prototype object. The same goes for regular JavaScript objects.
If you override properties inherited from the prototype, you may break code that depends on these prototype properties:
function isPlainObject(value) { return value.toString() === '[object Object]'; } const actor = { name: 'Harrison Ford', toString: 'Actor: Harrison Ford' }; // Does not work! isPlainObject(actor); // TypeError: value.toString is not a function
Properties defined on the object actor
toString
Overrides the toString()
method inherited from the prototype. Because it relies on the toString()
method, this breaks isObject()
.
Check the list of properties and methods that ordinary objects inherit from the prototype. Avoid using these names to define custom properties.
For example, suppose you have a user interface that manages some custom fields. Users can add fields by specifying names and values:
It would be convenient to store the state of custom fields into a normal object:
const userCustomFields = { 'color': 'blue', 'size': 'medium', 'toString': 'A blue box' };
But The user may choose a custom field name such as toString
(as shown in the example), constructor
, etc., which may break your object.
Don't create keys on ordinary objects by accepting user input!
map does not have this problem. The name of the key is not restricted:
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
The method toString()
will work regardless of whether the actorMap
has a property named toString
.
In order to traverse the properties of ordinary objects, you must use other auxiliary static functions, such as Object.keys()
or Object.entries()
(available in ES2017):
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.
However, the map itself is iterable:
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 iterable: for()
loops, unroll operators [...map]
, etc.
map also provides other methods that return iterations: map.keys()
iterates over keys, map.values()
iterates over values.
普通对象的另一个问题是你无法轻松确定其拥有的属性数量:
const exams = { 'John Smith': '10 points', 'Jane Doe': '8 points', }; Object.keys(exams).length; // => 2
要确定 exams
的大小,你必须通过它所有键来确定它们的数量。
map 提供了一种替代方法,通过它的访问器属性 size
计算键值对:
const examsMap = new Map([ ['John Smith', '10 points'], ['Jane Doe', '8 points'], ]); examsMap.size; // => 2
确定 map 的大小更加简单:examsMap.size
。
普通的 JavaScript 对象通常可以很好地保存结构化数据。但是它们有一些限制:
toString
,constructor
等)。所有这些问题都可以通过 map 轻松解决。而且它们提供了诸如迭代器和易于进行大小查找之类的好处。
不要将 map 视为普通对象的替代品,而应视为补充。
你知道 map 相对于普通对象的其他好处吗?请在下面写下你的评论!
原文地址:https://dmitripavlutin.com/maps-vs-plain-objects-javascript/
译文地址:https://segmentfault.com/a/1190000020660481
更多编程相关知识,请访问:编程课程!!
The above is the detailed content of A brief discussion on how to use map instead of pure JS objects. For more information, please follow other related articles on the PHP Chinese website!