[关闭]
@GivenCui 2016-06-04T20:04:50.000000Z 字数 4508 阅读 1027

第十课 Map (字典)

js高级


目录



JS课程合集跳转链接


Map对象详解

回顾JS对象

JS对象包括字面量对象和 new Object() 创建出来的对象
JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是只能用字符串当作键。这给它的使用带来了很大的限制。

  1. var dog = {5 : "我是卧底,我实际是字符串"};
  2. dog.name = "随便叫";
  3. console.log(dog.name); // 随便叫
  4. console.log(typeof name); // string
  5. console.log(dog["5"]) // 我是卧底,我实际是字符串
  6. // 侧面说明默认把5转换成了"5"
  1. /*******Demo2 对象不能作为字面量对象的键********/
  2. var data = {};
  3. var element = document.getElementById("myDiv");
  4. data[element] = metadata;
  5. data["[Object HTMLDivElement]"] // metadata
  6. // 上面代码原意是将一个DOM节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[Object HTMLDivElement]。

Map概念介绍

ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

Map和其它语言的字典的概念一模一样.
Map的数据形式是一个二维数组
JS中的Map和字面量对象的用法基本一样,可以存储诸如Number, String, Object等

  • map是一种红黑树的数据结构
  • map中的键是唯一的

Map语法

语法:
1.new Map()创建, set()动态添加
2.new Map(参数),参数为二维数组
var map = new Map([['a','刘'],['b','张']])

  1. // 语法2二维数组作为参数的内部实现原理
  2. var items = [
  3. ["a", "刘"],
  4. ["b", "张"]
  5. ];
  6. var map = new Map();
  7. items.forEach(([key, value]) => map.set(key, value));
  1. // 接受二维数组作为参数
  2. var map = new Map([
  3. ["staff1" , "部门名称"],
  4. ["staff2" , "部门名称"],
  5. ["staff3" , "部门名称"],
  6. ["staff4" , "部门名称"],
  7. ["staff5" , "部门名称"],
  8. ]);
  9. /*
  10. 通过console.log(map),在控制台打印出map:
  11. 显示为:
  12. Map { staff1: "部门名称", staff2: "部门名称", staff3: "部门名称", staff4: "部门名称", staff5: "部门名称" };
  13. 发现内部实现同字面量对象
  14. */

Map实例的属性和方法

1、size 属性返回成员数
2、clear() 该方法清除所有成员,没有返回值
3、has(key)
4、get(key)
5、set(key,value) 为key设置键值,返回值实例本身,所以可以采用链式写法

  1. // set()方法的链式写法
  2. let map = new Map()
  3. .set(1, 'a')
  4. .set(2, 'b')
  5. .set(3, 'c');
  6. console.log(map);

6、delete(key) 删除key, 返回值为true/false

  1. var m = new Map();
  2. var o = {p: "Hello World"}; //一个字面量对象
  3. m.set(o, "content") // set方法添加, o对象作为键
  4. m.get(o) // 查询键o的值 "content"
  5. m.has(o) // true has方法判断键是否存在,返回值为true/false
  6. m.delete(o) // true 删除"键", 返回值是true/false
  7. m.has(o) // false 证明元素不存在
  1. // 可以添加键值对儿
  2. // 值可以是数组、字符串、对象等
  3. map.set("c", "王");
  4. // 通过某个键获得某个值
  5. map.get("c");
  6. // 如果添加的键已经存在, 那么会把该键的值覆盖掉. 不会有重复的键出现.
  7. // 一句话就是, 键不可重复添加

