Home  >  Article  >  Web Front-end  >  A brief discussion on how to use map instead of pure JS objects

A brief discussion on how to use map instead of pure JS objects

青灯夜游
青灯夜游forward
2020-12-02 18:06:464970browse

A brief discussion on how to use map instead of pure JS objects

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.

1. map can accept keys of any type

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!

1.1 Use objects as keys

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.

2. map has no restrictions on key names

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 actortoString 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:

A brief discussion on how to use map instead of pure JS objects

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 .

3. map is iterable

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.

4. map的大小

普通对象的另一个问题是你无法轻松确定其拥有的属性数量:

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

5.结论

普通的 JavaScript 对象通常可以很好地保存结构化数据。但是它们有一些限制:

  1. 只能用字符串或符号用作键
  2. 自己的对象属性可能会与从原型继承的属性键冲突(例如,toStringconstructor 等)。
  3. 对象不能用作键

所有这些问题都可以通过 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!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete