Map数据结构
含义和基本用法
JavaScript 的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制
var user = {
"name":"xiaotong",
age:20
}
const data = {};
const element = document.getElementById('myDiv');
data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"
代码原意是将一个 DOM 节点作为对象 data 的键,但是由于对象只 接受字符串作为键名,所以 element 被自动转为字符串 [object HTMLDivElement]
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也 是键值对的集合,但是“键”的范围不限于字符串,各种类型的值 (包括对象)都可以当作键。也就是说,Object 结构提供了“字符串 —值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现
const m = new Map();
const o = { p: 'Hello World' };
m.set(o, 'content')
console.log(m.get(o));
注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。 这一点要非常小心。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
代码的 set 和 get 方法,表面是针对同一个键,但实际上这是两个不 同的数组实例,内存地址是不一样的,因此 get 方法无法读取该键, 返回 undefined
同理,同样的值的两个实例,在 Map 结构中被视为两个键
const map = new Map();
const k1 = ['a'];
const k2 = ['a'];
map.set(k1, 111).set(k2, 222);
map.get(k1) // 111
map.get(k2) // 222
由上可知,Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键
实时效果反馈
1. 下列代码运行结果是什么:
const map = new Map(); map.set(['hello'], 10); var h = ["hello"]; map.set(h,20) console.log(map.get(['hello']));
A 10
B 20
C undefined
D 报错
Map数据结构的属性和方法
size 属性
size 属性返回 Map 结构的成员总数
const map = new Map();
map.set('xiaotong', true);
map.set('txc', false);
map.size // 2
set()
set 方法设置键名 key 对应的键值为 value ,然后返回整个 Map 结构。 如果 key 已经有值,则键值会被更新,否则就新生成该键
const map = new Map();
map.set('itxiaotong', true);
map.set('txc', false);
console.log(map);
set 方法返回的是当前的 Map
const map = new Map();
map.set('itxiaotong', true).set("txc",15)
console.log(map);
get(key)
get 方法读取 key 对应的键值,如果找不到 key ,返回 undefined
const map = new Map();
map.set('itxiaotong', true).set("txc",15)
console.log(map.get("itxiaotong"));
console.log(map.get("it"));
has(key)
has
const map = new Map();
map.set('itxiaotong', true).set("txc",15)
console.log(map.has("itxiaotong"));
delete(key)
delete 方法删除某个键,返回 true 。如果删除失败,返回 false
const map = new Map();
map.set('itxiaotong', true).set("txc",15)
map.delete("itxiaotong")
console.log(map);
clear()
clear
const map = new Map();
map.set('itxiaotong', true).set("txc",15)
map.clear()
console.log(map);
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法
const map = new Map();
map.set('itxiaotong', true).set("txc",15)
for (let key of map.keys()) {
console.log(key);
}
for (let value of map.values()) {
console.log(value);
}
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
map.forEach(function (value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
实时效果反馈
1. 下列 Map 方法中,那个方法可以判断元素是否存在 Map 数据结构中:
A set
B get
C has
D delete
Proxy
Proxy
Proxy
var user = {};
var obj = new Proxy(user,{
get:function(target, propKey){
console.log(target, propKey);
},
set:function(target, propKey, value){
console.log(target, propKey, value);
}
})
obj.count = 1
obj.count
var user = {};
var obj = new Proxy(user,{
get:function(target, propKey){
console.log(target, propKey);
return 10
}
})
obj.count = 1
console.log(obj.count);
var user = {};
var obj = new Proxy(user, {
set: function (target, propKey, value) {
if (propKey === "age") {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
target[propKey] = value;
}
})
obj.age = 20
obj.name = "xiaotong"
console.log(obj.age);
console.log(obj.name);
实时效果反馈
1. 下列代码运行结果是什么:
var user = {}; var obj = new Proxy(user, { get: function (target, propKey) { return "不给你看" }, set: function (target, propKey, value) { if (propKey === "age") { if (!Number.isInteger(value)) { throw new TypeError('The age is not an integer'); } if (value > 200) { throw new RangeError('The age seems invalid'); } } target[propKey] = value; } }) obj.age = 20 obj.name = "itxiaotong" console.log(obj.age);
A The age is not an integer
B The age seems invalid
C 20
D 不给你看
Reflect
Reflect 对象与 Proxy
一般与 Proxy 配合完成更多的功能
检测一个对象是否存在特定属性
const duck = {
name: 'Maurice',
color: 'white',
greeting: function() {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
Reflect.has(duck, 'color'); // true
Reflect.has(duck, 'haircut'); // false
返回这个对象自身的属性
const duck = {
name: 'Maurice',
color: 'white',
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
console.log(Reflect.ownKeys(duck));
为这个对象添加一个新的属性
const duck = {
name: 'Maurice',
color: 'white',
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
Reflect.set(duck, 'eyes', 'black');
console.log(duck);
与 Proxy
var user = {}
var obj = new Proxy(user, {
set: function (target, name, value) {
var success = Reflect.set(target,name, value);
if (success) {
console.log('property ' + name + ' on ' + target + ' set to ' + value);
}
return success;
}
});
obj.name = "iwen"
console.log(obj.name);
同步与异步
定义:同步和异步关注的是消息通信机制(可以理解为数据的读取 方式)。
同步,就是调用某个东西是,调用方得等待这个调用返回结果才能 继续往后执行。
异步,和同步相反调用方不会理解得到结果,而是在调用发出后调 用者可用继续执行后续操作,被调用者通过状体来通知调用者,或 者通过回掉函数来处理这个调用。
举例
你去商城买东西,你看上了一款手机,能和店家说你一个这款手机,他就去仓库拿货,你得在店里等着,不能离开,这叫做同步。
现在你买手机直接去京东下单,下单完成后你就可用做 其他时间(追剧、打王者、lol)等货到了去签收就ok了,这就叫做异步。
同步代码表现
for (var i = 0; i < 10000; i++) {
if (i == 9999){
console.log("循环结束了~~")
}
}
console.log("ok")
// 循环结束了~~
// ok
for循环就是同步编程的, 只有循环结束后, 才会继续执行下面的代码
while(1){
}
console.log("ok")
永远都不会执行 console.log("ok") , 因为上面的循环是死循环, 永远都不会结束
异步代码表现
var n = 0;
setTimeout(function () {
n++;
console.log(n);
}, 1000);
console.log(n);
// 0
// 1
最常见的异步代码
<!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>
<script src="./js/jquery-3.6.0.js">
</script>
</head>
<body>
<p id="content"></p>
<script>
var content = ""
$.ajax({
type:"get",
url:"http://iwenwiki.com/api/blueberrypai/getBlueBerryJamInfo.php",
success:function(data){
// content = data.msg;
callback(data)
}
})
// 回调函数
function callback(data){
$("#content").html(data.msg)
}
</script>
</body>
</html>
实时效果反馈
1. 下列关于同步和异步描述错误的是:
A 同步就是自上而下运行,必须上面运行有了结果在继续向下运行
B 异步运行不会考虑上一行代码运行结果,而是直接向下运行
C 异步需要配合回调函数获取内容
D 同步需要配合回调函数获取内容