如何使用Map处理Dom节点

本文浅析一下为什么Map(和WeakMap)在处理大量DOM节点时特别有用。

我们在JavaScript中使用了很多普通的、古老的对象来存储键/值数据,它们处理的非常出色:

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

但是,当你开始处理较大的实体,其属性经常被读取、更改和添加时,人们越来越多地使用Map来代替。这是有原因的:在某些情况下,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(或其他字符串值)。这带来了一些额外的程序性开销,以便在需要时生成和读取这些值。

对象即key

与之对应的是,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的构建方式必须能够在项目数量不断增加时保持性能:

Map必须使用哈希表或其他机制来实现,平均来说,这些机制提供的访问时间是集合中元素数量的亚线性。

"亚线性"只是意味着性能不会以与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);

下面是测试结果:

100行 10000行 100000行
Object 0.023ms 3.45ms 89.9ms
Map 0.019ms 2.1ms 48.7ms
17% 39% 46%

请记住,这些结果在稍有不同的情况下可能会有相当大的差异,但总的来说,它们总体上符合我的期望。当处理相对较少的项目时,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中被移除并发生垃圾回收后,它看起来有点不同:

image.png

由于节点引用不再存在于DOM中,整个条目都被从WeakMap中删除,释放了一点内存。这是一个我很欣赏的功能,有助于保持环境的内存更加整洁。

太长不看版

我喜欢为DOM节点使用Map,因为:

  • 节点本身可以作为键。我不需要先在每个节点上设置或读取独特的属性。
  • 和具有大量成员的对象相比,Map(被设计成)更具有性能。
  • 使用以节点为键的WeakMap意味着如果一个节点从DOM中被移除,条目将被自动垃圾回收。

以上就是本文的全部内容,如果对你有所帮助,欢迎点赞、收藏、转发~

本文转载于网络 如有侵权请联系删除

