当前位置: 首页 > news >正文

通过这个简单的技巧让我们的 JavaScript 代码变得异常快

通过这个简单的技巧让我们的 JavaScript 代码变得异常快

秘诀:了解JavaScript 虚拟机(VM)的内部工作原理。

首先,我们来谈谈像 V8 这样的JavaScript 虚拟机(VM)。可以把它想象成我们的操作的大脑 —— 它将我们简洁的代码变成计算机可以理解和执行的东西。

好的、坏的代码

让我们深入研究一些代码示例,看看好的、坏的和执行快的代码。

缓慢的 JavaScript 示例:

function addProperty(obj, propName, value) {obj[propName] = value; // 这会改变对象的形状
}
const responseObject = { user1: 1, user2: 2 };
addProperty(responseObject, 'user3', 3); // 添加新的属性

是什么让它变慢?

形状更改:每次调用 addProperty 函数时,都会向对象添加一个新属性。这会改变对象的“形状”,即它包含的键变了,这反过来又会颠覆 JavaScript 引擎的优化。

添加或删除属性时,引擎可能必须丢弃以前的优化信息并重新开始。这种“形状变化”就是操作缓慢的原因。

快速 JavaScript 示例:

function  createObject ( a, b, c ) { 
// 对象的形状是固定的并且VM可以预测return { a, b, c }; 
} 
const dataObject = createObject ( 212,2344,43545);

是什么让它如此快速?

可预测的形状:

该对象是使用一组固定的属性创建的。创建后没有任何变化,更容易引擎优化。

隐藏类重用:

由于每次调用 createObject 时对象的形状都是一致的,因此 JavaScript 引擎可以重用为此形状创建的隐藏类。这种重用允许非常快速的属性访问,因为引擎确切地知道每个属性在内存中的位置。

为什么对象形状很重要:

当我们访问对象的属性时,引擎不想搜索所有属性来找到它。相反,它想要直接访问该属性在内存中的位置。如果对象的形状已知,引擎可以记住每个属性所在的位置(这称为“内联缓存”)。但是,如果形状发生变化(如上面的缓慢示例),引擎必须“重新学习”属性位置,这要慢得多。

为了获得最佳性能,特别是在频繁访问属性的代码关键部分,最好:

  • 创建对象时初始化所有属性:即使某些属性最初可能未定义。
  • 避免添加或删除属性:这可以保持对象的形状稳定。
  • 尽可能重用对象形状:创建始终生成具有相同属性集的对象的工厂函数。

通过遵循这些实践,我们可以帮助 JavaScript 引擎优化我们的代码,从而加快执行速度。

常见的用例

当处理来自外部源的对象(例如 API 响应或 DOM 元素)时,在使用这些对象之前将它们规范化为一致的形状对性能有益。这允许 JavaScript 引擎优化对这些对象的访问,因为形状(对象内的所有键)是可预测的并且不会改变。当我们频繁读取对象时,这种做法尤其有价值。

让我们来看下面两个常见的示例

通过 API 获取用户信息

慢速版本:

在慢速版本中,属性被一一添加到对象中,这可能会导致 JavaScript 引擎由于形状变化而取消对对象的访问优化。

