您当前的位置:首页 > 电脑百科 > 程序开发 > 前端

Maps与WeakMaps在DOM节点管理中的妙用

时间:2023-05-22 15:01:34  来源:微信公众号  作者:大迁世界
使用 Maps 和 WeakMaps 可以提高代码的可读性和可维护性。将DOM节点与相关数据关联起来,有助于使代码更清晰易懂。

这篇文章讨论了使用 Maps 和 WeakMaps 处理DOM节点的优势。Maps 和 WeakMaps 是非常实用的工具,尤其在处理大量DOM节点时,它们发挥着重要作用。

文章作者认为,使用 Maps 和 WeakMaps 处理 DOM 节点有以下几个优点。首先,它们可以方便地存储和检索数据。与其他数据结构相比,Maps和 WeakMaps 可以更简洁地组织和查找相关数据。其次,它们可以帮助开发者更好地管理内存。当不再需要某个DOM节点时,WeakMaps可以自动释放与该节点相关的内存,从而提高程序的性能。最后,使用 Maps 和 WeakMaps 可以提高代码的可读性和可维护性。将DOM节点与相关数据关联起来,有助于使代码更清晰易懂。

下面是正文:

JAVAScript中, 我们经常使用普通的对象来存储键/值数据,它们非常擅长这项工作 - 清晰易读:

const person = {
 firstName: 'Alex', 
 lastName: 'macArthur', 
 isACommunist: false
};

但是,当我们开始处理经常被读取、更改和添加属性的较大实体时,更倾向于使用 Maps。因为在某些情况下,Map 比对象具有多个优势,特别是性能问题或插入顺序比较重要的情况下。

但最近我特别喜欢使用它们来处理大量的DOM节点。

在阅读Caleb Porzio最近的博客文章时,我想到了这个想法。在这篇文章中,他正在使用由10,000个表行组成的表格,其中一个可以是“active”。为了管理选择不同行时的状态,使用对象作为键/值存储。这是他的一个迭代版本的注释版本。

import { ref, watchEffect } from 'vue';

let rowStates = {};
let activeRow;

document.querySelectorAll('tr').forEach((row) => {
    // Set row state.
    rowStates[row.id] = ref(false);

    row.addEventListener('click', () => {
        // Update row state.
        if (activeRow) rowStates[activeRow].value = false;

        activeRow = row.id;

        rowStates[row.id].value = true;
    });

    watchEffect(() => {
        // Read row state.
        if (rowStates[row.id].value) {
            row.classList.add('active');
        } else {
            row.classList.remove('active');
        }
    });
});

它使用一个对象作为大型哈希映射表,因此用于关联值的键必须是字符串,因此需要在每个项目上存在唯一的ID(或其他字符串值)。这带来了一些额外的编程开销,需要在需要时生成和读取这些值。

任何对象都可以作为键

相反,使用 Map 可以让我们直接将 html 节点作为键。因此,该代码片段最终看起来像这样:

import { ref, watchEffect } from 'vue';

- let rowStates = {};
+ let rowStates = new Map();
let activeRow;

