Map与WeakMap在JavaScript中的核心用法与实战

作者:代码拾荒时间:2026-10-24 06:14:24浏览:113

本文详解JavaScript中Map与WeakMap的核心特性。涵盖Map的创建、增删改查、遍历及类型转换,演示如何利用Map管理DOM节点与控制表单提交。深入解析WeakMap语法与弱引用机制,并通过选课组件开发案例展示其实际应用场景,帮助开发者掌握数据结构的高效使用。

Map类型基础与操作

Map类型具有独特的特性,其创建方式灵活。在JavaScript中,Map对象用于存储键值对,且键可以是任意类型的值,包括对象。创建Map实例时,可以传入一个可迭代对象,如数组,其中每个元素也是包含两个元素的数组,分别代表键和值。例如,通过new Map([[key, value]])即可初始化。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>


  <script>
    // 对象的键名会自动转为字符串
    // let obj = {
    //   1: 'cyy1',
    //   '1': 'cyy2'
    // };
    // console.log(obj);

    // 对象的键名是对象时,会自动转为字符串
    // 读取时也要先转为字符串再读取
    //     let obj = {
    //       name: 'cyy'
    //     };
    //     let obj2 = {
    //       obj: 'cyy2',
    //       [obj]: 'cyy3'
    //     };
    //     console.log(obj2);
    //     console.log(obj2[obj.toString()]);

    // map类型,什么都可以作为键,键名可以是任何类型
    // let map = new Map();
    // map.set('name', 'cyy');
    // map.set(function() {}, 'cyy2');
    // map.set({}, 'cyy3');
    // map.set(1, 'cyy4');
    // console.log(map);

    // 构造函数创建时加入数据
    let map = new Map([
      ['name', 'cyy'],
      ['age', 18]
    ]);
    console.log(map);

    // 支持链式操作
    let str = 'cyy';
    let str2 = str.toUpperCase().substr(1, 2);
    console.log(str2);

    map.set('11', 11).set('22', 22);
    console.log(map);
  </script>
</body>

</html>

对Map类型进行增删改查操作十分直观。使用set(key, value)方法添加或更新键值对;get(key)用于获取指定键的值;has(key)判断键是否存在;delete(key)移除指定键值对;clear()则清空整个Map。值得注意的是,Map中的键比较遵循严格相等(===)规则,这意味着即使两个对象内容相同,它们也是不同的键,这解决了传统对象键名自动转为字符串的问题。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>


  <script>
    let obj = {
      name: 'cyy'
    };
    let map = new Map();
    map.set(obj, 'obj');
    console.log(map.has(obj));
    console.log(map);
    console.log(map.get(obj));
    console.log(map.delete('abc'));
    console.log(map.delete(obj));
    map.clear();
    console.log(map);
  </script>
</body>

</html>

Map数据遍历与转换

遍历Map类型数据有多种方式。可以使用for...of循环结合entries()方法,或者直接使用keys()、values()和entries()迭代器。此外,forEach方法允许以回调函数形式遍历,回调函数接收值、键和Map对象本身作为参数。这种方式比传统对象遍历更灵活,能保持插入顺序。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>


  <script>
    let map = new Map([
      [1, 11],
      [2, 22]
    ]);
    // console.log(map.keys());
    // console.log(map.values());
    // console.log(map.entries());

    // for (let k of map.keys()) {
    //   console.log(k);
    // }
    // for (let v of map.values()) {
    //   console.log(v);
    // }
    // for (let e of map.entries()) {
    //   console.log(e);
    // }
    // for (let [k, v] of map.entries()) {
    //   console.log(`${k}--${v}`);
    // }

    map.forEach((item, key) => {
      console.log(item + '--' + key);
    })
  </script>
</body>

</html>