function fetchUserProfile(url) {fetch(url).then(response => response.json()).then(user => {const userProfile = {};if (user.name) {userProfile.name = user.name;}if (user.age) {userProfile.age = user.age;}if (user.email) {userProfile.email = user.email;}// ...处理更多的属性return userProfile;});
}
快速版本:

在快速版本中,我们从一开始就创建一个具有已知形状的对象,即使某些属性可能未定义。这种一致性允许 JavaScript 引擎优化属性访问。

function fetchUserProfile(url) {return fetch(url).then(response => response.json()).then(user => {// 先定义对象中包含的所有的属性const userProfile = {name: user.name || undefined,age: user.age || undefined,email: user.email || undefined,// ... 初始化更多属性};return userProfile;});
}

在快速版本中,即使用户对象不具有我们分配给 userProfile 的所有属性,我们仍然使用其相应的值或未定义的值来定义我们期望的所有键。这样,userProfile 的形状保持一致,这有利于稍后访问其属性时的性能。

这种做法对于性能关键型应用程序至关重要,优化可以极大地提高执行速度。

如果上面的例子让我们想起了什么,那是因为这个模式看起来像工厂模式,它遵循类似于工厂函数的原则,通过创建一个具有预定义形状的对象,但它并不完全是这样。在 JavaScript 中,工厂模式通常涉及构造并返回新对象的专用函数。当创建过程复杂或需要执行一些额外的设置工作时,工厂函数特别有用。

使用工厂模式

在给定的快速示例中,我们看到了一种创建具有一致形状的对象的方法。为了使其与工厂模式更加一致,我们可以将对象创建封装在专用函数中,如下所示:

function createUserProfile(name, age, email) {
// 通过工厂模式创建对象return {name: name || undefined,age: age || undefined,email: email || undefined,// ...};}function fetchUserProfile(url) {return fetch(url).then(response => response.json()).then(user => {return createUserProfile(user.name, user.age, user.email);});
}

在这个版本中,createUserProfile 是一个工厂函数,总是创建具有相同形状的对象,这有利于优化。fetchUserProfile 函数使用此工厂创建一个新的 userProfile 对象。

使用DOM

现在让我们讨论另一个常见的示例,在使用 DOM 时,我们经常需要从 HTML 元素读取信息,然后在应用程序中使用这些数据。保持对象形状一致对于性能非常重要,尤其是当我们重复执行这些操作时。

下面的示例演示了对象形状发生变化的慢速代码示例,以及对象形状可预测且一致的快速方法。

慢速代码示例
function getUserData() {const userObject = {};const userName = document.querySelector('#input-name');if (nameElement) {userObject.name = nameElement.textContent;}const userAge = document.querySelector('#input-age');if (ageElement) {userObject.age = parseInt(ageElement.textContent);}// 每次调用此函数时,它可能会也可能不会添加新属性// 这可能会导致对象形状发生变化return userObject;
}
快速代码示例
function createUserData(name = undefined, age = undefined) {
// 始终返回具有相同形状的对象的工厂函数return { name, age };
}
function getUserData() {const userName = document.querySelector('#input-name');const userAge = parseInt(document.querySelector('#input-age')?.textContent);// 无论元素是否存在,对象的形状都是一致的return createUserData(userName?.textContent, Number.isNaN(userAge) ? undefined : userAge);
}

在上面代码中,createUserData 工厂函数确保返回的对象始终具有相同的形状,这有利于 JavaScript 引擎的优化过程。getUserData 函数使用此工厂函数来创建配置文件数据对象,并通过提供 undefined 作为默认值来处理丢失的 DOM 元素,从而维护对象的形状。

通过使用可选链接运算符 (?.) 和空合并运算符 (??),我们可以进一步细化该函数以处理 DOM 元素可能不存在的情况:

function getUserData() {const name = document.querySelector('#input-name')?.textContent ?? undefined;const ageText = document.querySelector('#input-age')?.textContent ?? undefined;const age = ageText ? parseInt(ageText) : undefined;// 对象的形状一致return createUserData(name, age);
}

这种方法可以确保对象的形状保持不变,即使在 DOM 中找不到某些元素,这在动态 Web 应用程序中很常见,因为有时元素尚未渲染或元素渲染顺序不正确。

相关文章:

通过这个简单的技巧让我们的 JavaScript 代码变得异常快

通过这个简单的技巧让我们的 JavaScript 代码变得异常快 秘诀:了解JavaScript 虚拟机(VM)的内部工作原理。 首先,我们来谈谈像 V8 这样的JavaScript 虚拟机(VM)。可以把它想象成我们的操作的大脑 —— 它将我们简洁的代码变成计算机可以理解和执行的东…...

vue怎么实现国际化? vue-i18n 插件实现国际化,支持切换不同语言

依赖的文档开始 | Vue I18n 一、安装 npm install vue-i18n 如果在一个模块系统中使用它,你必须通过 Vue.use() 明确地安装 vue-i18n: import Vue from vue import VueI18n from vue-i18nVue.use(VueI18n)二、使用 在 src 下创建 lang 文件夹 1.准…...

rabbit MQ的延迟队列处理模型示例(基于SpringBoot延时插件实现)

rabbitMQ安装插件rabbitmq-delayed-message-exchange 交换机由此type 表示组件安装成功 生产者发送消息时设置延迟值 消息在交换机滞纳至指定延迟后,进入队列,被消费者消费。 组件注解类: package com.esint.configs;import org.springfra…...

虽不想承认,但这就是CSGO游戏搬砖行业的现状

CSGO搬砖日常出货更新 其实整个搬砖市场,现在已经变得乌烟瘴气,散发着“恶臭”。我个人非常鄙视那些虚有其表,大小通吃的做法,那些甚至连搬砖数据都看不懂的人,也出来吹嘘着“实力强大,经验丰富”。这个世界…...

想问问各位大佬,网络安全这个专业普通人学习会有前景吗?

网络安全是一个非常广泛的领域,涉及到许多不同的岗位。这些岗位包括安全服务、安全运维、渗透测试、web安全、安全开发和安全售前等。每个岗位都有自己的要求和特点,您可以根据自己的兴趣和能力来选择最适合您的岗位。 渗透测试/Web安全工程师主要负责模…...

uniapp IOS从打包到上架流程(详细简单) 原创

​ 1.登入苹果开发者网站,打开App Store Connect ​ 2.新App的创建 点击我的App可以进入App管理界面,在右上角点击➕新建App 即可创建新的App,如下图: ​ 3.app基本信息填写 新建完App后,需要填写App的基本信息&…...

React Native项目接入Sentry指南

本文主要介绍React Native项目接入Sentry流程,以及遇到的一些注意点,方便大家去解决和处理,如果在接入过程中,遇到任何问题可以在评论区留言,我将根据自己的接入经验给出一些解决方案和建议。 1, 安装sentry sdk 我们可以在项目中执行如下命令来安装sentry,命令如下: …...

首批!创邻科技入选《图数据库金融应用场景优秀案例》

11月11日,“全球金融科技中心网络年会”在第三届全球金融科技大会暨第五届成方金融科技论坛上成功在京举办。会上,北京前沿金融监管科技研究院发布了基于国际标准组织——国际关联数据基准委员会(LDBC)的《图数据库金融应用场景优…...

WPF树形控件TreeView使用介绍

WPF 中的 TreeView 控件用于显示层次结构数据。它是由可展开和可折叠的 TreeViewItem 节点组成的&#xff0c;这些节点可以无限嵌套以表示数据的层次。 TreeView 基本用法 例如实现下图的效果&#xff1a; xaml代码如下&#xff1a; <Window x:Class"TreeView01.Mai…...

Django 模型和Admin站点管理(三)

一、定义模型 &#xff08;1&#xff09; 创建模型类&#xff0c;必须要继承自 models.Model from django.db import models# Create your models here. #设计数据库 #创建模型 class UserModel(models.Model):namemodels.CharField(max_length30) #对应于SQL name varchar(30…...

JVMj之console Java监视与管理控制台

jconsole Java监视与管理控制台 1、jconsole介绍 jconsole (java monitoring and management console)是一款基于JMX (Java Management Extensions) 的可视化监视和管理工具。 2、启动jconsole 1、在linux和windwos下通过jconsole启动即可。 2、然后会自动搜索本机运行的…...

Android进阶知识:ANR的定位与解决

1、前言 ANR对于Android开发者来说一定不会陌生&#xff0c;从刚开始学习Android时的一不注意就ANR&#xff0c;到后来知道主线程不能进行耗时操作注意到这点后&#xff0c;程序出现ANR的情况就大大减少了&#xff0c;甚至于消失了。那么真的是只要在主线程做耗时操作就会产生…...

基于SSM的老年公寓信息管理(有报告)。Javaee项目

演示视频&#xff1a; 基于SSM的老年公寓信息管理&#xff08;有报告&#xff09;。Javaee项目 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc …...

阿里云 ACK 新升级,打造智算时代的现代化应用平台

云布道师 今天&#xff0c;能想到的或是想不到的领域&#xff0c;对容器和 Kubernetes 的需求都居高不减&#xff0c;使这项技术正在真正走向无处不在。 在 2023 云栖大会上&#xff0c;阿里云云原生产品线容器服务负责人易立关于容器服务 ACK 在本届亚运会上应用的介绍&#…...

CodeWhisperer 一款好玩的 AI 插件

忙里抽闲&#xff0c;今天试了试 CodeWhisperer 这款插件&#xff0c;我是在 IDEA 中做的测试&#xff0c;下面是我的一些使用感想&#xff1a; 安装 CodeWhisperer 插件&#xff1a;在 IntelliJ IDEA 中&#xff0c;可以通过插件管理器安装 CodeWhisperer 插件&#xff0c;然…...

大模型生态新篇章:以AI Agent为引,助企业创新应用落地

文 | 智能相对论 作者 | 沈浪 以聊天机器人、虚拟助手、智能客服等为代表的对话式人工智能 (Conversational AI Agents ) 在具体服务场景中的应用已经十分普遍。今年以来&#xff0c;随着大模型技术的爆发与加持&#xff0c;对话式AI被市场赋予了更高的期望。 “所有行业都值…...

什么是网络安全工程师,你想知道的都在这里!

随着互联网的发展和大数据时代的到来&#xff0c;网络已经日渐深入到我们生活、工作中的方方面面&#xff0c;社会信息化和信息网络化&#xff0c;突破了应用信息在时间和空间上的障碍&#xff0c;使信息的价值不断提高。但是&#xff0c;与此同时&#xff0c;网页篡改、计算机…...

uniapp开发的微信小程序进行代码质量控制,分包+压缩js+组件按需注入等

小程序代码分包的操作请看另外一篇文章&#xff1a;uniapp分包优化&#xff0c;包括分包路由跳转规则-CSDN博客 JS文件压缩&#xff1a;在工具「详情」-「本地设置」中开启「上传代码时自动压缩脚本文件」的设置 代码包&#xff1a;组件 > 启用组件按需注入解决办法 在小程…...

GD32替换STM32使用HAL库开发问题

GD32HAL库开发问题 1can初始化进入error handle2发送邮箱不能按照填写顺序发送3 GD32修改代码被stm32cudemx覆盖问题 1can初始化进入error handle HAL库的HAL_CAN_Init中&#xff0c;hcan->Instance->MSR寄存器无法清零&#xff0c;STM32先清零&#xff0c;再退出睡眠模…...

PE文件分析

用PEView及Python程序分析压缩包中的lab01.exe和lab01.dll两个PE文件&#xff0c;关键步骤给出截图&#xff1a; 分析两个文件的文件头中的各部分&#xff1b; 2.找出时间戳&#xff0c;计算出两个文件的生成时间&#xff1b; 注&#xff1a;时间戳为从1970年1月1日0时0分0秒到…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...