forEack遍历Map对象

 用到forEach()

  1. 对象.forEach(function (元素, keyindex , 调用的对象本身) {
  2. // 每个元素都执行的代码
  3. });
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title> 字典Map </title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. // 字典Map
  10. // 创建一个map对象
  11. var map = new Map();
  12. // 给map添加键值对
  13. map.set("a", "阿拉斯加");
  14. map.set("b", "编写");
  15. map.set("c", {name : "张xx",age : 18});
  16. // 添加个重复的键
  17. map.set("a","重复就覆盖");
  18. console.log(map);
  19. // 通过键获得值
  20. console.log(map.get("a"));
  21. // forEach 遍历Map
  22. map.forEach(function (value, key ,map123) {
  23. // 三个参数
  24. // value: map当前键对应的值
  25. // key: map当前的键
  26. // 对象本身,叫什么都行
  27. console.log("键: " + key);
  28. console.log("值: " + value);
  29. });
  30. // 数组调用forEach
  31. var arr = [1,"你好",false];
  32. arr.forEach(function (element, idx ,arr123) {
  33. console.log(element);
  34. })
  35. </script>
  36. </body>
  37. </html>

map对象的坑

一个键多次赋值,会覆盖

如果对同一个键多次赋值,后面的值将覆盖前面的值。

  1. let map = new Map();
  2. map
  3. .set(1, 'aaa')
  4. .set(1, 'bbb');
  5. map.get(1) // "bbb"

上面代码对键1连续赋值两次,后一次的值覆盖前一次的值。

键为引用类型的坑

只有对同一个对象的引用,Map结构才将其视为同一个键。这一点要非常小心。

  1. var map = new Map();
  2. map.set(['a'], 555);
  3. map.get(['a']) // undefined

上面代码的set和get方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined。

同理,同样的值的两个实例,在Map结构中被视为两个键。

  1. var map = new Map();
  2. var k1 = ['a'];
  3. var k2 = ['a'];
  4. map
  5. .set(k1, 111)
  6. .set(k2, 222);
  7. map.get(k1) // 111
  8. map.get(k2) // 222

上面代码中,变量k1和k2的值是一样的,但是它们在Map结构中被视为两个键。
由上可知,Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。

Map与其他类型的数据结构的相互转换

(1)Map转为数组

前面已经提过,Map转为数组最方便的方法,就是使用扩展运算符(...)。

  1. let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
  2. [...myMap]
  3. // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

(2)数组转化为Map

将数组转入Map构造函数,就可以转为Map。

  1. new Map([[true, 7], [{foo: 3}, ['abc']]])
  2. // Map {true => 7, Object {foo: 3} => ['abc']}

(3)Map转为对象

如果所有Map的键都是字符串,它可以转为对象。

  1. function strMapToObj(strMap) {
  2. let obj = Object.create(null);
  3. for (let [k,v] of strMap) {
  4. obj[k] = v;
  5. }
  6. return obj;
  7. }
  8. let myMap = new Map().set('yes', true).set('no', false);
  9. strMapToObj(myMap)
  10. // { yes: true, no: false }

(4)对象转为Map

  1. function objToStrMap(obj) {
  2. let strMap = new Map();
  3. for (let k of Object.keys(obj)) {
  4. strMap.set(k, obj[k]);
  5. }
  6. return strMap;
  7. }
  8. objToStrMap({yes: true, no: false})
  9. // [ [ 'yes', true ], [ 'no', false ] ]

(5)Map转为JSON

Map转为JSON要区分两种情况。一种情况是,Map的键名都是字符串,这时可以选择转为对象JSON。

  1. function strMapToJson(strMap) {
  2. return JSON.stringify(strMapToObj(strMap));
  3. }
  4. let myMap = new Map().set('yes', true).set('no', false);
  5. strMapToJson(myMap)
  6. // '{"yes":true,"no":false}'

(6)JSON转化为Map

JSON转为Map,正常情况下,所有键名都是字符串。

  1. function jsonToStrMap(jsonStr) {
  2. return objToStrMap(JSON.parse(jsonStr));
  3. }
  4. jsonToStrMap('{"yes":true,"no":false}')
  5. // Map {'yes' => true, 'no' => false}

但是,有一种特殊情况,整个JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map。这往往是数组转为JSON的逆操作。

  1. function jsonToMap(jsonStr) {
  2. return new Map(JSON.parse(jsonStr));
  3. }
  4. jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
  5. // Map {true => 7, Object {foo: 3} => ['abc']}
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注