First of all, let’s welcomeMapTell me a little bit about yourself MapA map is a classic type of data structure in which data is defined askey/valueExists in the form of key-value pairs

Map Object
The default value The default contains no values, only explicitly inserted keys An Object has a prototype. The key name of the prototype may conflict with the key name of its own Object
type any StringSymbol
The length of the The number of key-value pairs is obtained by the size attribute The number of key-value pairs can only be calculated manually
performance The performance is better when key pairs are frequently added or deleted No optimization is made for scenarios where key/value pairs are frequently added and deleted

Basic usage of Map

Accept any type of keystroke key, be any!!


const testMap = new Map(a)
let str = 'Not studying today'.    num = Awesome!.    keyFunction = function () {},
 keySymbol = Symbol('Web'),  keyNull = null. keyUndefined = undefined. keyNaN = NaN // Add key-value pairs // Basic usage testMap.set('key'.'value') // Map(1) {"key" => "value"}  testMap.set(str, 'Spicy chicken tomorrow') testMap.set(num, 'front Sneaker') testMap.set(keyFunction, 'Your function is awesome.') testMap.set(keySymbol, 'Big front end') testMap.set(keyNull, 'I'm a Null') testMap.set(keyUndefined, 'I am an undiam') testMap.set(keyNaN, 'I'm a NaN')  testMap.get(function () {}) //undefined testMap.get(Symbol('Web')) //undefined  // Although NaN! == NaN but it makes no difference as a Map key name testMap.get(NaN) //" I am a NaN" testMap.get(Number('NaN')) //" I am a NaN"  Copy the code

Except for NaN, the get method of “Map” is obtained by comparing key names for equality (===), and returning undefined if they are not equal

Compare Map and Object

define

//Map
const map = new Map(a);map.set('key'.'value'); // Map(1) {"key" => "value"}
map.get('key'); // 'value'

//Object const someObject = {}; someObject.key = 'value'; someObject.key; // 'value' Copy the code

It’s clear here that the definition behavior is very similar, and I’m sure you haven’t figured out exactly when “Map” is a best practice, so don’t move on.

The key type

JavaScript “Object” only accepts two types of key names: String and Symbol. You can use other types of key names, but ultimately JavaScript implicitly converts them to strings

const obj = {}
// Look directly at some of the more special key names
obj[true] = 'Boolean'
obj[1] = 'Number'
obj[{'front end':'Sneaker'}] = '666'
 Object.keys(obj) // ["1", "true", "[object Object]"] Copy the code

Take a look at “Map”, which accepts any type of key name and retains its key name type.

const map = new Map(a);map.set(1.'value');
map.set(true.'value');
map.set({'key': 'value'}, 'value');
for (const key of map.keys()) {
 console.log(key); } / / 1 // true // {key: "value"}  // In addition, Map also supports re as key names map.set(/^1[3456789]\d{9}$/.'Regular phone number') //Map(1) {/^1[3456789]\d{9}$/ =>" Copy the code

“Map” supports regular expressions as key names, which is not allowed in Object

Prototype Prototype

“Object” is different from “Map” in that it is more than superficial. The “Map” contains only the key-value pairs you define, but the “Object” Object has some built-in properties from its prototype

const newObject = {};
newObject.constructor; ƒ Object() {[native code]}
Copy the code

If you don’t do it right, it can lead to problems and bugs that you didn’t expect

const countWords = (words) = > {
  const counts = { };
  for (const word of words) {
    counts[word] = (counts[word] || 0) + 1;
  }
 return counts; }; const counts = countWords(['constructor'.'creates'.'a'.'bug']); // {constructor: "function Object() { [native code] }1", creates: 1, a: 1, bug: 1} Copy the code

This example was inspired by the book Effective TypeScript [1]

The iterator

“Map” is iterable and can be iterated directly, such as forEach loops or for… of… cycle

//forEach
const map = new Map(a);map.set('key1'.'value1');
map.set('key2'.'value2');
map.set('key3'.'value3');
map.forEach((value, key) = > {  console.log(key, value); }); // key1 value1 // key2 value2 // key3 value3  //for... of... for(const entry of map) {  console.log(entry); } // ["key1", "value1"] // ["key2", "value2"] // ["key3", "value3"] Copy the code

But forObjectIt is not possible to iterate directly, and attempts to iterate will result in an error

const object = {
  key1: 'value1'.  key2: 'value2'.  key3: 'value3'.};
for(const entry of object) {  console.log(entry); } // Uncaught TypeError: object is not iterable Copy the code

You need an extra step to retrieve its key name, key value, or key-value pair

