JavaScript 中有了Object 为什么还需要 Map 呢

编辑: admin 分类: javascript 发布时间: 2021-11-17 来源:互联网
目录
  • 一、别把对象当 Map
    • 1、可能通过原型链访问到未定义的属性
    • 2、对象的 Key 只能是字符串
  • 二、使用 Map
    • 1、Map 常用操作
    • 2、遍历 Map
    • 3、Map 中判断 key 相等
    • 4、复制或合并 Map
    • 5、Map 序列化
  • 三、Map 和 Object 的性能差异

    一、别把对象当 Map

    1、可能通过原型链访问到未定义的属性

    假设现有场景,开发一个网站,需要提供日语、汉语、韩语三种语言,我们可以定义一个字典去管理。

    const dictionary = {
        'ja': {
            'Ninjas for hire': '忍者を雇う',
        },
        'zh': {
            'Ninjas for hire': '忍者出租',
        },
        'ko': {
            'Ninjas for hire': '고용 닌자',
        }
    }
    
    console.log(dictionary.ja['Ninjas for hire']) // 忍者を雇う
    console.log(dictionary.zh['Ninjas for hire']) // 忍者出租
    console.log(dictionary.ko['Ninjas for hire']) // 고용 닌자
    
    

    这样我们就把不同语言的字典管理起来了。但是,当我们试图访问 constroctor 属性,问题就出现了。

    console.log(dictionary.ko['constructor']) // ƒ Object() { [native code] }
    
    

    对于不存在的属性,我们期望得到 undefined,结果却通过原型链访问到了未定义的属性,原型对象的 constructor 属性,指向构造函数。

    此处有一个解决办法是把原型设置为 null

    Object.setPrototypeOf(dictionary.ko, null)
    console.log(dictionary.ko['constructor']) // undefined
    
    

    2、对象的 Key 只能是字符串

    假设需要将对象的 key 映射为 html 节点。我们写如下代码:

    /* html部分
    <div id="firstElement"></div>
    <div id="secondElement"></div>
    */
    
    const firstElement = document.getElementById('firstElement')
    const secondElement = document.getElementById('secondElement')
    
    const map = {}
    
    map[firstElement] = {
        data: 'firstElement'
    }
    map[secondElement] = {
        data: 'secondElement'
    }
    
    console.log(map[firstElement].data)     // secondElement
    console.log(map[secondElement].data)    // secondElement
    
    

    第一个元素的数据被覆盖了,原因是对象中的 key 只能是字符串类型,当我们没有使用字符串类型时,它会隐式调用 toString() 函数进行转换。于是两个 html 元素都被转为字符串 [object HTMLDivElement]

    对象的键也可以为 Symbol,不过在 for..in 遍历和 Object.keys() 以及用 JSON.stringify() 进行序列化的时候,都会忽略为 Symbol 的键。

    二、使用 Map

    1、Map 常用操作

    Map 可以使用任何 JavaScript 数据类型作为键

    function People(name) {
        this.name = name
    }
    const zhangsan = new People('zhangsan')
    const xiaoming = new People('xiaoming')
    const lihua = new People('lihua')
    // 创建 Map
    const map = new Map()
    // 创建 Map 并进行初始化 将二维键值对数组转换成一个Map对象
    const map1 = new Map([
        ['key1', 'val1'],
        ['key2', 'val2'],
    ])
    // 将 Map 转为二维数组
    console.log(Array.from(map1)) // [ [ 'key1', 'val1' ], [ 'key2', 'val2' ] ]
    // 设置键值映射关系
    map.set(zhangsan, { region: 'HB' })
    map.set(xiaoming, { region: 'HN' })
    // 根据 key 获取对应值
    console.log(map.get(zhangsan)) // { region: 'HB' }
    console.log(map.get(xiaoming)) // { region: 'HN' }
    // 获取不存在的 key 得到 undefined
    console.log(map.get(lihua))     // undefined
    // 通过 has 函数判断指定 key 是否存在
    console.log(map.has(lihua))     // false
    console.log(map.has(xiaoming))  // true
    // map存储映射个数
    console.log(map.size)           // 2
    // delete 删除 key
    map.delete(xiaoming)
    console.log(map.has(xiaoming))  // false
    console.log(map.size)           // 1
    // clear 清空 map
    map.clear()
    console.log(map.size)           // 0
    
    

    2、遍历 Map

    Map 可以确保遍历的顺序和插入的顺序一致

    const zhangsan = { name: 'zhangsan' }
    const xiaoming = { name: 'xiaoming' }
    const map = new Map()
    map.set(zhangsan, { region: 'HB' })
    map.set(xiaoming, { region: 'HN' })
    // 每个键值对返回的是 [key, value] 的数组
    for (let item of map) { // = for (let item of map.entries()) {
        console.log(item)
        // [ { name: 'zhangsan' }, { region: 'HB' } ]
        // [ { name: 'xiaoming' }, { region: 'HN' } ]
    }
    // 遍历 key
    for (let key of map.keys()) {
        console.log(key)
        // { name: 'zhangsan' }
        // { name: 'xiaoming' }
    }
    // 遍历 value
    for (let key of map.values()) {
        console.log(key)
        // { region: 'HB' }
        // { region: 'HN' }
    }
    // 使用 forEach() 方法迭代 Map
    map.forEach(function(value, key) {
        console.log(key, value)
        // { name: 'zhangsan' } { region: 'HB' }
        // { name: 'xiaoming' } { region: 'HN' }
    })
    
    

    3、Map 中判断 key 相等

    Map 内部使用 SameValueZero 比较操作。

    关于SameValue SameValueZero

    SameValue (Object.is()) 和严格相等(===)相比,对于 NaN 和 +0,-0 的处理不同

    Object.is(NaN, NaN) // true
    Object.is(0, -0) // false
    
    

    SameValueZero SameValue 的区别主要在于 0 与 -0 是否相等。

    map.set(NaN, 0)
    map.set(0, 0)
    console.log(map.has(NaN))   // true
    console.log(map.has(-0))    // true
    
    

    4、复制或合并 Map

    Map 能像数组一样被复制

    let original = new Map([
        [1, {}]
    ])
    let clone = new Map(original) // 克隆 Map美国多ip服务器http://www.558idc.com/mgzq.html
    
    console.log(clone.get(1)); // {}
    console.log(original === clone) // false
    console.log(original.get(1) === clone.get(1)) // true
    

    多个 Map 合并

    let first = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three'],
    ]);
    let second = new Map([
        [1, 'uno'],
        [2, 'dos']
    ]);
    // 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的。
    // 展开运算符本质上是将 Map 对象转换成数组。
    let merged = new Map([...first, ...second]);
    
    console.log(merged.get(1)); // uno
    console.log(merged.get(2)); // dos
    console.log(merged.get(3)); // three
    

    5、Map 序列化

    Map 无法被序列化,如果试图用 JSON.stringify 获得 Map 的 JSON 的话,只会得到 "{}"。

    由于 Map 的键可以是任意数据类型,而 JSON 仅允许将字符串作为键,所以一般情况下无法将 Map 转为 JSON。

    不过可以通过下面的方式去尝试序列化一个 Map:

    // 初始化 Map(1) {"key1" => "val1"}
    const originMap = new Map([['key1', 'val1']])
    // 序列化 "[[\"key1\",\"val1\"]]"
    const mapStr = JSON.stringify(Array.from(originMap.entries())) 
    // 反序列化 Map(1) {"key1" => "val1"}
    const cloneMap = new Map(JSON.parse(mapStr))
    
    

    三、Map 和 Object 的性能差异

    内存占用

    不同浏览器的情况不同,但给定固定大小的内存,Map 大约可以比 Object 多存储 50% 的键/值对。

    插入性能

    Map 略快,如果涉及大量操作,建议使用 Map

    查找速度

    性能差异极小,但如果只包含少量键/值对,则 Object 有时候速度更快。Object 作为数组使用时浏览器会进行优化。如果涉及大量查找操作,选择 Object 会更好一些。

    删除性能

    如果代码涉及大量的删除操作,建议选择 Map

    到此这篇关于JavaScript 中有了Object 为什么还需要 Map 呢的文章就介绍到这了,更多相关JavaScript  Map 内容请搜索hwidc以前的文章或继续浏览下面的相关文章希望大家以后多多支持hwidc!