什么时候使用映射而不是简单的JavaScript对象
纯JavaScript对象{key: 'value'}保存结构化数据。但是普通的JavaScript对象有其局限性:
Only strings and symbols can be used as keys of Objects. If we use any other things, say, numbers as keys of an object then during accessing those keys we will see those keys will be converted into strings implicitly causing us to lose consistency of types. const names= {1: 'one', 2: 'two'}; Object.keys(names); // ['1', '2']
There are chances of accidentally overwriting inherited properties from prototypes by writing JavaScript identifiers as key names of an object (e.g., toString, constructor, etc.)
Another object cannot be used as key of an object, so no extra information can be written for an object by writing that object as key of another object and value of that another object will contain the extra information
Objects are not iterators
The size of an object cannot be determined directly
对象的这些局限性可以通过映射来解决,但我们必须将映射视为对象的补充而不是替代。基本上Map只是数组的数组,但我们必须将该数组作为参数传递给Map对象,并使用new关键字,否则仅对于数组数组,Map的有用属性和方法不可用。记住数组中的数组或Map中的键值对必须用逗号分隔,不能像普通对象那样用冒号分隔。
决定使用Map还是Object的三个技巧
当键在运行时之前是未知的时,使用map而不是对象,因为由用户输入形成的键或在不知情的情况下,如果这些键覆盖了对象的继承属性,则会破坏使用对象的代码,因此map在这种情况下更安全。当所有键都是相同类型且所有映射都是相同类型时,也要使用映射。
如果需要将基本值存储为键,则使用映射。
如果需要对单个元素进行操作,则使用对象。
使用地图的好处
1. Map接受任何键类型,并保留键类型:
我们知道,如果对象的键不是字符串或符号,那么JavaScript会隐式地将其转换为字符串。相反,Map接受任何类型的键:字符串、数字、布尔值、符号。等,Map保留原来的键类型。在这里,我们将在Map中使用number作为键,它将保持为数字:
const numbersMap= new Map();
numbersMap.set(1, 'one');
numbersMap.set(2, 'two');
const keysOfMap= [...numbersMap.keys()];
console.log(keysOfMap); // [1, 2]
在Map中,我们甚至可以使用整个对象作为键。有时,我们希望存储一些与对象相关的数据,而不将这些数据附加到对象本身中,以便我们可以使用精简对象,但希望存储关于对象的一些信息。在这种情况下,我们需要使用Map,这样我们就可以将Object作为键,并将对象的相关数据作为值。
const foo= {name: foo};
const bar= {name: bar};
const kindOfMap= [[foo, 'Foo related data'], [bar, 'Bar related data']];
但是这种方法的缺点是通过键访问值的复杂性,因为我们必须遍历整个数组才能获得所需的值。
function getBy Key(kindOfMap, key) {
for (const [k, v] of kindOfMap) {
if(key === k) {
return v;
}
}
return undefined;
}
getByKey(kindOfMap, foo); // 'Foo related data'
我们可以通过使用适当的Map来解决不能直接访问值的问题。
const foo= {name: 'foo'};
const bar= {name: 'bar'};
const myMap= new Map();
myMap.set(foo, 'Foo related data');
myMap.set(bar, 'Bar related data');
console.log(myMap.get(foo)); // 'Foo related data'
我们本可以用WeakMap来实现,只需要写入const myMap= new WeakMap()。Map和WeakMap之间的区别在于,WeakMap允许键(这里是对象)的垃圾收集,因此它可以防止内存泄漏,WeakMap只接受对象作为键,并且WeakMap减少了方法集。
2. Map对键名没有限制:
对于普通的JavaScript对象,我们可能会意外地覆盖从原型继承的属性,这可能是危险的。在这里,我们将重写actor对象的toString()属性:
const actor= {
name: 'Harrison Ford',
toString: 'Actor: Harrison Ford'
};
现在让我们定义一个函数isPlainObject(),来确定所提供的参数是否是一个普通对象,这个函数使用toString()方法来检查它:
function isPlainObject(value) {
return value.toString() === '[object Object]';
}
isPlainObject(actor); // TypeError : value.toString is not a function
// this is because inside actor object `toString` property is a
// string instead of inherited method from prototype
Map对键名没有任何限制。虽然actorMap对象有一个名为toString的属性,但我们可以使用toString()方法继承自actorMap对象的原型,工作完美。
const actorMap= new Map();
actorMap.set('name', 'Harrison Ford');
actorMap.set('toString', 'Actor: Harrison Ford');
function isMap(value) {
return value.toString() === '[object Map]';
}
console.log(isMap(actorMap)); // true
如果我们遇到用户输入创建键的情况,那么我们必须在Map中而不是普通对象中获取这些键。这是因为用户可以选择自定义字段名,如toString、构造函数等,那么在普通对象中这样的键名可能会破坏以后使用该对象的代码。所以正确的解决方案是将用户界面状态绑定到map上,没有办法破坏map:
const userCustomFieldsMap= new Map([['color', 'blue'],
['size', 'medium'], ['toString', 'A blue box']]);
3.Map是可迭代的:
要迭代一个普通对象的属性,我们需要object .entries()或object .keys()。object .entries(plainObject)返回一个从对象中提取的键值对数组,然后我们可以解构这些键和值,并得到正常的键和值输出。
const colorHex= {
'white': '#FFFFFF',
'black': '#000000'
}
for(const [color, hex] of Object.entries(colorHex)) {
console.log(color, hex);
}
//
'white' '#FFFFFF'
'black' '#000000'
由于Map是可迭代的,这就是为什么我们不需要entries()方法来遍历Map和析构键,值数组可以直接在Map上完成,因为在Map中每个元素都是由逗号分隔的键值对数组。
const colorHexMap = new Map();
colorHexMap.set('white', '#FFFFFF');
colorHexMap.set('black', '#000000');
for(const [color, hex] of colorHexMap) {
console.log(color, hex);
}
//'white' '#FFFFFF' 'black' '#000000'
map.keys()返回键的迭代器,map.values()返回值的迭代器。
4. 我们很容易知道地图的大小
我们不能直接确定一个普通对象中属性的数量。我们需要一个helper函数,比如object. keys(),它返回一个包含对象键的数组,然后使用length属性,我们可以获得键的数量或普通对象的大小。
const exams= {'John Rambo': '80%', 'James Bond': '60%'};
const sizeOfObj= Object.keys(exams).length;
console.log(sizeOfObj); // 2
但在地图的情况下,我们可以直接访问地图的大小使用地图。大小属性。
const examsMap = new Map([['John Rambo', '80%'], ['James Bond', '60%']]);
console.log(examsMap.size);