相关文章

  • leetcode - 可以形成最大正方形的矩形数目

    题意给你一个数组rectangles,其中rectangles[i]=[li,wi]表示第i个矩形的长度为li、宽度为wi。如果存在k同时满足k<=li和k<=wi,就可以将第i个矩形切成边长为k的正方形。例如,矩形[4,6]可以切成边长最大为4的正方形。设maxLen为可以从矩形数组rectangles切分得到的最大正方形的边长。返回可以切出边长为maxLen的正方形的矩形数目。示例示例1:输入:rectangles=[[5,8],[3,9],[5,12],[16,5]] 输出:3 解释:能从每个矩形中切出的最大正方形边长分别是[5,3,5,5]。 最大正方形的边长为5,可以由3个矩形切分得到。 复制示例2:输入:rectangles=[[2,3],[3,7],[4,3],[3,7]] 输出:3 复制提示1<=rectangles.length<=1000rectangles[i].length==21<=li,wi<=109li!=wi出处链接:https://leetcode-cn.com/problems/number-of-rectangl

  • Python+Selenium与Chro

    开篇:最近有学员在学习Selenium时,使用firefox有问题,就想尝试chrome,而其使用crome时又遇到个各种问题..。特整理如下文章供遇到该问题的人参考整篇:如何解决呢?可参考如下..基础环境介绍: python3.5+selenium2.48(或以上)+chrome(版本65.0.3325.181  64位),其中chrome截至到2018年4月份,已经更新到最新版本了... 下载chromedriver.exe chromedriver.exe文件是调用chrome的驱动文件,因此该文件的版本要和chrome的版本必须兼容 chromedriver.exe下载地址如下:http://chromedriver.storage.googleapis.com/index.html 其中chromedriver.exe版本很多,针对chrome(版本65.0.3325.181)下载的是下图所示的版本(2.37) 点击上图中的2.37版本。  3.将下载的chromedriver.exe(2.37)放到(复制或移动)至chrome的安装目录下(一般chrome的安装路径如下

  • 系统架构师论文-改进Web服务器性能的有关技术

    改进Web服务器性能的有关技术【摘要】一个大中型的图书馆信息系统涉及到许多方面的技术与方案,本文着重讨论与Web服务器性能有关的一些内容。 本人有幸作为项目负责人之一参与了某大型图书馆数字化信息系统的设计和基于Web应用软件的开发工作。由于在数字化图书馆信息系统中流通着的大多是数字化的索引、文摘、全文、图像或音频视频等多媒体值息,対Web服务器性能有着较高的要求。 结合实际工程经验,本文将从硬件实现手段(缓存服务器、均衡负载设备、Web双机镜像、CPU和网卡的提升、网络带宽扩充)和软件实现手段(三层C/S软件结构设计、应用程序部署)等两个大方面论述如何提高Web服务大路的性能,以便使用户能够更快捷、高效、安全地使用应用系统。【正文】随着Intranet值息技术的发展,图书馆为了更好地发挥其图书流通、资料检索和学术交流的职能,图书馆的数字信息化工程也势在必行。某图书馆为了尽快地歩入世界先进图书馆的行列,已经启动了一部分的数字图书馆工程。 该数字图书馆工程主要包括対外信息Web发布系统,交互式检索网,后台馆藏信息管理系统、多媒体资料采集制作以及外VOD点播系统等。本人有幸作为项目负责人之一

  • 一文读懂神经网络(附解读&案例)

    “你的大脑并不产生思想。你的思想塑造了神经网络。”——DeepakChopra引文J.NocedalyS.Wright,“Numericaloptimization”,Springer,1999TLDR:J.Bullinaria,“LearningwithMomentum,ConjugateGradientLearning”,2015作为阐明神经网络背后的理论以及如何设计和实现神经网络系列文章的第一篇,本文力求向更广泛的受众群体详细、深入的介绍神经网络,使对神经网络的工作几乎一无所知,或相对熟悉但可能还没有完全掌握的读者都能从中获益。我将在这篇文章中介绍神经网络的动机和基础知识。在以后的文章中将对神经网络和深度学习的设计和优化进行更深入的讨论。本系列教程大部分基于哈佛和斯坦福的计算机科学及数据科学系的课程。本系列教程中所有(全连接)机器学习的代码都在我的神经网络Github存储库中,通过以下链接就可以找到。https://github.com/mrdragonbear/Neural-Networks 不论你之前对神经网络有多少了解,都希望你能愉快的看完这篇文章并学到一点知识。现在我们开

  • 【重磅】DeepMind发布最佳语音神经网络生成模型,与人类差距缩减50%以上

    【新智元导读】本文介绍的是WaveNet——一个原始音频波形深度模型。我们展示了,Wavenet能够生成模仿人类的语音,听起来要比现有最好的文本到语音转化系统更自然,将与人类表现的差距缩减了50%以上。在我们的展示中,相同的网络能被用于合成其他的音频信号,比如,音乐。在这里,我们提供了一些样本——自动生成的钢琴曲。会说话的机器让人能与机器对话是人机交互长期以来的一个梦想。近年来,随着深度神经网络的应用(比如,谷歌的语音搜索),计算机理解自然语音的能力取得了革命性的进展。但是,用计算机生成语音仍然大量地依赖于所谓的TTS(文本到语音)拼接技术,在这个过程中,首先要记录一个说话人的声音片段,并基于此构建超大型的数据库,随后,经过再次结合过程,形成完整的表达。这样一来,在不纪录一个完整的新数据库的情况下,要修饰声音就会变得很困难(比如,转化到不同的说话者,或者转化语音中的情感和语气)。这导致了对参数的TTS的大量需求,在这里面,所有生成数据所需要的信息都被存储到模型的参数中,并且,语音中的内容和个性可以通过模型的输入进行控制。但是,目前为止,参数的TTS听起来更多的是不自然的,而是合成的,至

  • 马斯克为什么比其他人学得更快?

    马斯克是独一无二的,但他的能力并不神奇,我们是可以通过学习掌控他的快速学习能力的。 埃隆·马斯克是如何在他45岁左右的时候在四个不同的领域(软件、能源、交通和航天)成功打造出4家数十亿美元的公司的呢? 有些人可能将马斯克的成功归功于他的投入和英雄式职业道德(他通常每周工作85小时)、他的现实扭曲力场能力,以及他令人难以置信的韧性。 但所有这些答案都无法让我感到满意。很多人都有这些特点。我想知道的是让马斯克取得如此骄人成绩的与众不同的特质是什么。 当我继续阅读许多关于马斯克的文章、视频和书籍时,我注意到很多人都忽略了马斯克之所以获得如此巨大的成功的一个重要原因。传统智慧认为,要想成为世界级的企业,我们应该只专注于一个领域。马斯克打破了这个规则。他的专业范围很广,从火箭科学、工程、建筑、隧道、物理和人工智能到太阳能和能源。 我把埃隆·马斯这样的人称为现代通才。现代通才的特点包括: 遵循5小时定律,即每周至少投入5小时时间用于学习。 在很多不同领域进行广泛的学习。 理解能将这些不同领域连接起来的更深层次的原理和思维模型。 将这些思维模型应用于他们的核心专业。 基于

  • 几种常见的微服务架构方案简述——ZeroC IceGrid、Spring Cloud、基于消息队列

    2017-07-26 http://www.broadview.com.cn/article/348 微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果。虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合项目的特点来选择某个合适的微服务架构平台,以此稳妥地实施项目的微服务化改造或开发进程。本文选自《架构解密:从分布式到微服务》一书,了解本书详情请点击阅读原文。 本文盘点了四种常用的微服务架构方案,分别是ZeroCIceGrid、SpringCloud、基于消息队列与DockerSwarm 1ZeroCIceGrid微服务架构 ZeroCIceGrid作为一种微服务架构,它基于RPC框架发展而来,具有良好的性能与分布式能力,如下所示是它的整体示意图。 IceGrid具备微服务架构的如下明显特征。 首先,微服务架构需要一个集中的服务注册中心,以及某种服务发现机制。IceGrid服务注册采用XML文件来定义,其服务注册中心就是Ice Registry,这是一个独立的进程,并且提供了HA高可用机

  • Python tkinter之控件方法bind的使用

      1.调用规则:窗体对象.bind(事件类型,回调函数)   2.<Button-1>表示鼠标左键单击,其中的1换成3表示右键被单击,为2的时候表示鼠标中键   t=Label(root,text='标签')   t.bind(<Button-1>,函数名) #鼠标左键点击时调用函数 例:   1fromtkinterimport* 2tk=Tk()#父窗口类实例 3tk.title("bind用法实例")#窗口标题 4 5defLoveChina(event):#定义回调函数 6x1=Label(tk,text='我爱你中国!',background='pink') 7x1.pack() 8x2=Button(tk,text='单击左键试试')#定义一个按钮 9x2.bind('<Button-1>',LoveChina)#单击鼠标左键,绑定LoveChina()函数 10x2.pack() 11 12tk.mainloop()复制   运行结果如下:   3.<KeyP

  • 在MAC下切换GCC编译器的办法(MacPorts)

      在MAC下切换GCC编译器的办法(MacPorts)这里的办法是通过port命令选项实现的,所以需要先安装MacPorts,具体的安装步骤就不多说了,切换不同版本gcc的命令如下:执行:$sudoportselect--listgcc显示:Password:Availableversionsforgcc:       gcc42       llvm-gcc42       mp-gcc45(active)       none选择llvm-gcc42作为编译器,执行:$sudoportselect--setgccllvm-gcc42显示:Selecting'llvm-gcc42'for'gcc'succeeded.'llvm-gcc42'isnowactive.以前版本的MacPorts是通过

  • 最前沿物理学——宇宙只是幻象!

    http://www.fosss.org/Book/ShiJie/Index.html 我们应该修改物理学教材了!”2005年8月,在德国康斯坦茨湖畔一所大学的最高建筑的顶楼,美国物理学家克里斯托弗·福熙(ChristopherFuch)用这样一个大胆的提议作为其组织的系列研讨会的开场白。康斯坦茨大学出资邀请了50多位美国、加拿大、英国以及意大利、法国和澳大利亚的理论学家和哲学家来参加这一为期一周的会议。他们都是微观物质运动规律研究领域的顶尖专家。其中的一些学者极具威望,但他们也毫不犹豫的推翻了自己曾经持有的观点。作为门外汉,我们无法深入理解研讨会期间专家们彻夜争论的深奥的数学问题。不过,他们讨论都是围绕着这样一个观点展开的:物理学为我们描绘的世界也许并不是真实的物质世界,而很可能只是一个巨大的幻象! 这让我们一下子坠入了云雾之中。在我们一贯的观念中,物理学的目的,正如字典里所清晰定义的那样,不就是“研究物质的属性”吗?物理学家们的确是通过对如同康斯坦茨湖畔的石头一般真实存在的物体的考察和研究,才得以提出了现有的物理学理论。那么凭什么说物理学描绘的只是一个幻象呢?很简单,这是因为物理学

  • 算术表达式的后缀表示

    算术表达式的后缀表示 规则 中缀表达式a+b*c+(d*e+f)*g,其转换成后缀表达式则为abc*+de*f +g*+。 转换过程需要用到栈,具体过程如下: 1)如果遇到操作数,我们就直接将其输出。 2)如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。 3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。 4)如果遇到任何其他的操作符,如(“+”,“*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到")"的情况下我们才弹出"(",其他情况我们都不会弹出"("。 5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。 实例 规则很多,还是用实例比较容易说清楚整个过程。以上面的转换为例,输入为a+b*c+(d*e+f)*g,处理过程如下: 1)首先读到a,直接输出。 2)读到“+”,将其放入到栈中。 3)读到b,直接输出。 此时栈和输出的情况如下:   4)读到“*”,因为栈顶元素"+"优

  • 在UE5中用KawaiiPhysics插件进行衣服物理模拟

    参考链接 KawaiiPhysics插件Github下载链接 英文ReadMe 参数对照表 B站教程第一集 第二集 【Youtube】Ue4Kawaiiphysicsexplained 【Youtube】UE4ClothSimulationTutorial 【Youtube】PonytailphysicsTutorialinUnrealEngine4 【Youtube】SettingupPhysicsonaCharacterinUnrealEngine 【Youtube】HowtoAddBoobPhysicsinUnrealEngine5Tutorial-MasteringJigglePhysics 正文 开始研究如何在UE5里让MMD人物的衣服动起来了,当然也包括尾巴耳朵等如何运动。 首先如果是MMD模型的话,一种普遍方法是使用KawaiiPhysics插件根据模型绑定好的骨骼进行伪物理仿真(更加Kawaii的物理仿真?)。该插件是Github开源的,而且UE5版本的也出来了,英文版README可以戳这里,参数讲解对照表可以戳这里。B站上有讲解如何使用的教程(很详细),一共有两集(第

  • 友链

    友链 听说大家都比较喜欢挂个友链?那我也来挂一个吧。 同校的聚聚们 上上届的聚聚们 boshi hzy Rayment litble 上届的聚聚们 CYJian Kewth 同届的聚聚们 Daniel_yuan Imakf fhs_lzx lightmain 春日野悠 TUIFEI_oier zzt ovor 下届的聚聚们 WanRP WanRP2 zhy12138 zhy2 Iris 下下下届的聚聚们 Qiuly LoserMoonlights

  • C#简易计算器设计 计应192(西)—第一组—李俊

    计应192(西)—第一组—李俊 C#简易计算器设计 PSP图: 程序设计思路:项目中使用了两个控件:数子按钮控件和操作符控件。计算器项目中处理共享事件的处理过程,其中首先通过sendenr对象获取按钮的Text属性,然后通过按钮Text属性可以可以判断是哪个Button被单击,并执行相应操作。 需求分析:作为一名小学生,我希望能够非常方便的进行计算,以便于我的妈妈不会吵我。 作为小学生的家长,我希望有一款算题卡,以便于我的儿子能够进行更好的学习。   具体设计: 具体编码: privatevoidForm1_Load(objectsender,EventArgse){//计算计算器窗口居中intleft=Screen.PrimaryScreen.Bounds.Width/2-this.Width/2;inttop=Screen.PrimaryScreen.Bounds.Height/2-this.Height/2;this.Location=newPoint(left,top);}doublenum1=0;doublenum2=0;booliskey=false;//1pr

  • 在vue-cli@3.X中配置代理解决开发环境的跨域问题

    我们在前后端分离开发时,不得不面对跨域问题,对于跨域,可以在前端这样处理配置. 1、在 vue.config.js文件中配置 proxy 属性,将API请求代理到API服务器上,设置 devServer.proxy 例如:App.vue复制 <template> <divid="app"> <!--<divid="nav"> <router-linkto="/">Home</router-link>| <router-linkto="/about">About</router-link> </div>--> <!--遍历数组item:数组元素index:元素对应下标--> <divv-for="(item,index)indata.data1":key="'0'+index"> {{item}} {{index}} </div> <!--遍历对象item:对象值key:对象键index:对象值对

  • addEventListener和attachEvent以及element.onclick的区别

    attachEvent是ie添加事件处理程序,接收两个参数,其中事件类型名称要加"on", 可以添加多个事件处理程序,按照添加顺序相反的顺序触发; addEventListener是给非ie添加事件处理程序,接收三个参数,第一个是事件名,不需要加“on”, 第二个是绑定的函数,第三个参数是一个布尔值,是事件的方式,意思是是否使用useCatch方式, 如果是false,就使用传统的冒泡方式,如果为true,就在捕获阶段调用事件处理程序。 addEventListener可以添加多个事件处理程序,按照添加顺序触发 二者有个本质上的区别,attachEvent的事件处理程序会在全局作用域中运行,this等于window对象, 而addEventLinstener添加的事件处理程序是在其依附的元素的作用域中运行的,this等于绑定元素对象。 既然他们的this指向不同,那怎么才能实现相同的this指向呢? 如果想要实现this关键字指向相同的话,要用Function的apply或者call方法。示例代码如下: functionbind(el,fn,type){ var_fn=functio

  • ES6 Proxy 在 Immer 中的妙用

    写在前面 Immer结合Copy-on-write机制与ES6Proxy特性,提供了一种异常简洁的不可变数据操作方式: constmyStructure={ a:[1,2,3], b:0 }; constcopy=produce(myStructure,()=>{ //nothingstodo }); constmodified=produce(myStructure,myStructure=>{ myStructure.a.push(4); myStructure.b++; }); copy===myStructure//true modified!==myStructure//true JSON.stringify(modified)===JSON.stringify({a:[1,2,3,4],b:1})//true JSON.stringify(myStructure)===JSON.stringify({a:[1,2,3],b:0})//true 复制 这究竟是怎么做到的呢?   一.目标 Immer只有一个核心API: produce(currentS

  • xml与DataSet互转

    //将DataSet转换为xml字符串  publicstaticstringConvertDataSetToXMLFile(DataSetxmlDS,Encodingencoding)  {      MemoryStreamstream=null; XmlTextWriterwriter=null;      stringresult="<result>-3</result>";      try      {          stream=newMemoryStream();         

  • 行业领先的界面控件包DevExpress v21.2.7全新发布

    DevExpress UniversalSubscription拥有.NET开发需要的所有平台控件,包含600多个UI控件、报表平台、DevExpressDashboardeXpressApp框架、适用于VisualStudio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpressUniversal2021年11月正式发布了v21.2,该版本拥有众多新产品和数十个具有高影响力的功能,可为桌面、Web和移动应用提供直观的解决方案,全面解决各种使用场景问题。 DevExpressv21.2.7官方正式版下载 具体更新内容如下: 此列表包括v21.2.7中已解决的所有问题。 WinForms T1074232 – PrintPreview中的"SignatureOptions"对话框不使用上下文菜单的皮肤 T1066907 – 如果RTL设置为WindowsFormsSettings,参数面板中的参数说明不会显示在右侧 T1081948 – DataSourceWizard-"Couldnotlo

  • Python的基础知识(1)

    ⼀一.格式化输出    现在有以下需求,让⽤用户输入name,age,job,hobby然后输出如下所⽰示:------------infoofAlexLi----------Name :AlexLiAge  :22job  :TeacherHobbie:girl-------------end----------------你怎么实现呢?你会发现,⽤用字符拼接的⽅方式还难实现这种格式的输出,所以⼀一起来学⼀一下新姿势只需要把要打印的格式先准备好,由于⾥里里⾯面的⼀一些信息是需要⽤用户输⼊入的,你没办法预设知道,因此可以先放置个占位符,再把字符串串⾥里里的占位符与外部的变量量做个映射关系就好啦name=input("Name:")age=input("Age:")job=input("Job:")hobby=input("Hobbie:")info='''------------infoof%s-----------#这⾥里里的每个%s就是⼀一个占位符,本⾏行行的代表后⾯面拓拓号⾥里里的nameN

  • 关于使用d4rl时出现gym.error.UnregisteredEnv: No registered env with id: hopper-medium-v2的问题的解决方案

    在安装d4rl时,出现了这样的问题: gym.error.UnregisteredEnv:Noregisteredenvwithid:hopper-medium-v2   后经检查,是因为安装d4rl时,报错: Nomodulenamed'mjrl'Warning:Flowfailedtoimport.SettheenvironmentvariableD4RL_SUPPRESS_IMPORT_ERROR=1tosuppressthismessage. 发现是因为我们在安装环境时,未安装”mirl“引起的,因此通过下面的指令解决: pipinstallgit+https://github.com/aravindr93/mjrl@master#egg=mjrl   即可。  

相关推荐

推荐阅读