Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
在 JavaScript 中,基本类型(基本数值、基本数据类型)是一种既非对象也无方法或属性的数据。有 7 种原始数据类型:
Map 对象是键值对的集合。
Map 中的一个键只能出现一次;它在 Map 的集合中是独一无二的。
规范要求 Map 实现 “平均访问时间与集合中的元素数量呈次线性关系” 。因此,它可以在内部表示为哈希表(使用 O(1) 查找)、搜索树(使用 O(log(N)) 查找)或任何其他数据结构,只要复杂度小于 O(N)。
键的比较基于零值相等算法。(它曾经使用同值相等,区别在于同值相等将 0 和 -0 视为不同)
这意味着 NaN 是与 NaN 相等的(虽然 NaN !== NaN
),剩下所有其他的值是根据 ===
运算符的结果判断是否相等。
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键 |
一个 Object 有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突(备注:虽然可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见) |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型 |
一个 Object 的键必须是一个 String 或是 Symbol |
键的顺序 | Map 中的键是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值 |
虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的(因此,最好不要依赖属性的顺序) |
Size | Map 的键值对个数可以轻易地通过 size 属性获取 |
Object 的键值对个数只能手动计算 |
迭代 | Map 是可迭代的 |
Object 没有实现迭代协议,所以使用 JavaSctipt 的 for...of] 表达式并不能直接迭代对象(备注:对象可以实现迭代协议,或者你可以使用 Object.keys 、 Object.entries 或 for...in ) |
性能 | 在频繁增删键值对的场景下表现更好 | 在频繁添加和删除键值对的场景下未作出优化 |
序列化和解析 | 没有元素的序列化和解析的支持 | 由 Object 到 JSON 的序列化使用 JSON.stringfy() ;由 JSON 到 Object 的解析使用 JSON.parse() |
设置对象属性同样适用于 Map 对象,但会产生一些问题,下面用实例来演示:
// 这里可能会有一些疑问:为什么 map 使用 const 定义的却还能进行更改
// 原因是:const 判断变量是判断变量地址是否改变,而复杂数据类型中元素的改变并没有改变地址
const map = new Map();
map['a'] = 1;
console.log(map); // Map { a: 1 }
若对变量赋值传址还是传值有疑惑可以点击这里进行查看
但这种设置属性的方式不会改变 Map 的数据结构。它使用的是通用对象的特性。‘a’ 的值未被存储在 Map 中,无法被查询到。其他的对这一数据的操作也会失败:
console.log(map.has('a')); // false——表示map不存在'a'键
console.log(map.delete('a')); // false——表示删除键为'a'的键值对失败
console.log(map); // Map { a: 1 }
正确的存储到 Map 中的方式是使用 set(key, value)
方法,下面会详细的讲到。
通过 new Map()
的方法创建一个 Map 对象。
// new Map():创建一个空 Map
var map = new Map();
// new Map(iterable):创建一个带默认值的 Map
// iterable:键值对的数组([[1, 'one'], [2, 'two']])或 其他可迭代对象
var map = new Map(iterable);
注意:Map() 只能用 new 构造。尝试不使用 new 调用它会抛出 TypeError。
通过 size
属性来获取 Map 的键值对数量。
// size:返回 Map 的键值对数量
var map = new Map([[1, 'one'], [2, 'two']]);
console.log(map.size); // 2
size 属性的值是一个整数,表示 Map 对象有多少个键值对。size 是只读属性,用 set 方法修改 size 返回 undefined,即不能改变它的值。
通过 set(key, value)
方法向 Map 中添加(或修改)一个键值对。(返回修改后的 Map 对象)
// set(key, value):向 Map 中添加(或修改)一个键值对
// key:键值对的键
// value:键值对的值
var map = new Map();
// 如果遇到不存在的键则添加新键值对
console.log(map.set('a', 1)); // Map{'a' => 1}
// 如果遇到存在的键则修改值
console.log(map.set('a', 2)); // Map{'a' => 2}
除了上述写法外,set(key, value)
还有一种链式调用的方法,同样返回修改后的 Map 对象,下面进行实例展示:
// set 的链式调用
var map = new Map();
console.log(map.set(['a', 1]).set(['b', 2])); // Map{ 'a': 1, 'b': 2 }
通过 get(key)
方法获取 Map 的指定键的值。(返回对应值)
// get(key):获取 Map 的指定键的值
// key:键值对的键
var map = new Map([['a', 1]]);
console.log(map.get('a')); // 1
如果值是复杂数据类型(数组、对象等),可能会存在内存泄露的问题。
const arr = [];
const map = new Map();
map.set('bar', arr); // 此时的 arr 传的是栈内存中的地址
map.get('bar').push('foo');
console.log(arr); // ["foo"]
console.log(map.get('bar')); // ["foo"]
注意:持有原始对象引用的映射实际上意味着对象不能被垃圾回收,这可能会导致意外的内存问题。如果你希望存储在映射中的对象具有与原始对象相同的生命周期,请考虑使用 WeakMap。
通过 has(key)
判断 Map 中是否存在指定键的键值对。(返回布尔值)
// has(key):判断 Map 中是否存在指定键的键值对
// key:键值对的键
var map = new Map([['a', 1]]);
console.log(map.has('a')); // 存在返回 true
console.log(map.has('b')); // 不存在返回 false
通过 delete(key)
移除 Map 中指定键的键值对。(返回布尔值)
// delete(key):移除 Map 中指定键的键值对
// key:键值对的键
var map = new Map([['a', 1]]);
console.log(map.delete('a')); // 删除成功返回 true
console.log(map.delete('a')); // 删除失败返回 false
通过 clear()
移除 Map 中所有的键值对。(返回 undefined)
// clear():移除 Map 中所有的键值对
var map = new Map([['a', 1]]);
console.log(map.clear()); // undefined
通过 keys()
获取 Map 中所有的键。(返回 MapIterator)
// keys():获取 Map 中所有的键
var map = new Map([['a', 1]]);
console.log(Array.from(map.keys())); // ['a']
建议配合
Array.from()
使用,下面的values()
、entries()
同样。
若对数组方法 Array.from() 不了解可以点击这里查看
通过 values()
获取 Map 中所有的值。(返回 MapIterator)
// values():获取 Map 中所有的值
var map = new Map([['a', 1]]);
console.log(Array.from(map.values())); // [1]
通过 entries()
获取 Map 中所有的键值对。(返回 MapIterator)
// entries():获取 Map 中所有的键值对
var map = new Map([['a', 1]]);
console.log(Array.from(map.entries())); // [['a', 1]]
var map = new Map([['a', 1], ['b', 2], ['c', 3]]);
for (const value of map) {
console.log(value);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]
var map = new Map([['a', 1], ['b', 2], ['c', 3]]);
map.forEach((item, index) => {
console.log(index + ' : ' + item);
})
// a : 1
// b : 2
// c : 3
若对 forEach 有异或可以点击这里查看
var map = new Map([['a', 1], ['b', 2]]);
// 获取迭代器
const iterator = map.entries();
console.log(iterator.next().value); // ["a", 1]
console.log(iterator.next().value); // ["b", 2]