Map类型支持多种转换操作,便于与其他数据结构交互。可以将Map转换为数组,通过展开语法[...map]或[...map.entries()]实现。同样,Map的值可以通过展开语法...map.values()获取。这些转换使得Map数据可以方便地用于数组操作或序列化处理,增强了数据处理的灵活性。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>

  <script>
    let map = new Map([
      ['name', 'cyy'],
      ['age', '18']
    ]);
    // console.log(...map);
    // console.log([...map]);
    // console.log([...map.entries()]);
    // console.log([...map.keys()]);
    // console.log([...map.values()]);

    // let arr = [...map].filter(item => item[1].includes('cyy'));
    let arr = [...map].filter(item => {
      return item[1].includes('cyy');
    });
    // console.log(arr);
    let new_map = new Map(arr);
    console.log(new_map);
    console.log(new_map.values());
    // ...是展开语法
    console.log(...new_map.values());
  </script>
</body>

</html>

Map在DOM与表单中的应用

利用Map类型管理DOM节点是一种高效策略。由于Map的键可以是对象,可以将DOM元素作为键,存储相关的状态或数据。这种方式避免了全局变量污染,且能确保数据与DOM节点的生命周期紧密绑定。通过Map,可以轻松地根据DOM元素查找或更新其关联数据,提升代码的可维护性。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>
  <div name="cyy1">cyy1</div>
  <div name="cyy2">cyy2</div>

  <script>
    let map = new Map();
    let divs = document.querySelectorAll('div');
    divs.forEach(item => {
      // 往map中压入数据
      map.set(item, {
        content: item.getAttribute('name')
      });
    });
    // console.log(map);
    map.forEach((config, elem) => {
      // console.log(config, elem);
      elem.addEventListener('click', function() {
        console.log(config.content);
      });
    })
  </script>
</body>

</html>

使用Map类型控制网站表单提交可以简化逻辑。在表单处理中,可以将表单元素作为键,存储验证状态或提交数据。通过Map管理这些状态,可以在提交前快速检查所有字段的合法性,或者动态生成提交负载。这种方式提高了表单处理的准确性和效率,减少了冗余代码。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>
  <form action="#" onsubmit="return post()">
    接受协议:
    <input type="checkbox" name="agreement" error="请接受协议"> 我是学生:
    <input type="checkbox" name="student" error="网站只对学生开放">
    <input type="submit" value="提交">
  </form>

  <script>
    function post() {
      let map = new Map();
      let inputs = document.querySelectorAll('[error]');
      console.log(inputs);
      inputs.forEach(item => {
        map.set(item, {
          error: item.getAttribute('error'),
          status: item.checked
        });
      });
      // console.log(map);
      return [...map].every(([elem, config]) => {
        // 短路操作,前面为真,则不会执行后面
        // 前面会假,则执行后面
        config.status || alert(config.error);
        return config.status;
        // console.log(config);
      });

    }
  </script>
</body>

</html>

Map类型基础与操作

WeakMap语法与弱引用机制

WeakMap的语法结构与Map类似,但其键必须是对象,且值也是弱引用。创建WeakMap使用new WeakMap(),并通过set(key, value)、get(key)、has(key)和delete(key)进行操作。WeakMap的关键特性在于其弱引用性质,当键对象不再被其他引用时,垃圾回收机制会自动清理WeakMap中的对应条目,防止内存泄漏。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <style>

  </style>
</head>

<body>
  <div>cyy</div>
  <div>cyy2</div>

  <script>
    // WeakMap的键只能是引用对象
    // let map = new WeakMap();
    // map.set('name');
    // console.log(map);

    // map.set([]);
    // console.log(map);

    // let map = new WeakMap();
    // let divs = document.querySelectorAll('div');
    // divs.forEach(item => map.set(item, item.innerHTML));
    // console.log(map);

    // WeakMap也是弱引用类型
    // let arr = [];
    // let map = new WeakMap();
    // map.set(arr, 'cyy');
    // map.delete(arr);
    // console.log(map.has(arr));
    // console.log(map);

    // 弱引用类型,values、keys、entries、迭代循环都用不了
    let map = new WeakMap();
    // console.log(map.keys());

    for (const iterator of map) {
      console.log(iterator);
    }
  </script>
</body>

</html>

