《每天十分钟》-红宝书第4版-性能

在使用垃圾回收的编程环境中,开发者通常无须关心内存管理。不过,JavaScript 运行在一个内存管理与垃圾回收都很特殊的环境。分配给浏览器的内存通常比分配给桌面软件的要少很多,分配给移动浏览器的就更少了。这更多出于安全考虑而不是别的,就是为了避免运行大量 JavaScript 的网页耗尽系统内存而导致操作系统崩溃。这个内存限制不仅影响变量分配,也影响调用栈以及能够同时在一个线程中执行的语句数量。

很大一部分前端开发自从有了chrome之后,很少再关心性能的问题了,除非写的代码特别不好或者业务对性能要求特别高(比如交易类系统),针对有性能要求的场景写写这部分内容。

现代垃圾回收程序会基于对 JavaScript 运行时环境的探测来决定何时运行。探测机制因引擎而异,
但基本上都是根据已分配对象的大小和数量来判断的。比如,根据 V8 团队 2016 年的一篇博文的说法:
“在一次完整的垃圾回收之后,V8 的堆增长策略会根据活跃对象的数量外加一些余量来确定何时再次垃
圾回收。”

  1. 手动赋null
function createPerson(name){ let localPerson = new Object(); localPerson.name = name; return localPerson; 
} 
let globalPerson = createPerson("Nicholas"); 
// 解除 globalPerson 对值的引用
globalPerson = null;

在上面的代码中,变量 globalPerson 保存着 createPerson()函数调用返回的值。在 createPerson()
内部,localPerson 创建了一个对象并给它添加了一个 name 属性。然后,localPerson 作为函数值
被返回,并被赋值给 globalPerson。localPerson 在 createPerson()执行完成超出上下文后会自
动被解除引用,不需要显式处理。但 globalPerson 是一个全局变量,应该在不再需要时手动解除其
引用,最后一行就是这么做的。
不过要注意,解除对一个值的引用并不会自动导致相关内存被回收。解除引用的关键在于确保相关
的值已经不在上下文里了,因此它在下次垃圾回收时会被回收。

  1. 通过 const 和 let 声明提升性能
    相比于使用 var,使用这两个新关键字可能会更早地让垃圾回收程序介入,尽早回收应该回收的内存。在块作用域比函数作用域更早终止的情况下,这就有可能发生。

  2. 隐藏类和删除操作

function Article() { this.title = 'Inauguration Ceremony Features Kazoo Band'; 
} 
let a1 = new Article(); 
let a2 = new Article();

V8 会在后台配置,让这两个类实例共享相同的隐藏类,因为这两个实例共享同一个构造函数和原型。假设之后又添加了下面这行代码:

a2.author = 'Jake'; 

此时两个 Article 实例就会对应两个不同的隐藏类。根据这种操作的频率和隐藏类的大小,这有可能对性能产生明显影响。当然,解决方案就是避免 JavaScript 的“先创建再补充”(ready-fire-aim)式的动态属性赋值,并在构造函数中一次性声明所有属性,如下所示:

function Article(opt_author) { this.title = 'Inauguration Ceremony Features Kazoo Band'; this.author = opt_author; 
} 
let a1 = new Article(); 
let a2 = new Article('Jake');

这样,两个实例基本上就一样了(不考虑 hasOwnProperty 的返回值),因此可以共享一个隐藏类,
从而带来潜在的性能提升。不过要记住,使用 delete 关键字会导致生成相同的隐藏类片段。看一下这
个例子:

function Article() { this.title = 'Inauguration Ceremony Features Kazoo Band'; this.author = 'Jake'; 
} 
let a1 = new Article(); 
let a2 = new Article(); 
delete a1.author;

在代码结束后,即使两个实例使用了同一个构造函数,它们也不再共享一个隐藏类。动态删除属性与动态添加属性导致的后果一样。最佳实践是把不想要的属性设置为 null。这样可以保持隐藏类不变和继续共享,同时也能达到删除引用值供垃圾回收程序回收的效果。比如:

function Article() { this.title = 'Inauguration Ceremony Features Kazoo Band'; this.author = 'Jake'; 
} 
let a1 = new Article(); 
let a2 = new Article(); 
a1.author = null;
  1. 内存泄漏
    全局变量
function setName() { name = 'Jake'; 
}

定时器

let name = 'Jake'; 
setInterval(() => { console.log(name); 
}, 100);

