Map
Map是一组键值对的结构,用于解决以往不能用对象做为键的问题
- 具有极快的查找速度
- 函数、对象、基本类型都可以作为键或值
基本概念
声明定义
📗 可以接受一个数组作为参数,该数组的成员是一个表示键值对的数组。
let m = new Map([
['world', 'Jerry'],
['abccms', '开源系统']
]);
console.log(m.get('world')); // Jerry
1
2
3
4
5
6
2
3
4
5
6
💡 使用set
方法添加元素,支持链式操作
let map = new Map();
let obj = {
name: "Jerry"
};
map.set(obj, "world.com").set("name", "abccms");
console.log(map.entries()); //MapIterator {{…} => "world.com", "name" => "abccms"}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
📗 使用构造函数new Map
创建的原理如下
const abc = new Map();
const arr = [["world", "Jerry"], ["abccms", "开源系统"]];
arr.forEach(([key, value]) => {
abc.set(key, value);
});
console.log(abc);
1
2
3
4
5
6
7
2
3
4
5
6
7
📌 对于键是对象的Map
, 键保存的是内存地址,值相同但内存地址不同的视为两个键。
let arr = ["Jerry"];
const abc = new Map();
abc.set(arr, "world.com");
console.log(abc.get(arr)); //world.com
console.log(abc.get(["Jerry"])); // undefined
1
2
3
4
5
2
3
4
5
获取数量
获取数据数量size
console.log(map.size);
1
元素检测
检测元素是否存在has
console.log(map.has(obj1));
1
读取元素
let map = new Map();
let obj = {
name: 'Jerry'
}
map.set(obj, 'world.com');
console.log(map.get(obj));
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
删除元素
使用 delete()
方法删除单个元素
let map = new Map();
let obj = {
name: 'Jerry'
}
map.set(obj, 'world.com');
console.log(map.get(obj));
map.delete(obj);
console.log(map.get(obj));
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
使用clear
方法清除Map所有元素
let map = new Map();
let obj1 = {
name: 'abccms.com'
}
let obj2 = {
name: 'world.com'
}
map.set(obj1, {
title: '内容管理系统'
});
map.set(obj2, {
title: 'Jerry'
});
console.log(map.size); // 2
map.clear();
console.log(map.size); // 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
遍历数据
📗 使用 keys()/values()/entries()
都可以返回可遍历的迭代对象。
let abc = new Map([["world", "Jerry"], ["abccms", "开源系统"]]);
console.log(abc.keys()); //MapIterator {"world", "abccms"}
console.log(abc.values()); //MapIterator {"Jerry", "开源系统"}
console.log(abc.entries()); //MapIterator {"world" => "Jerry", "abccms" => "开源系统"}
1
2
3
4
2
3
4
可以使用keys/values
函数遍历键与值
let abc = new Map([["world", "Jerry"], ["abccms", "开源系统"]]);
for (const key of abc.keys()) {
console.log(key);
}
for (const value of abc.values()) {
console.log(value);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
使用for/of
遍历操作,直播遍历Map 等同于使用entries()
函数
let abc = new Map([["world", "Jerry"], ["abccms", "开源系统"]]);
for (const [key, value] of abc) {
console.log(`${key}=>${value}`);
}
1
2
3
4
2
3
4
📌 使用forEach
遍历操作
let abc = new Map([["world", "Jerry"], ["abccms", "开源系统"]]);
abc.forEach((value, key) => {
console.log(`${key}=>${value}`); // world=>Jerry abccms=>开源系统
});
1
2
3
4
2
3
4
数组转换
可以使用展开语法
或 Array.form
静态方法将Set类型转为数组,这样就可以使用数组处理函数了
let abc = new Map([["world", "Jerry"], ["abccms", "开源系统"]]);
console.log(...abc); //(2) ["world", "Jerry"] (2) ["abccms", "开源系统"]
console.log(...abc.entries()); //(2) ["world", "Jerry"] (2) ["abccms", "开源系统"]
console.log(...abc.values()); //Jerry 开源系统
console.log(...abc.keys()); //world abccms
1
2
3
4
5
6
2
3
4
5
6
检索包含Jerry
的值组成新Map
let abc = new Map([["world", "Jerry"], ["abccms", "开源系统"]]);
let newArr = [...abc].filter(function(item) {
return item[1].includes("Jerry");
});
abc = new Map(newArr); // Map(1) {"world" => "Jerry"}
console.log(...abc.keys()); // world
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
节点集合
📗 map的key可以为任意类型,下面使用DOM节点做为键来记录数据。
为DOM节点在不添加属性的情况下保存更多的信息
<body>
<div desc="Jerry">world</div>
<div desc="开源系统">abccms</div>
</body>
<script>
const divMap = new Map();
const divs = document.querySelectorAll("div");
divs.forEach(div => {
divMap.set(div, {
content: div.getAttribute("desc")
});
});
divMap.forEach((config, elem) => {
elem.addEventListener("click", function() {
alert(divMap.get(this).content);
});
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实例操作
当不接受协议时无法提交表单,并根据自定义信息提示用户。
<form action="" onsubmit="return post()">
接受协议:
<input type="checkbox" name="agreement" message="请接受接受协议" />
我是学生:
<input type="checkbox" name="student" message="网站只对学生开放" />
<input type="submit" />
</form>
</body>
<script>
function post() {
let map = new Map();
let inputs = document.querySelectorAll("[message]");
//使用set设置数据
inputs.forEach(item =>
map.set(item, {
message: item.getAttribute("message"),
status: item.checked
})
);
//遍历Map数据
return [...map].every(([item, config]) => {
config.status || alert(config.message);
return config.status;
});
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
WeakMap
WeakMap对象是一组键/值对的集
- 键名必须是对象
- WeaMap对键名是弱引用的,键值是正常引用
- 垃圾回收不考虑WeaMap的键名,不会改变引用计数器,键在其他地方不被引用时即删除
- 因为WeakMap 是弱引用,由于其他地方操作成员可能会不存在,所以不可以进行
forEach( )
遍历等操作 - 也是因为弱引用,WeaMap 结构没有keys( ),values( ),entries( )等方法和 size 属性
- 当键的外部引用删除时,希望自动删除数据时使用
WeakMap
声明定义
📌 以下操作由于键不是对象类型将产生错误
new WeakSet("abccms"); //TypeError: Invalid value used in weak set
1
将DOM节点保存到WeakSet
<body>
<div>world</div>
<div>abccms</div>
</body>
<script>
const abc = new WeakMap();
document
.querySelectorAll("div")
.forEach(item => abc.set(item, item.innerHTML));
console.log(abc); //WeakMap {div => "abccms", div => "world"}
</script>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
基本操作
下面是WeakSet的常用指令
const abc = new WeakMap();
const arr = ["abccms"];
//添加操作
abc.set(arr, "world");
console.log(abc.has(arr)); //true
//删除操作
abc.delete(arr);
//检索判断
console.log(abc.has(arr)); //false
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
垃圾回收
WakeMap的键名对象不会增加引用计数器,如果一个对象不被引用了会自动删除。
- 下例当
abc
删除时内存即清除,因为WeakMap是弱引用不会产生引用计数 - 当垃圾回收时因为对象被删除,这时WakeMap也就没有记录了
let map = new WeakMap();
let abc = {};
map.set(abc, "abccms");
abc = null;
console.log(map);
setTimeout(() => {
console.log(map);
}, 1000);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
选课案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
* {
padding: 0;
margin: 0;
}
body {
padding: 20px;
width: 100vw;
display: flex;
box-sizing: border-box;
}
div {
border: solid 2px #ddd;
padding: 10px;
flex: 1;
}
div:last-of-type {
margin-left: -2px;
}
ul {
list-style: none;
display: flex;
width: 200px;
flex-direction: column;
}
li {
height: 30px;
border: solid 2px #e67e22;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 10px;
color: #333;
transition: 1s;
}
a {
border-radius: 3px;
width: 20px;
height: 20px;
text-decoration: none;
text-align: center;
background: #16a085;
color: white;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
margin-right: 5px;
}
.remove {
border: solid 2px #eee;
opacity: 0.8;
color: #eee;
}
.remove a {
background: #eee;
}
p {
margin-top: 20px;
}
p span {
display: inline-block;
background: #16a085;
padding: 5px;
color: white;
margin-right: 10px;
border-radius: 5px;
margin-bottom: 10px;
}
</style>
<body>
<div>
<ul>
<li><span>js</span> <a href="javascript:;">+</a></li>
<li><span>ts</span> <a href="javascript:;">+</a></li>
<li><span>css</span><a href="javascript:;">+</a></li>
</ul>
</div>
<div>
<strong id="count">共选了2门课</strong>
<p id="lists"></p>
</div>
</body>
<script>
class Lesson {
constructor() {
this.lis = document.querySelectorAll("ul>li");
this.countELem = document.getElementById("count");
this.listElem = document.getElementById("lists");
this.map = new WeakMap();
}
run() {
this.lis.forEach((item) => {
item.querySelector("a").addEventListener("click", (event) => {
const elem = event.target;
const state = elem.getAttribute("select");
if (state) {
elem.removeAttribute("select");
this.map.delete(elem.parentElement);
elem.innerHTML = "+";
elem.style.backgroundColor = "green";
} else {
elem.setAttribute("select", true);
this.map.set(elem.parentElement, true);
elem.innerHTML = "-";
elem.style.backgroundColor = "red";
}
this.render();
});
});
}
count() {
return [...this.lis].reduce((count, item) => {
return (count += this.map.has(item) ? 1 : 0);
}, 0);
}
lists() {
return [...this.lis]
.filter((item) => {
return this.map.has(item);
})
.map((item) => {
return `<span>${item.querySelector("span").innerHTML}</span>`;
});
}
render() {
this.countELem.innerHTML = `共选了${this.count()}课`;
this.listElem.innerHTML = this.lists().join("");
}
}
new Lesson().run();
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144