document.querySelectorAll('tr').forEach((row) => {
- rowStates[row.id] = ref(false);
+   rowStates.set(row, ref(false));

    row.addEventListener('click', () => {
-       if (activeRow) rowStates[activeRow].value = false;
+       if (activeRow) rowStates.get(activeRow).value = false;

        activeRow = row;

-       rowStates[row.id].value = true;
+       rowStates.get(activeRow).value = true;
    });

    watchEffect(() => {
-       if (rowStates[row.id].value) {
+       if (rowStates.get(row).value) {
            row.classList.add('active');
        } else {
            row.classList.remove('active');
        }
    });
});

这里最明显的好处是我不需要担心每行存在唯一ID。节点引用本身是唯一的,可以作为键。因此,既不需要设置也不需要读取任何属性。这更简单、更具弹性。

读/写操作通常更高效

当我们处理更大的数据集时,操作的性能显著提高。甚至在规范中也有说明 - 必须以保持性能的方式构建 Map,以便随着项目数量的增加而增长:

Maps must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection.

“Sublinear” 的意思是性能不会随着 Map 的大小成比例地下降。因此,即使是大型 Map,性能也应该保持相当迅速。

再次强调,没有必要干扰DOM属性或通过类似字符串的ID执行查找。每个键本身就是一个引用,这意味着我们可以跳过一两个步骤。

我进行了一些基本的性能测试来确认所有这些。首先,按照Caleb的场景,我在页面上生成了10,000个 <tr> 元素:

const table = document.createElement('table');
document.body.Append(table);

const count = 10_000;
for (let i = 0; i < count; i++) {
  const item = document.createElement('tr');
  item.id = i;
  item.textContent = 'item';
  table.append(item);
}

接下来,我设置了一个模板来测量循环遍历所有这些行并将一些相关状态存储在对象或Map中需要多长时间。我还在 for 循环中运行了同样的过程多次,然后确定编写和读取所需的平均时间。

const rows = document.querySelectorAll('tr');
const times = [];
const testMap = new Map();
const testObj = {};

for (let i = 0; i < 1000; i++) {
  const start = performance.now();

  rows.forEach((row, index) => {
    // Test Case #1  
 // testObj[row.id] = index;
 // const result = testObj[row.id];

 // Test Case #2
 // testMap.set(row, index);
 // const result = testMap.get(row);
  });

  times.push(performance.now() - start);
}

const average = times.reduce((acc, i) => acc + i, 0) / times.length;

console.log(average);

我用不同的行大小运行了这个测试:

 

图片

 

请记住,即使是稍微不同的情况,这些结果也可能会有很大的差异,但总体而言,它们通常符合我的预期。在处理相对较少的项目时,Map 和对象之间的性能是可比的。但随着项目数量的增加,Map 开始拉开差距。性能的次线性变化开始显现。

WeakMaps更有效地管理内存

有一个特殊版本的 Map 接口,旨在更好地管理内存 - WeakMap 。它通过保持对其键的“弱”引用来实现这一点,因此,如果这些对象键中的任何一个不再具有其他地方绑定的引用,则它有资格进行垃圾回收。因此,当不再需要该键时,整个条目将自动从 WeakMap 中删除,从而清除更多内存。它也适用于DOM节点。

为了调整这个,我们将使用 FinalizationRegistry ,它会在你正在观察的引用被垃圾回收时触发回调。我们将从几个列表项开始:

<ul>
  <li id="item1">first</li>
  <li id="item2">second</li>
  <li id="item3">third</li>
</ul>

接下来,我们将把这些项目放入 WeakMap 中,并将 item2 注册为注册表监视的对象。我们将其删除,每当它被垃圾回收时,回调将被触发,我们将能够看到 WeakMap 如何发生变化。

但是...垃圾收集是不可预测的,也没有官方的方法来触发它,因此为了测试,我们将定期生成一堆对象并将它们保存在内存中。以下是整个脚本:

(async () => {
 const listMap = new WeakMap();

 // Stick each item in a WeakMap.
 document.querySelectorAll('li').forEach((node) => {
  listMap.set(node, node.id);
 });

 const registry = new FinalizationRegistry((heldValue) => {
  // Garbage collection has happened!
  console.log('After collection:', heldValue);
 });

 registry.register(document.getElementById('item2'), listMap);
    
 console.log('Before collection:', listMap);

 // Remove node, freeing up reference!
 document.getElementById('item2').remove();

  // Periodically create a bunch o' objects to trigger collection.
  const objs = [];
  while (true) {
     for (let i = 0; i < 100; i++) {
   objs.push(...new Array(100));
  }

  awAIt new Promise((resolve) => setTimeout(resolve, 10));
 }
})();

在发生任何事情之前,WeakMap 如预期的那样包含三个项。但是在从DOM中删除第二项并进行垃圾收集之后,它看起来有点不同

 

图片

 

由于节点引用在DOM中不再存在,整个条目已从 WeakMap 中删除,从而释放了更多的内存。这是一个很 nice 功能,有助于使环境的内存更加整洁。



Tags:Maps   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
使用 Mapstructure 解析 Json,你学会了吗?
背景前几天群里的小伙伴问了一个这样的问题:图片其实质就是在面对 value 类型不确定的情况下,怎么解析这个 json?我下意识就想到了 [mapstructure](https://github.com/mitchel...【详细内容】
2023-12-27  Search: Maps  点击:(151)  评论:(0)  加入收藏
.NET领域性能最好的对象映射框架Mapster使用方法
Mapster是一个开源的.NET对象映射库,它提供了一种简单而强大的方式来处理对象之间的映射。在本文中,我将详细介绍如何在.NET中使用Mapster,并提供一些实例和源代码。和其它框架...【详细内容】
2023-12-22  Search: Maps  点击:(79)  评论:(0)  加入收藏
Go 1.21新增的 maps 包详解
maps 包提供了几个非常有用的用于操作 map 类型(任何类型的 map)的函数,本文接下来详细介绍下这几个函数。maps.Clone定义如下:func Clone[M ~map[K]V, K comparable, V any](m...【详细内容】
2023-08-26  Search: Maps  点击:(253)  评论:(0)  加入收藏
Maps与WeakMaps在DOM节点管理中的妙用
使用 Maps 和 WeakMaps 可以提高代码的可读性和可维护性。将DOM节点与相关数据关联起来,有助于使代码更清晰易懂。这篇文章讨论了使用 Maps 和 WeakMaps 处理DOM节点的优势。...【详细内容】
2023-05-22  Search: Maps  点击:(316)  评论:(0)  加入收藏
重磅,MapStruct 1.5 发布,这次终于支持Map转为Bean
本月三号,MapStruct 1.5.0 Final发布,本次正式版距离上次正式版发布已经过去了快7年(上个正式版发布于2015年11月),此次发布除了修复了110多个bug外,还有以下新特性值得关注: 支持...【详细内容】
2022-06-21  Search: Maps  点击:(623)  评论:(0)  加入收藏
使用MapStruct,让Bean对象之间转换更简单
场景通常,在后端项目开发中,因为会有项目分层的设计,例如MVC架构,以及最近很火热的DDD架构中,会在不同的层级,有对应的DO,BO,VO,DTO等各种各样的POJO类,而我们在层级之间进行调用的数...【详细内容】
2022-02-23  Search: Maps  点击:(479)  评论:(0)  加入收藏
Google Maps与Photos服务即将带来一系列新功能
以在推出新功能前揭秘而闻名的应用研究员Jane Wong称,Google Photos谷歌照片可能很快就会允许用户直接在应用中设置账户资料图片。当在照片应用中搜索自身人名时,会出现一个新...【详细内容】
2020-06-22  Search: Maps  点击:(393)  评论:(0)  加入收藏
▌简易百科推荐
20k级别前端是怎么使用LocalStorage的,想知道吗?
当咱们把咱们想缓存的东西,存在localStorage、sessionStorage中,在开发过程中,确实有利于咱们的开发,咱们想看的时候也是一目了然,点击Application就可以看到。前言大家好,我是林...【详细内容】
2024-03-26  前端之神  微信公众号  Tags:前端   点击:(12)  评论:(0)  加入收藏
前端不存在了?盲测64%的人更喜欢GPT-4V的设计,杨笛一等团队新作
3 月 9 日央视的一档节目上,百度创始人、董事长兼 CEO 李彦宏指出,以后不会存在「程序员」这种职业了,因为只要会说话,人人都会具备程序员的能力。「未来的编程语言只会剩下两种...【详细内容】
2024-03-11  机器之心Pro    Tags:前端   点击:(9)  评论:(0)  加入收藏
前端开始“锈化”?Vue团队开源JS打包工具:基于Rust、速度极快、尤雨溪主导
Vue 团队已正式开源Rolldown &mdash;&mdash; 基于 Rust 的 JavaScrip 打包工具。Rolldown 是使用 Rust 开发的 Rollup 替代品,它提供与 Rollup 兼容的应用程序接口和插件接口...【详细内容】
2024-03-09  OSC开源社区    Tags:Vue   点击:(11)  评论:(0)  加入收藏
两年前端经验还不会手写Promise?
什么是promise?当我们处理异步操作时,我们经常需要进行一系列的操作,如请求数据、处理数据、渲染UI等。在过去,这些操作通常通过回调函数来处理,但是回调函数嵌套过多会导致代码...【详细内容】
2024-03-07  海燕技术栈  微信公众号  Tags:Promise   点击:(23)  评论:(0)  加入收藏
网站开发中的前端和后端开发有什么区别
前端开发和后端开发都是干什么的?有哪些区别?通俗地讲,前端干的工作是用户可以直接看得见的,而后端开发的工作主要在服务端,用户不太能直接看到。虽然前端开发和后端开发的工作有...【详细内容】
2024-02-21  CarryData    Tags:前端   点击:(32)  评论:(0)  加入收藏
网站程序开发中的前后端分离技术
随着互联网的快速发展和技术的不断创新,传统的网站开发模式已经难以满足日益增长的业务需求。为了提高开发效率、增强系统的可维护性和可扩展性,前后端分离技术逐渐成为了网站...【详细内容】
2024-01-31  网站建设派迪星航    Tags:前后端分离   点击:(23)  评论:(0)  加入收藏
如何优雅的实现前端国际化?
JavaScript 中每个常见问题都有许多成熟的解决方案。当然,国际化 (i18n) 也不例外,有很多成熟的 JavaScript i18n 库可供选择,下面就来分享一些热门的前端国际化库!i18nexti18ne...【详细内容】
2024-01-17  前端充电宝  微信公众号  Tags:前端   点击:(69)  评论:(0)  加入收藏
Vue中Scope是怎么做样式隔离的?
scope样式隔离在 Vue 中,样式隔离是通过 scoped 特性实现的。当在一个组件的 <style> 标签上添加 scoped 特性时,Vue 会自动为这个样式块中的所有选择器添加一个唯一的属性,以...【详细内容】
2024-01-04  海燕技术栈  微信公众号  Tags:Vue   点击:(80)  评论:(0)  加入收藏
vue3中 ref和 reactive的区别 ?
最近有朋友在面试过程中经常被问到这么一个问题,vue3 中的ref 和 reactive的区别在哪里,为什么 要定义两个API 一个 api不能实现 响应式更新吗??带着这个疑问 ,我们 接下来进行逐...【详细内容】
2024-01-03  互联网高级架构师  今日头条  Tags:vue3   点击:(38)  评论:(0)  加入收藏
React18 与 Vue3 全方面对比
1. 编程风格 & 视图风格1.1 编程风格 React 语法少、难度大;Vue 语法多,难度小例如指令:Vue<input v-model="username"/><ul> <li v-for="(item,index) in list" :key="inde...【详细内容】
2024-01-03  爱做梦的程序员  今日头条  Tags:Vue3   点击:(72)  评论:(0)  加入收藏
站内最新
站内热门
站内头条