体验WeakMap的弱引用特性有助于理解其内存管理优势。在WeakMap中,如果键对象被外部引用移除,WeakMap会自动释放该键值对,不会阻止垃圾回收。这一特性使得WeakMap非常适合存储与对象关联的私有数据或元数据,而无需担心内存占用问题。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
  <style>

  </style>
</head>

<body>


  <script>
    let a = { name: 'cyy' };
    let b = a;
    let map = new WeakMap();
    map.set(a, 'cuu2');
    console.log(map);
    a = null;
    b = null;
    console.log(map);
    setTimeout(function () {
      console.log(map);
    }, 1000);
  </script>
</body>

</html>

WeakMap实战:选课组件开发

使用WeakMap开发选课组件是一个典型应用场景。在选课系统中,可以将学生对象作为键,存储其选课信息。由于WeakMap的弱引用特性,当学生对象不再被引用时,选课数据也会自动清理,避免了内存泄漏。这种方式既保证了数据的关联性,又优化了内存使用,适合处理大量动态对象。


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    main {
      width: 100%;
      display: flex;
      flex: 1;
    }

    section {
      border: 2px solid #ddd;
      width: 50%;
      padding: 10px;
    }

    ul {
      list-style: none;
    }

    li {
      padding: 10px;
      border: 2px solid orange;
      margin-bottom: 5px;
      position: relative;
    }

    a {
      display: inline-block;
      position: absolute;
      right: 10px;
      width: 20px;
      height: 20px;
      background: green;
      color: white;
      text-decoration: none;
      line-height: 20px;
      text-align: center;
    }

    #list span {
      background: green;
      border-radius: 5px;
      padding: 5px;
      color: white;
      margin: 5px;
    }

    #list {
      margin-top: 20px;
    }
  </style>
</head>

<body>
  <main>
    <section>
      <ul>
        <li><span>html</span><a href="javascript:;">+</a></li>
        <li><span>css</span><a href="javascript:;">+</a></li>
        <li><span>js</span><a href="javascript:;">+</a></li>
      </ul>
    </section>
    <section>
      <strong id="count">共选了2门课</strong>
      <p id="list">
        <!-- <span>111</span> -->
      </p>
    </section>
  </main>

  <script>
    class Lesson {
      // 构造函数
      constructor() {
        this.lis = document.querySelectorAll('li');
        this.countElem = document.getElementById('count');
        this.listElem = document.getElementById('list');
        // console.log(this.lis, this.countElem, this.listElem);
        this.map = new WeakMap();
      }
      run() {
        this.lis.forEach(li => {
          li.querySelector('a').addEventListener('click', event => {
            let a = event.target;
            // console.log(li);
            // console.log(event.target.parentElement);
            const state = li.getAttribute('select');
            if (state) {
              // 移除
              li.removeAttribute('select');
              this.map.delete(li);
              a.style.backgroundColor = 'green';
              a.innerHTML = '+';
            } else {
              // 添加
              li.setAttribute('select', true);
              this.map.set(li);
              a.style.backgroundColor = 'red';
              a.innerHTML = '-';
            }
            // console.log(this.map);
            this.render();
          });
        })
      }
      render() {
        this.countElem.innerHTML = `共选了${this.count()}门课`;
        // console.log(this.count());
        this.listElem.innerHTML = this.list();
      }
      count() {
        return [...this.lis].reduce((count, li) => {
          return count += this.map.has(li) ? 1 : 0;
        }, 0);
      }
      list() {
        let lis = [...this.lis].filter(li => {
          return this.map.has(li);
        }).map(li => {
          return `<span>${li.querySelector('span').innerHTML}</span>`;
        }).join('');
        // console.log(lis);
        return lis;
      }
    }
    new Lesson().run();
  </script>
</body>

</html>

Map类型基础与操作

本文介绍了Map与WeakMap在JavaScript中的核心用法与实战技巧。通过掌握Map的增删改查、遍历、转换及DOM管理,以及WeakMap的弱引用机制,开发者可以更高效地处理复杂数据结构。更多相关内容可参考本站其他文章,感谢支持。

作者最新文章