闭包

let outer = function() { let name = 'Jake'; return function() { return name; }; 
};
  1. 静态分配与对象池
    在写方法的时候一定纠结过到底是引用传参还是数值传参,不同方式会对内存有影响,比如
function addVector(a, b) { let resultant = new Vector(); resultant.x = a.x + b.x; resultant.y = a.y + b.y; return resultant; 
}

调用这个函数时,会在堆上创建一个新对象,然后修改它,最后再把它返回给调用者。如果这个
矢量对象的生命周期很短,那么它会很快失去所有对它的引用,成为可以被回收的值。假如这个矢量
加法函数频繁被调用,那么垃圾回收调度程序会发现这里对象更替的速度很快,从而会更频繁地安排
垃圾回收。
该问题的解决方案是不要动态创建矢量对象,比如可以修改上面的函数,让它使用一个已有的矢量对象:

function addVector(a, b, resultant) { resultant.x = a.x + b.x; resultant.y = a.y + b.y; return resultant; 
}

使用对象池按需分配

let vectorList = new Array(100); 
let vector = new Vector(); 
vectorList.push(vector); 

由于 JavaScript 数组的大小是动态可变的,引擎会删除大小为 100 的数组,再创建一个新的大小为
200 的数组。垃圾回收程序会看到这个删除操作,说不定因此很快就会跑来收一次垃圾。要避免这种动态分配操作,可以在初始化时就创建一个大小够用的数组,从而避免上述先删除再创建的操作。不过,必须事先想好这个数组有多大

最近因为孩子教育问题十分头大,灌自己一碗鸡汤,先干为敬

不能把小孩子的精神世界变成单纯学习知识。如果我们力求使儿童的全部精神力量都专注到功课上去,他的生活就会变得不堪忍受。他不仅应该是一个学生,而且首先应该是一个有多方面兴趣、要求和愿望的人。——苏霍姆林斯基

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/594428.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于VS2019的C++动态链接库DLL生成与调用

一、理论知识及实践经验 实验注意事项及部分程序编写规范(部分源自ChatGPT-3.5): Ⅰ __declspec(dllexport)和__declspec(dllimport)是用于在C中定义动态链接库(DLL)的关键字。在编写动态链接库时,__declsp…

AIGC年度回顾!2024向量数据库是否还是AI发展方向之一?

引言 2023 年,是 AI 技术大爆发的一年,从年初到年末,全球关心技术发展的人们见证了一次次的 AI 技术升级,也逐步加深着对 AGI 发展的畅想。而伴随着生成式人工智能的飞速发展,向量数据库以其独特的技术优势逐渐崭露头角…

修复移动硬盘显示盘符但打不开问题

问题: 移动硬盘显示盘符,但无法打开。点击属性不显示磁盘使用信息。 分析解决: 这是由于硬盘存在损坏导致的,可以通过系统自带的磁盘检查修复解决,而无需额外工具。 假设损坏的盘符是E,在命令行运行以下命令…

流媒体学习之路(WebRTC)——Pacer与GCC(5)

流媒体学习之路(WebRTC)——Pacer与GCC(5) —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标:可以让大家熟悉各类Qos能力、带宽估计能力,提供每个环节关键参数调节接口并实现一个json全…

数据库-MySQL 启动方式

以管理员身份运行命令行 或者Shell net start //查看所有服务 net start MYSQL80 //启动服务 net stop MYSQL80 //停止服务完整安装MySQL社区版本的 会有这个 启动服务 停止服务 重启服务

C# 进阶语法,Linq入门到详解

什么是Linq LINQ (Language Integrated Query) 即语言集成查询–用来作查询一些操作类库主要负责对象的查询。 1、LINQ to Objects 主要负责对象的查询 2、LINQ to XML 主要负责XML的查询。 3、LINQ to ADO.NET 主要负责数据库的查询。 linq核心就是对数据源的操作 学linq另外…

Linux C获取终端尺寸

可用ioctl读取winsize结构。 结构定义 struct winsize {unsigned short ws_row;unsigned short ws_col;unsigned short ws_xpixel;unsigned short ws_ypixel;};测试代码 #include <stdio.h> #include <sys/ioctl.h> #include <unistd.h> #include <sign…

20240101 SQL基础50题打卡

20240101 SQL基础50题打卡 1211. 查询结果的质量和占比 Queries 表&#xff1a; ---------------------- | Column Name | Type | ---------------------- | query_name | varchar | | result | varchar | | position | int | | rating | int | ---…

