JavaScript中的Map手册

178 阅读3分钟

Map

Map 是一种新的集合类型,它为 JS 带来了真正的键/值存储机制。作为ECMAScript 6 的新特性,它与 Object 之间又会有哪些区别呢?

本文将会为您讲述 Map 的基本API ,迭代,开发场景优势等。

1.基本API:get(), has(), delete(), clear()

// 使用 new 关键字和 Map 构造函数创建一个空映射
        const why = new Map();
​
        // set() 方法返回映射实例
        why.set('cat', 'white').set('dog', 'yellow').set('pig', 'pink');
​
        // 使用 get() 和 has() 方法进行查询
        console.log(why.has('cat'));   //true
        console.log(why.has('snake'));  //false
        console.log(why.get('cat'));   //white
        console.log(why.get('snake'));  //undefined
        // 值得一提的是,get(), has(), delete() 的参数都是键/值对中的键。
        // 使用 delete(), 和clear()
        why.delete('cat')
        console.log(why.has('cat'));   //false
        console.log(why.get('cat'));   //undefined
        console.log(why.size) //2
        why.clear()
        console.log(why.size)  //0

2.Map的特别之处在于:Map可以使用任何 JS 数据类型作为键

而Object只能使用数值,字符串或者符号作为键

// 使用new关键字和Map构造函数创建一个空映射
        const why = new Map();
        const functionKey = function () { };
        const symbolKey = Symbol();
        const objectKey = new Object();
// 将函数,Symbol和对象作为键
        why.set(functionKey, 'functionCat');
        why.set(symbolKey, 'symbolCat');
        why.set(objectKey, 'objectCat');
        why.set(1, '1Cat');
​
        console.log(why.get(functionKey))  //functionCat
        console.log(why.get(symbolKey))  //symbolCat
        console.log(why.get(objectKey))  //objectCat
        console.log(why.get(1))  //1Cat

3.Map的迭代

Map 实例会维护键/值对的插入顺序 ,所以迭代操作可以根据插入的顺序

在 Map 实例中会提供一个迭代器(Interator),能够以插入的顺序生成 [key,value] 的数组。

Map 提供了一个属性和一个方法来取得迭代器,entries() 方法和 Symbol.iterator属性

// 使用new关键字和Map构造函数创建一个空映射
        const why = new Map([
            ['cat', 'white'],
            ['dog', 'yellow'],
            ['pig', 'pink']
        ]);
        console.log(why.keys());  //[cat,dog,pig]
        console.log(why.values());  //[white,yellow,pink]
        for (let m of why.entries()) {
            console.log(m);  //遍历得到数组的每一个键/值对
        }
​

4.迭代时的修改

键和值在迭代器遍历的时候是可以修改的,但在映射内部的引用是无法修改的。

  • 遍历中修改键
    const why = new Map().set('cat', 'white');
        for (let key of why.keys()) {
            key = "dog";
            console.log(key);  //dog
            console.log(why.get('dog'));  //undefined
            console.log(why.get('cat'));  //white
        }
  • 遍历中修改值
       const why = new Map().set('cat', 'white');
       for (let value of why.values()) {
            value = "black";
            console.log(value);  //black
            console.log(why.get('cat'));  //white
        }
  • 当Map实例是一个对象时,是可以修改该对象的属性,且对象在映射内部仍然引用相同的值
 const cat = { age: 1 }
        const why = new Map([
            [cat, 'white'],
        ])
        for (let key of why.keys()) {
            key.age = 100;
            console.log(key);   //{ age: 100 }
            console.log(why.get(cat)); // white
        }
        console.log(cat);  //{ age: 100 }

5.Object 与Map 的开发场景比较

  • 内存占用 批量添加或者删除键/值对取决于各个浏览器对该类型内存分配的工程实现,不同的浏览器情况不同。但是存储单个键/值对所占用的内存数量都会随键的数量线性增加。但是若给定大小的内存,Map 大约可以比 Object 多存储50%的键/值对
  • 插入性能 插入Map在所有的浏览器中一般会比插入 Object 稍微快点,若涉及大量插入工作,Map性能更好
  • 查找速度 从大型 Object 和 Map 中查找键值对的性能差异非常的小,但是当只包含少量键值对,则Object有时候更快。在把 Object当成数组使用的情况下,浏览器引擎可以优化,在内存中使用更高效的布局,这对 Map 无法实现。如果代码涉及大量的查找操作,选择 Object 更好一些。
  • 删除性能 Object 使用 delete 的删除性能一直饱受诟病,为此,出现了伪删除对象属性的操作,例如把属性值设置为 undefined 或者 null。但是这是一种令人讨厌的或者是不适宜的折中。对于大多数浏览器引擎,Map的delete()操作比插入和查找更快。如果涉及大量删除操作,应使用Map