Map 定义与基本用法
JavaScript 的对象(Object)本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键,这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了 Map 数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map 对 象</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
/**map:创建一个 Map 对象
* json:创建一个 json 对象*/
let map = new Map();
const json = {"name": "Hua An"};
/**赋值:第一个 key 为对象 json,第二个 key 为字符串 "json" */
map.set(json, 'content');
map.set("json", json);
console.log(map.get(json)); // 根据 key 取值:"content"
console.log(map.has(json)); // 判断是否存在某个 Kye:true
console.log(map.delete(json));//删除某个 key:true
console.log(map.has(json)); // 判断是否存在某个 Kye:false
console.log(map.get("json")["name"]);//根据 Key 取值,然后取 json 的值:Hua An
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>
作为构造函数,Map 也可以接受一个数组作为参数,该数组的成员是一个个表示键值对的数组。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map 对 象</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
/**创建一个 json 对象*/
const json = {"name": "Hua An"};
/**map:创建一个 Map 对象,同时初始化值*/
const map = new Map(
[
['name', '张三'],
['title', 'Author'],
["user", json]
]);
console.log(map.size);// 3
console.log(map.has('name'));//判断是否存在某个 Kye: true
console.log(map.get('name'));// 根据 key 取值:"张三"
console.log(map.has('title'));// 判断是否存在某个 Kye:true
console.log(map.get('title'));// 根据 key 取值:"Author"
console.log(map.get("user")["name"]);// "Hua An"
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>
事实上,不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作 Map 构造函数的参数。这就是说,Set 和 Map 都可以用来生成新的 Map 。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map 对 象</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
let mapSource = new Map();
mapSource.set("name", "华安");
mapSource.set("title", "宁王造反");
let map = new Map(mapSource);
console.log(map.size);// 2
console.log(map.get('name'));// 根据 key 取值:"华安"
console.log(map.get('title'));// 根据 key 取值:"宁王造反"
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>
如果对同一个键多次赋值,后面的值将覆盖前面的值,如果读取一个未知的键,则返回undefined。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map 对 象</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
let map = new Map();
map.set("name", "华安");
map.set("name", "宁王造反");
console.log(map.size);// 1
console.log(map.get('name'));// 根据 key 取值:"宁王造反"
console.log(map.get('title'));// 根据 key 取值:undefined
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>
只有对同一个对象的引用,Map 结构才将其视为同一个键,Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map 对 象</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
let map = new Map();
/**
* 内存地址是不一样的,所以 k1 与 k2 是两个不同的对象
* @type {string[]}
*/
let k1 = ["name", "华安"];
let k2 = ["name", "华安"];
map.set(k1, "我是 k1");
map.set(k2, "我是 k2");
console.log(map.size);// 2
console.log(map.get(k1));// 根据 key 取值:我是 k1
console.log(map.get(k2));// 根据 key 取值:我是 k2
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>
如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0和-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Map 对 象</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
let map = new Map();
map.set(-0, 123);
map.set(-5, 456);
console.log(map.get(+0));// 123
console.log(map.get(5));// undefined
console.log(map.get(-5));// 456
map.set(true, 1);
map.set('true', 2);
console.log(map.get(true));// 1
console.log(map.get("true"));// 2
map.set(undefined, 3);
map.set(null, 4);
console.log(map.get(undefined));// 3
console.log(map.get(null));// 4
map.set(NaN, 123);
console.log(map.get(NaN));// 123
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>
Map 属性与方法
size 属性返回 Map 结构的成员总数:
let map = new Map([
["name", "华安"],
["age", 35]
]);
console.log(map.size);//输出 2
let map2 = new Map();
console.log(map2.size);//输出 0
set(key,value) 方法设置键名 key 对应的键值为 value,然后返回整个 Map 结构。如果 key 已经有值,则键值会被更新,否则就新生成该键:
let map = new Map();
map.set("name", "张三");
map.set(123, "age");
map.set(undefined, "未定义");
console.log(map);//输出 {"name" => "张三", 123 => "age", undefined => "未定义"}
/**set方法返回的是原 Map 对象,所以可以链式编程*/
map.set("name", "华安").set(123, "年龄");
console.log(map);//输出 {"name" => "华安", 123 => "年龄", undefined => "未定义"}
get(key) 方法读取 key 对应的键值,如果找不到 key,返回 undefined:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>ES6 测试</title>
<!-- JQuery CDN-->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="module">
$(function () {
let map = new Map();
map.set("name", "华安").set("age", 35);
map.set("address", "苏州");
console.log(map.get("name"), map.get("age"), map.get("address"), map.get("birthday"));
//输出:华安 35 苏州 undefined
});
</script>
</head>
<body>
</body>
</html>
has(key) 方法返回一个布尔值,表示某个键是否在当前 Map 对象之中:
let map = new Map();
map.set("name", "华安").set("age", 35);
map.set(110, "公安局");
console.log(map.has("name"), map.has("age"), map.has(110));//输出:true true true
console.log("abc");//输出:false
delete(key) 方法删除某个键,返回 true。如果删除失败,返回 false:
let map = new Map();
map.set("name", "华安").set("age", 35);
map.set(110, "公安局");
console.log(map.has("name"), map.has("age"), map.has(110));//输出:true true true
console.log(map.delete("name"));//输出:true
console.log(map.delete(110));// 输出:true
console.log(map.has("name"), map.has("age"), map.has(110));//输出:false true false
clear 方法清除所有成员,没有返回值:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>ES6 测试</title>
<!-- JQuery CDN-->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="module">
$(function () {
let map = new Map();
map.set("name", "华安").set("age", 35);
map.set(110, "公安局");
console.log(map.size);//输出:3
map.clear();
console.log(map.size);//输出:0
});
</script>
</head>
<body>
</body>
</html>
Map 遍历
Map 结构原生提供三个遍历器生成函数和一个遍历方法, 需要特别注意的是,Map 的遍历顺序就是插入顺序。
• keys():返回键名的遍历器。
• values():返回键值的遍历器。
• entries():返回所有成员的遍历器。
• forEach():遍历 Map 的所有成员。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>增强 For 循环</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
let map = new Map([
["name", "赵子龙"],
["age", 35],
["address", "常山"]
]);
console.log("--------1------");
// 依次输出:name、age、address
for (let keyLoop of map.keys()) {
console.log(keyLoop);
}
console.log("--------2------");
//依次输出:赵子龙、35、常山
for (let valueLoop of map.values()) {
console.log(valueLoop);
}
console.log("--------3------");
//依次输出:name 赵子龙、age 35、address 常山
/**其中循环的 item 相当于 Java 中 Map 迭代时的 Entry*/
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
console.log("--------4------");
//依次输出:name 赵子龙、age 35、address 常山
/**上面可以简写如下*/
for (let [keyLoop,valueLoop] of map.entries()) {
console.log(keyLoop, valueLoop);
}
console.log("--------5------");
//依次输出:name 赵子龙、age 35、address 常山
/**上面可以再简写如下*/
for (let [keyLoop,valueLoop] of map) {
console.log(keyLoop, valueLoop);
}
//依次输出:name 赵子龙、age 35、address 常山
console.log("--------6------");
map.forEach(function (value, key) {
console.log(key, value);
})
</script>
</head>
<body>
<div id="myDiv"></div>
</body>
</html>