ECMAScript 6
以前,在 JavaScript
中实现“键/值”式存储可以使用 Object
来方便高效地完成,也就是使用对象属性作为键,再使用属性来引用值。
但这种实现并非没有问题,Object
只能使用数值、字符串或符号作为键,因此在将对象作为键时,object
会将对象强制转换成字符串形式(调用其toString
),再将其作为键值,如object => [object Object]
,这使得使用不同对象来映射不同值成为不可能。
Map
是 ES6
新增的一种新的集合类型,它是一种真正的键/值存储机制。Map
的大多数特性都可以通过 Object
类型实现,但二者之间还是存在一些细微的差异。具体实践中使用哪一个,还是值得细细甄别。
基本API
Map
提供了一系列初始化、操作、遍历的方法来方便我们使用。
初始化
Map
是一个构造函数,需要使用new
来创建示例
let map = new Map();//Map(0) {}
如果想在创建的同时初始化实例,可以给 Map
构造函数传入一个可迭代对象,需要包含键/值对数组。可迭代对象中的每个键/值对都会按照迭代顺序插入到实例中:
// 使用嵌套数组初始化映射
const m1 = new Map([
["key1", "val1"],
["key2", "val2"],
["key3", "val3"]
]);
>>> Map(3) {"key1" => "val1", "key2" => "val2", "key3" => "val3"}
// 使用自定义迭代器初始化映射
const m2 = new Map({
[Symbol.iterator]: function*() {
yield ["key1", "val1"];
yield ["key2", "val2"];
yield ["key3", "val3"];
}
});
>>> Map(3) {"key1" => "val1", "key2" => "val2", "key3" => "val3"}
操作
Map
的五大操作方法:set
、get
、has
、delete
、clear
size
map
对象的一个内部属性,返回元素的数量,该属性是只读的,因此不能像数组通过修改此值来修改大小。用set
方法修改size
返回undefined
let myMap = new Map();
myMap.set("a", "alpha");
myMap.set("b", "beta");
myMap.set("g", "gamma");
myMap.size // 3
set
添加或更新一个指定了键(key
)和值(value
)的(新)键值对,并返回自身,因此可链式调用。
let myMap = new Map();
// 将一个新元素添加到 Map 对象
myMap.set("bar", "foo");
myMap.set(1, "foobar");
// 在Map对象中更新某个元素的值
myMap.set("bar", "baz");
//链式调用
myMap.set('bar', 'foo')
.set(1, 'foobar')
.set(2, 'baz');
get
返回 Map
中的指定元素。
let myMap = new Map();
myMap.set("bar", "foo");
myMap.get("bar"); // 返回 "foo"
myMap.get("baz"); // 返回 undefined
has
返回一个 bool
值,用来表明 map
中是否存在指定元素。
let myMap = new Map();
myMap.set("bar", "foo");
myMap.has("bar"); // returns true
myMap.has("baz"); // returns false
delete
移除 Map 对象中指定的元素。
let myMap = new Map();
myMap.set("bar", "foo");
myMap.delete("bar"); // 返回 true。成功地移除元素
myMap.has("bar"); // 返回 false。"bar" 元素将不再存在于 Map 实例中
clear
移除 Map
对象中的所有元素,返回值是undefined
let myMap = new Map();
myMap.set("bar", "baz");
myMap.set(1, "foo");
myMap.size; // 2
myMap.has("bar"); // true
myMap.clear();
myMap.size; // 0
myMap.has("bar") // false
遍历
Map
提供了一些方法来返回迭代器对象,方便我们使用for...of
、forEach
进行遍历
keys
返回一个引用的 Iterator
对象。它包含按照顺序插入 Map
对象中每个元素的 key
值。
let myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
let mapIter = myMap.keys();
console.log(mapIter.next().value); // "0"
console.log(mapIter.next().value); // 1
console.log(mapIter.next().value); // Object
for(let i of mapIter)
console.log(i)
values
返回一个新的Iterator
对象。它包含按顺序插入Map
对象中每个元素的 value
值。
let myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
let mapIter = myMap.values();
console.log(mapIter.next().value); // "foo"
console.log(mapIter.next().value); // "bar"
console.log(mapIter.next().value); // "baz"
entires
返回一个新的包含 [key, value]
对的 Iterator
对象,返回的迭代器的迭代顺序与 Map
对象的插入顺序相同。
const m = new Map([
["key1", "val1"],
["key2", "val2"],
["key3", "val3"]
]);
for (let pair of m.entries()) {
console.log(pair);
}
// [key1,val1]
// [key2,val2]
// [key3,val3]
m.forEach((val, key) => console.log(`${key} -> ${val}`));
// key1 -> val1
// key2 -> val2
// key3 -> val3
与object的区别
键的类型
Map
的键可以是任意值,包括函数、对象或任意基本类型。 Object
的键只能是 String
或是 Symbol
,将除这2
种类型外的类型作为键时,内部会将其转换成字符串再作为键。
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
const obj = {}
const map = new Map()
// dom节点对象作为Object的键值时,会被转换成字符串(调用其toString),将值[object HTMLDivElement]作为键
// 因此div1的值被div2覆盖了
obj[div1] = 'div1'
obj[div2] = 'div2'
console.log(obj)//{ [object HTMLDivElement]: "div2" }
// map直接将dom节点作为键
map.set(div1,"div1")
map.set(div2,"div2")
console.log(map)//{ div#div1 => "div1", div#div2 => "div2" }
键的顺序
Map
中的 key
是有序的。因此,当迭代的时候,一个 Map
对象以插入的顺序返回键值。Object
的键是无序的
[]运算符
Map
和Object
都能使用[]运算符,但是效果不一样。
const map = new Map()
const obj = new Object()
map['name']="jalenl" //Map(1){name:"Jalen"}
map.has('name')//false
map.get('name')//undefined
map.set('name','jalenl')// Map(1){"name" => "Jalen"}
map.has('name')//true
obj['name']="jalenl" //{name:"Jalen"}
Map
和Object
使用[]
修改的是自身的对象属性,但对于Map
来说,自身的属性和元素没有任何关系,size()
得到的元素数量不变。
但Map
之所以能使用[]
运算符,是因为其原型链最底层就是Object
,它是从Object
继承来的。
const map = new Map()
map instanceof Object//true
自带的键
Map
默认情况不包含任何键。只包含显式插入的键。一个 Object
实例有一个原型, 原型链上的键名有可能和自定义设置的键名产生冲突。
迭代器
Map内置迭代器对象,其默认迭代器是entries()
,Object
没有内置迭代器。因此for...of
可直接用于map
实例,而object
实例不可以,必须为object
实例设置迭代器对象。
const obj ={
name:"jalenl",
age: 18
}
const map = new Map([["name","jalenl"],["age","18"]])
console.log(map[Symbol.iterator])//[Function: entries]
console.log(obj[Symbol.iterator])//undefined
如何选择
对于普通开发任务来说,选择 Object
或 Map
看个人偏好,但在内存管理和性能上,它俩有显著差别。
- 内存占用
Object
和 Map
的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量
都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。
不同浏览器的情况不同,但给定固定大小的内存,Map
大约可以比 Object
多存储 50%
的键/值对。
- 插入性能
向 Object
和 Map
中插入新键/值对的消耗大致相当,不过插入 Map
在所有浏览器中一般会稍微快
一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操
作,那么显然 Map
的性能更佳。
- 查找速度
与插入不同,从大型 Object
和 Map
中查找键/值对的性能差异极小,但如果只包含少量键/值对,
则 Object
有时候速度更快。在把 Object
当成数组使用的情况下(比如使用连续整数作为属性),浏
览器引擎可以进行优化,在内存中使用更高效的布局。这对 Map
来说是不可能的。对这两个类型而言,
查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选
择 Object
更好一些。
- 删除性能
使用 delete
删除 Object
属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此,
出现了一些伪删除对象属性的操作,包括把属性值设置为 undefined
或 null
。但很多时候,这都是一
种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map
的 delete()
操作都比插入和查找更快。
如果代码涉及大量删除操作,那么毫无疑问应该选择 Map
。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!