• 【JavaScript面试】map()方法


    Map

    Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。

    声明一个空的Map

    var map = new Map()
    
    • 1

    声明一个自定义的Map

    var map = new Map([['name','Tom'],['age',18]])
    注意:这里定义的[]中都是用,的形式存入
    
    • 1
    • 2

    插入值到map

    map.set('key','value')
    
    • 1

    根据键得到它对应的值

    map.get('key')
    
    • 1

    判断map对应的键值是否存在

    map.has('key')
    
    • 1

    删除map中某一对值

    map.delete('key')
    
    • 1

    清空map

    map.clear()
    
    • 1
    遍历整个map

    浅析 Map 和 WeakMap 区别以及使用场景
    以采用for…of循环和forEach两种方法。由于Map实例会维护键值对的插入顺序,因此可以根据插入顺序进行遍历

    maps.forEach((elem,value) => {
      console.log(elem)     //这个是值
      console.log(value)    //这个是键
    })
    
    • 1
    • 2
    • 3
    • 4

    初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:

    var m = new Map(); // 空Map
    m.set('Adam', 67); // 添加新的key-value
    m.set('Bob', 59);
    m.has('Adam'); // 是否存在key 'Adam': true
    m.get('Adam'); // 67
    m.delete('Adam'); // 删除key 'Adam'
    m.get('Adam'); // undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:

    var m = new Map();
    m.set('Adam', 67);
    m.set('Adam', 88);
    m.get('Adam'); // 88 
    
    • 1
    • 2
    • 3
    • 4

    Map的特点:

    1. Map 默认情况下不包含任何键,所有键都是自己添加进去的。不同于 Object 原型链上有一些默认的键。

    2. Map 的键可以是任意类型数据,就连函数都可以。

    3. Map 的键值对个数可以轻易通过size属性获取,Object 需要手动计算。

    4. Map 在频繁增删键值对的场景下性能要比 Object 好。

    什么时候用 Map?

    • 要添加的键值名和 Object 上的默认键值名冲突,又不想改名时,用 Map
    • 需要 String 和 Symbol 以外的数据类型做键值时,用 Map
    • 键值对很多,有需要计算数量时,用 Map
    • 需要频繁增删键值对时,用 Map

    WeakMap

    WeakMap 对象的 key 只能是 Object 实例化的对象或者派生类的对象,如果不是的话,则会报错;WeakMap 对象的 value 可以是任意类型:

    // 1. Object 实例化的对象
    const k1 = new Object()
    
    // 2. 使用字面量对象,字面量对象实际上也是 Object 实例化的对象
    const k2 = {}
    
    // 3. Object 派生类的对象
    class Other extends Object{}
    const k3 = new Other()
    
    // WeakMap 的 value 可以是任意的基本类型或者引用对象
    const wm = new WeakMap([
        [k1, 10086],
        [k2, "I am China Moblie"],
        [k3, new Array("Interesting")]
    ])
    
    // 如果 key 不符合规范则会报错
    const badWM = new WeakMap([
        [1, 2],
        ["Yoo", "Ugh"]
    ])
    //> TypeError: Invalid value used as weak map key
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    weakMap 的其他 API 基本和 Map对象是一样的,但注意,并没有 size 属性和 clear() 方法

    • set(k, v),添加一对新的键值对,如果键已存在,则用新值覆盖其旧值
    • get(k),根据键得到它对应的值
    • has(k),判断是否存在该键
    • delete(k):删除指定的键值对
    弱键

    WeakMap 的 key 只能是 Object 实例化对象或者派生类对象的目的是,让这个key被弱持有。

    假设我们有一个场景,我们需要存储DOM节点的属性以及它的值,我们可能会用到 Object 或者 Map,假设使用 Map:

    const m = new Map()
    // 假设我们需要保存一个登录按钮的属性值
    const loginButton = document.querySelector("#login")
    m.set(loginButton, {disabled: true})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样会产生一个问题:当用户登录之后,跑到另外一个页面,登录按钮被移除了,正常来说,这个登录 DOM 节点应该也应该被垃圾回收器清除,但它被 loginButton 变量引用,而 loginButton 作为 key 被 map 引用,所以登录 DOM 节点会保留在内存中,白白占用空间。

    这时候解决方法是手动解除引用,要么使用 delete 方法删除该键值对,要么等 Map 对象被销毁。

    如果我们使用 WeakMap 对象进行同样的储存:

    const wm = new WeakMap()
    const loginButton = document.querySelector("#login")
    wm.set(loginButton, {disabled: true})
    
    
    • 1
    • 2
    • 3
    • 4

    作为 WeakMap 对象key的 loginButton 不会被算成正式的引用(formal reference),也就是说 loginButton 变量相当于不会被 wm 引用,这时垃圾回收器就可以把这个 loginButton 变量和登录 DOM节 点都给干掉,释放内存空间。

    这样就起到了自动清理的效果,这也是 WeakMap 弱持有的目的所在。

    不可迭代键

    WeakMap 的 key 是不算正式引用,随时可能会被回收清除掉,因此 WeakMap 不提供迭代的功能。

    对于 size 属性和 clear() 方法,由于它们需要先迭代遍历所有的 key 才能计算得到,所以同样无法使用。

    Map和weakMap之间的区别和联系?

    Map 相对于 WeakMap :

    • Map 的键可以是任意类型,WeakMap 只接受对象作为键(null除外),不接受其他类型的值作为键
    • Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键; WeakMap 的键是弱引用,键所指向的对象可以被垃圾回收,此时键是无效的
    • Map 可以被遍历, WeakMap 不能被遍历
    WeakMap 的使用场景?
    1. DOM 节点元数据
    2. 部署私有属性
    3. 数据缓存

    Set

    Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

    ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

    NaN 和 undefined 都可以被存储在 Set 中,NaN 之间被视为相同的值(NaN 被认为是相同的,尽管 NaN !== NaN)

    Set常用操作

    方法/属性功能介绍
    size获取当前Set对象的长度
    add(value)向当前Set对象中添加一个值,返回的是Set对象,所以支持链式写法
    delete(value)删除当前Set对象中的一个值,返回一个布尔值,表示是否删除成功
    has(value)检测这个value是否是当前Set对象的一个元素,通过返回的布尔值表示
    clear()清除当前Set对象所有元素,没有返回值
    const s = new Set();
     
    s.add(1).add(2).add(2);
     
    console.log(s.size);
     
    console.log(s.has(1));
    console.log(s.has(2));
    console.log(s.has(3));
     
    s.delete(2);
    console.log(s.has(2));
     
    // set转数组
    const items = new Set([1,2,3,4,5]);
    const array = Array.from(items);
    console.log(array);
     
    // 去除数组重复成员
    const items = [1,2,3,4,5];
    console.log([...new Set(items]);
    
    //注意:传入NaN和引用类型时,达不到去重的效果
     const arr = [1, {}, {}, 2]
     console.log([...new Set(arr)]); //[1, {}, {}, 2]
     
     const arr = [1, NaN, NaN, 2]
     console.log([...new Set(arr)]); // [1, NaN, 2]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    let set = new Set(['red', 'green', 'blue']);
     
    // 返回键名
    for(let item of set.keys()) {
      console.log(item);
    }
     
    // 返回键值
    for(let item of set.values()) {
      console.log(item);
    }
    // set 键名=键值
     
    // 返回键值对
    for(let item of set.entries()){
      console.log(item);
    }
     
    // 可以直接用 for of遍历Set
    // for in 和 for of的区别是:in 是遍历对象,of是遍历值
    for (let x of set) {
      console.log(x);
    }
     
    // set也有forEach()方法
    set.forEach((value, key) => console.log(key + ' : ' + value));
    // 此处forEach方法的参数是一个处理函数。
     
    // 数组的 map 和 filter 方法也可以间接用于Set
    let s = new Set([1,2,3]);
     
    // map 将原数组映射成新数组
    s = new Set([...s].map(x => x * 2));
    console.log(s);
     
    // filter返回过滤后的新数组
    s = new Set([...s].filter(x => (x % 3) ==0));
    console.log(s);
     
    // 实现并集、交集、差集
    let a = new Set([1,2,3]);
    let b = new Set([4,3,2]);
     
    let union = new Set([...a, ...b]);
    console.log(union);
     
    let intersect = new Set([...a].filter(x => b.has(x)));
    console.log(intersect);
     
    let difference = new Set([...a].filter(x => !b.has(x)));
    console.log(difference);
     
    // 在遍历操作中,同步改变原来的Set结构的两种变通方法
     
    // 1.利用原Set结构映射出一个新的结构,然后赋值给原来的Set结构
    let set1 = new Set([1,2,3]);
    set1 = new Set([...set1].map(val => val *2));
    console.log(set1);
     
    // 2.利用Array.from
    let set2 = new Set([1,2,3]);
    set2 = new Set(Array.from(set2, val => val * 2));
    console.log(set2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    const s = new Set();
    
    [2,3,5,4,5,2,2].forEach(x => s.add(x));
    // Set结构不会添加重复的值
     
    for(let i of s) {
      console.log(i);
    }
     
     
    // ## 初始化
    // 例一 可以接受一个数组作为参数
    const set = new Set([1,2,3,4,4,]);
     
    // ...将一个数组转为用逗号分隔的参数序列
    console.log([...set]);
     
    // 例二
    const items = new Set([1,2,3,4,5,5,5,5,]);
    console.log(items.size);
     
     
    // 例三 可以接受具有iterable接口的其他数据结构作为参数
    const set2 = new Set(document.querySelectorAll('div'));
    console.log(set.size);
     
    // 类似于
    const set2 = new Set();
    document
        .querySelectorAll('div')
        .forEach(div => set.add(div));
    console.log(set.size);
     
    // set中NaN等于自身,其余比较相当于 ===
    let set3 = new Set();
    let a = NaN;
    let b = NaN;
    set3.add(a);
    set3.add(b);
    console.log(set3)
     
    // 两个对象总是不相等的
    let set4 = new Set();
    set4.add({});  // 1
    console.log(set4.size);
     
    set4.add({});  // 2
    console.log(set4.size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    Set 遍历方法

    方法/属性功能介绍
    keys()返回该Set对象键名的遍历器,等同values()
    values()返回该Set对象键值的遍历器,等同keys()
    entries()返回该Set对象键值对的遍历器(目前感觉没什么用)
    forEach()使用回调函数遍历该Set对象的每个元素
    WeakSet

    JavaScript垃圾回收是一种内存管理技术。在这种技术中,不再被引用的对象会被自动删除,而与其相关的资源也会被一同回收。Set中对象的引用都是强类型化的,并不会允许垃圾回收。这样一来,如果Set中引用了不再需要的大型对象,如已经从DOM树中删除的DOM元素,那么其回收代价是昂贵的。
    为了解决这个问题,ES6还引入了WeakSet的弱集合。这些集合之所以是“弱的”,是因为它们允许从内存中清除不再需要的被这些集合所引用的对象。
    首先让我们了解下WeakSet与Set的不同,以下三点是WeakSet与Set不一样的地方:

    1. Set可以存储值类型和对象引用类型,而WeakSet只能存储对象引用类型,否则会抛出TypeError。
    2. WeakSet不能包含无引用的对象,否则会被自动清除出集合(垃圾回收机制)。
    3. WeakSet对象是不可枚举的,也就是说无法获取大小,也无法获取其中包含的元素。
    let weakset = new WeakSet();
    (function(){ 
       let a = {}; 
       weakset.add(1); //TypeError: Invalid value used in weak set
       weakset.add(a);
    })();  //here 'a' is garbage collected from weakset
    console.log()
    console.log(weakset.size); //output "undefined"
    console.log(...weakset); //Exception is thrown
    weakset.clear(); //Exception, no such function
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    const weakset=new WeakSet();
    let foo={bar:1};
    weakset.add(foo);
    console.log(weakset.has(foo)); //output true
    foo=null;
    console.log(weakset.has(foo)); //output false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    总结Map和Set的区别

    (1) 这两种方法具有极快的查找速度;
    (2) 初始化需要的值不一样,Map需要的是一个二维数组,而Set 需要的是一维 Array 数组
    (3) Map 和 Set 都不允许键重复
    (4) Map的键是不能修改,但是键对应的值是可以修改的;Set不能通过迭代器来改变Set的值,因为Set的值就是键。
    (5) Map 是键值对的存在,值也不作为健;而 Set 没有 value 只有 key,value 就是 key;

    10分钟讲明白WeakSet和WeakMap的弱引用

  • 相关阅读:
    《DevOps实践指南》笔记:第2章
    JVM——GC垃圾回收机制
    Leetcode1455 检查单词是否为句中其他单词的前缀
    c#中适配器模式详解
    linux-kernel 启动过程 一
    TCP Vegas 收敛性与吞吐
    25G LWDM与25G MWDM光模块的区别对比
    【数据结构-进阶】二叉搜索树
    WebGIS开发基础
    Concanavalin A-CY5.5/cy7/cy3 花菁染料CY5.5标记伴刀豆球蛋白A
  • 原文地址:https://blog.csdn.net/qq_40992225/article/details/126761312