1. Spring Boot 自动配置 Mybatis

1. Spring Boot 自动配置 Mybatis 自动配置 约定大于配置&#xff0c;缺省的配置看这个注解 EnableConfigurationProperties({MybatisProperties.class})。这个注解中引入了MybatisProperties类&#xff0c;包含了一些默认的配置。 Configuration ConditionalOnClass({SqlSe…

RabbitMQ消息队列常见面试题

前言 本文是对RabbitMQ的常见面试题问答的总结&#xff0c;同时也是一个RabbitMQ的入门学习&#xff0c;还没了解的同学快来认识一下。 1.RabbitMQ消息模型有哪些&#xff1f; &#xff08;1&#xff09;HelloWorld 模型(点对点模型) 一个生产者一个消息队列一个消费者 &a…

三种 SqlSession

三种 SqlSession ​SqlSession​ 是一个接口&#xff0c;并且里面包含了许多 CRUD 操作数据库等方法。 ​SqlSession​​ 它有三个实现类&#xff0c;分别是 SqlSessionManager​​ 、DefaultSqlSession​​ 和 SqlSessionTemplate​​&#xff0c;其中 DefaultSqlSession​​…

使用函数的选择法排序

本题要求实现一个用选择法对整数数组进行简单排序的函数。 函数接口定义&#xff1a; void sort( int a[], int n ); 其中a是待排序的数组&#xff0c;n是数组a中元素的个数。该函数用选择法将数组a中的元素按升序排列&#xff0c;结果仍然在数组a中。 裁判测试程序样例&am…

15、Kubernetes核心技术 - 探针

目录 一、概述 二、探针类型 2.1、就绪探针&#xff08;Readiness Probe&#xff09; 2.2、存活探针&#xff08;Liveness Probe&#xff09; 三、探针探测方法 3.1、exec 3.2、httpGet 3.3、tcpSocket 四、探针配置项 五、探针使用 5.1、就绪探针&#xff08;Readin…

自定义异常面试及答案(2024)

1、为什么要使用自定义异常&#xff1f; 使用自定义异常&#xff08;Custom Exceptions&#xff09;在程序设计中是一个良好的实践&#xff0c;它有几个重要的好处&#xff1a; 提高代码可读性&#xff1a; 自定义异常的名称如果能清晰表达出异常的情况&#xff0c;那么阅读代…

springboot整合ftp服务器实现上传与下载

springboot整合ftp服务器实现上传与下载 1. 添加依赖 在项目的pom.xml文件中添加spring-boot-starter-web和commons-net的依赖&#xff1a; <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-…

Java框架相关高频面试题

一&#xff0c;Spring 1&#xff0c;Spring框架中单例bean是线程安全的吗&#xff1f; 2&#xff0c;什么是AOP&#xff1f;你项目有用过吗&#xff1f; 3&#xff0c;Spring事务的失效场景有哪些&#xff1f; 发生自身调用&#xff08;类中使用this调用本类的方法&#xff0…

全国计算机等级考试| 二级Python | 真题及解析(9)

一、选择题 1. 以下关于程序设计语言的描述,错误的选项是: A Python语言是一种脚本编程语言 B汇编语言是直接操作计算机硬件的编程语言 C程序设计语言经历了机器语言、汇编语言、脚本语言三个阶段 D编译和解释的区别是一次性翻译程序还是每次执行时都要翻译程序 正确答…

java每日一题——找出区间内的素数(答案及编程思路)

前言&#xff1a; 学习编程还是要做大量练习呀&#xff0c;不能只学不练&#xff0c;一个题目可以从多个角度去解决&#xff0c;可以全方面巩固知识点。每天记录一点点&#xff0c;daydayup&#xff01; 题目&#xff1a;判断101-200之间有多少个素数&#xff0c;并输出所有素数…

这货能大大增强ChatGpt的战斗力

今天我给你介绍一个能大大增强ChatGpt的战斗力的工具&#xff1a; gapier。 注册gapier ChatGpt推出了GPTs的功能&#xff0c;在创建GPTs的时候有个Actions的选项&#xff0c;是给我们调用第三方接口用的&#xff0c;以前一直不知道这么用。 直到我发现了一个网站&#xff1a…

11.盛水最多的容器(双指针,C解法)

题目描述&#xff1a; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;…