for(const key of Object.keys(object)) {
  console.log(key);
}
// key1
// key2
// key3  for(const value of Object.values(object)) {  console.log(value); } // value1 // value2 // value3  for(const entry of Object.entries(object)) {  console.log(entry); } // ["key1", "value1"] // ["key2", "value2"] // ["key3", "value3"]  for(const [key,value] of Object.entries(object)) {  console.log(key,value); } //"key1", "value1" //"key2", "value2" //"key3", "value3" Copy the code

You can also use for… in… To iterate through the loop key name

for(const key in object) {
  console.log(key);
}
// key1
// key2
// key3 Copy the code

Element order and length

Map keeps track of length so that it can be accessed in O(1) complexity

const map = new Map(a);map.set('key1'.'value1');
map.set('key2'.'value2');
map.set('key3'.'value3');
map.size; / / 3
Copy the code

On the other hand, for “Object”, if you want to obtain the attribute length of the Object, you need to iterate it manually, making it O(n) complexity and the attribute length n

In the example mentioned above, we can see that “Map” always returns key names in insertion order. But “Object” is not. Starting with ES6, the String and Symbol keys are stored in order, but keys saved to strings by implicit conversion are out of order

const object = { };
object['key1'] = 'value1';
object['key0'] = 'value0';
object; // {key1: "value1", key0: "value0"}
object[20] = 'value20';
object; // {20: "value20", key1: "value1", key0: "value0"}  Object.keys(object).length; / / 3 Copy the code

Object/Map application scenarios

The above is the basic difference between “Map” and “Object”, and the difference should be considered when solving the problem.

  • whenInsertion orderIs what you need to consider when solving the problem, and is currently required to use divisionStringSymbolOther than the key name, thenMapIt’s the best solution
  • If you needIterate over the key-value pairs(and need to consider the order), then I think it needs to be prioritizedMap.
  • MapIs a pure hash structure, andObjectNo (it has its own internal logic).MapFrequently add or delete key-value pairsBetter performance and higher performance in the scenario of. This is also a priority when you need to manipulate data frequentlyMap
  • For another practical example, if you have a user action feature for custom fields that you can customize from a form, it’s best to use a Map, because there’s a good chance it willDestroy the original object
const userCustomFields = {
  'color':    'blue'.  'size':     'medium'.  'toString': 'A blue box'
};
Copy the code

In this case, the user-defined toString will destroy the original object, and the “Map” key will accept any type and have no effect

function isMap(value) {
  return value.toString() === '[object Map]';
}

const actorMap = new Map(a); actorMap.set('name'.'Harrison Ford'); actorMap.set('toString'.'Actor: Harrison Ford');  // Works! isMap(actorMap); // => true Copy the code
  • “Object” is perfectly useful when you need to work with properties, especially when you need to work with JSON data. Since a “Map” can be of any type, there is no native way to convert it to JSON.
var map = new Map(a)map.set('key'.'value')
JSON.stringify(map)  / / "{}"
Copy the code
  • If you want to keep unique logic and attributes in an Object, you can only use Object
var obj = {
    id: 1.    name: "Front-end Sneaker".    speak: function(){
        return `Object Id: The ${this.id}, with Name: The ${this.name}`;
 } } console.log(obj.speak());//Object Id: 1, with Name: front-end Sneaker. Copy the code
  • When you need to passRegular expressionWhen deciding to process some business logic,MapWill be your best solution
const actions = (a)= >{
  const functionA = (a)= >{/*do sth*/}
  const functionB = (a)= >{/*do sth*/}
  const functionC = (a)= >{/*send log*/}
  returnnewMap([
 [/^guest_[1-4]$/,functionA],  [/^guest_5$/,functionB],  [/^guest_.*$/,functionC],  / /...  ]) }  const onButtonClick = (identity,status) = >{  let action = [...actions()].filter(([key,value]) = >(key.test(`${identity}_${status}`)))  action.forEach(([key,value]) = >value.call(this)) } Copy the code

By taking advantage of the nature of the array loop, any logic that meets the re condition is executed, so you can execute both the public logic and the individual logic at the same time. Because of the re, you can open your imagination to unlock more gameplay and see more elegant ways to write complex JavaScript decisions

Conclusion:

“Object” objects are generally good for holding structured data, but they also have limitations:

  1. The key name accept type can only be usedStringorSymbol
  2. Custom key names are easily inherited from stereotypesKey name conflict(e.g.toString.constructorEtc.)
  3. The object/re cannot be used as a key name and these problems can be solved by “Map”, providing benefits such as iterators and easy size look-ups

Do not use “Map” as a substitute for plain “Object”, but as a complement to plain objects

If you like xiaobian, you can pay attention to the following public number, learn more about dry goods.

The resources

[1]

Effective TypeScript: https://www.oreilly.com/library/view/effective-typescript/9781492053736/


[2]

MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map


[3]

dmitripavlutin: https://dmitripavlutin.com/maps-vs-plain-objects-javascript


[4]

medium: https://medium.com/javascript-in-plain-english