JS性能优化必读,前端热修复原理

GC算法介绍

================================================================

  • GC定义

  • GC可以理解为就是垃圾回收机制的简写

  • GC可以帮找到内存中的垃圾,并释放和回收空间

  • GC里边垃圾是什么

  • 程序当中不再需要使用的对象 — 如果我们某一个数据,在使用完成之后上下文里边不再需要用到他了,我们就可以把它当作垃圾看待,例如name,当函数调用完成后,我们就不再需要使用name了,从需求的角度考虑,他应该是被当作垃圾然后被回收掉的,至于有没有被回收先不考虑

function func() {

name = ‘xl’

return my name is ${name}

}

func()

  • 程序中不能在访问到的对象 — 在函数调用结束之后,我们在外部的空间当中,就不能在访问到name,所以当我们找不到它的时候,其实它也可以算作是一种垃圾

function func() {

const name = ‘xl’

return my name is ${name}

}

func()

  • 什么是GC算法

  • GS是一种机制,垃圾回收器完成具体的回收工作

  • 工作的内容就是查找垃圾 / 释放空间 / 回收空间

  • 算法就是工作时查找和回收所遵循的规则

  1. 如何去查找空间

  2. 释放空间时应该如何释放

  3. 回收空间如何去进行分配

  4. 这些过程必然有不同的方式,这个GC的算法就可以理解为上述的垃圾回收器在工作过程中所遵循的一些规则

  • 常见GC算法

  • 引用计数

  • 通过一个数字来判断当前这个变量时是不是一个垃圾

  • 标记清除

  • 在GC工作的时候给那些活动的对象添加上一个标记,然后判断是不是一个垃圾

  • 标记整理

  • 与标记清除很类似,我们后续回收的过程中可以做一些不同的事情

  • 分代回收

  • 在V8中会用到

引用计数算法实现原理

====================================================================

  • 核心思想:在内部,通过一个引用计数器来维护当前对象的引用数,从而判断当前引用数是否为0来判断它是否是一个垃圾对象。当这个数值为0时,GC就开始工作,将其所在的对象空间回收和释放在使用

  • 引用计数器:相对于其他一些GC算法来说,也正是由于引用计数器的存在,导致了引用计数再执行效率上,可能有其他的算法有区别

  • 当某一个对象的引用关系改变时,引用计数器主动修改当前对象所对应的引用数值

  • 假设代码中现在有一个引用空间,目前来说有一个变量名指向它,这个时候我们就把数值加1,如果在说这个时候又多一个对象还是指向它,那么我们就把数值在加1,如果是减少的情况就减1,如果为0时,那么GC就会立即工作将当前的对象空间回收

const user1 = {age: 18}

const user2 = {age: 28}

const user3 = {age: 38}

const nameList = [user1.age, user2.age, user3.age]

function fn() {

const num1 = 1

const num2 = 2

}

fn() // 不可以获取内部变量

function fn1() {

num1 = 1

num2 = 2

}

fn1() // 可以获取到内部变量

  • 总结:其实就是靠着当前对象身上的一个引用计数的数值,来判断是否为0,从而决定它是不是一个垃圾对象

引用计数算法优缺点

===================================================================

优点


  • 发现垃圾时立即回收

  • 它可以根据当当前这个引用数是否为0来决定这个对象是不是一个垃圾,如果找到了,那就可以立即进行释放

  • 最大限度减少程序暂停

  • 我们的应用程序再执行的过程中,必然会对内存进行一个消耗,而我们当前的执行平台他的内存肯定会有上限的,所以内存肯定会有沾满的时候,不过由于引用计数算法他是时刻监控着那些引用数值为0的对象,所以我们就可以认为,当他发现这个内存即将饱满时,那么引用计数就会立马去找到那些数值为0的计算空间,然后对其进行释放,这样就保证了我们当前的一个内存是不会有占满的时候

缺点


  • 无法回收循环引用的对象

  • 时间开销大

  • 因为当前的引用计数它需要去维护一个数值的变化,所以在这个情况下,他要时刻的去监控着当对象的一个引用数值是否需要修改,本身来说,这个对象的数值修改就需要消耗时间,如果说这个内存里边有更多的对象需要修改,那么这个时间就会显得更大一些。

function fn() {

const obj1 = {}

const obj2 = {}

obj1.name = obj2

obj2.name = obj1

return ‘xl’

}

fn()

/**

  • 我们这个函数执行结束后,内部所在的空间,肯定会涉及到空间回收的情况

  • 比如说obj1 obj2 因为在全局的地方我们已经不在直接去指向他了

  • 所以这个是他的引用计数应该为0

  • 但是现在会有一个问题:

  • 在里边当我们想要去找GC来把obj1删除时,obj2现在有一个属性指向obj1

  • 换句话说,按照之前的规则,我们在全局作用域下,找不到obj1和obj2了

  • 但是由于他两者之间在同一作用域内明显还有互相指引的关系

  • 所以在这种指引下他们身上的引用计数器的数值还不为0

  • 这时引用计数算法下的GC就没有办法将其回收,从而造成了内存空间的浪费

  • 这就是所谓的对象之间的循环引用

  • */

标记清除算法实现原理

====================================================================

  • 相对于之前的引用计数算法,这个算法的原理实现更加简单,而且还能拿个解决一些相应的问题,在后续的V8当中会被大量的使用到

  • 核心思想:分标记和清除两个阶段完成

  • 遍历所有对象,找到活动对象,进行标记

  • 活动对象和可达对象是一个道理

  • 遍历所有对象,没有标记的对象进行清除

  • 注意:第二阶段会把第一阶段设置的标记抹除,便于GC下次还能正常工作

  • 通过两次遍历行为回收相应的空间,最终再交给相应的空闲列表进行维护,后续我们的程序代码可以实现使用

标记清除算法优缺点

===================================================================

  • 作为一个GC算法,它依然是做不到完美无瑕的

优点


  • 解决对象循环引用的回收操作

缺点


  • 回收完成之后地址不连续,后续我们想去申请一片空间,刚好申请的空间大小是1.5格子,这种情况下,有的回收空间是两个格子,有的是一个格子,这个时候造成了一个当前标记清除算法中最大的问题 — 空间的碎片化

  • 空间碎片化 ---- 由于我们当前所回收的垃圾对象,在地址上他本身是不连续的,由于这种不连续从而造成了,我们在回收之后他们分散在各个角落,后续我们想去使用时,新的生成空间刚好与他们大小匹配,那就可以直接使用,一旦是多了或者少了,就不太适合使用了,所以这就是我们标记清除算法当中的一个缺点

标记整理算法实现原理

====================================================================

  • 和标记清除算法一样,在V8当中经常会被使用到

  • 标记整理其实可以看作为标记清除的增强操作

  • 因为标记阶段的操作和标记清除是一样的,因为他们都是遍历所有对象,然后将当前的可达活动对象进行标记

  • 清除阶段时,标记清除算法是直接将未被标记的垃圾对象做空间的回收,但是标记整理会在清除之前,先执行一个整理的操作,移动对象的位置,去让他们能够在地址上产生一个连续

常见GC算法总结

==================================================================

  • 引用计数

  • 在内部去通过一个引用计数器来维护每个对象都存在的一个引用数值,通过这个数值是否为0来判断这个对象是否为垃圾对象,从而去回收他的一个垃圾空间

  • 优缺点

  • 可以即使回收垃圾对象

  • 减少程序卡顿时间

  • 无法回收循环引用的对象

  • 资源消耗较大

  • 标记清除

  • 标记 — 遍历所有对象,然后给当前活动对象标记

  • 清除 — 遍历所有对象,然后给没有被标记到的对象的空间回收

  • 优缺点

  • 可以回收循环引用的对象

  • 容易产生碎片化空间,浪费空间

  • 不能够立即回收垃圾对象

  • 标记整理

  • 标记 — 遍历所有对象,然后给当前活动对象标记

  • 清除 — 遍历所有对象,在清除之前,先执行一个整理的操作,移动对象的位置,去让他们能够在地址上产生一个连续,然后给没有被标记到的对象的空间回收

  • 优缺点

  • 减少碎片化空间

  • 不会立即回收垃圾对象

认识V8

==============================================================

  • V8是目前市面上最主流的JS执行引擎

  • 目前我们的chrome浏览器,nodejs平台都在采用

  • JS之所以能在上边高效的工作,正是因为V8的存在

  • V8采用即时编译

  • 之前的引擎都需要先将我们的JS代码转为字节码,然后才能去执行,对于V8来说,可以直接将源码直接翻译为我们当前可以直接执行的机器码,所以这个时候的速度是非常快的

  • V8内存设有上限

  • V8的内存空间设置了一个数值,在64位的操作系统上不能超过1.5G,对于32位来说这个数值不能超过800MB的

  • 为什么

  • V8本身就是为了浏览器来去制造的,所以现有的内存大小对于网页应用来说是足够的

  • V8内部所去使用的一个垃圾回收机制决定了采用这样一个设置是合理的,因为官方做过这样一个测试,当我们的垃圾内存达到1.5G的时候,如果V8去采用增量标记的算法进行垃圾回收只需要消耗50ms,而如果采用非增量标记的话则需要1s,从用户体验来说,1s其实已经算是很长的时间了,所以V8就是用1.5G为界限了

V8垃圾回收策略

==================================================================

  • 前置描述:我们都知道,在程序的使用过程中,我们会用到很多数据,而这些数据我们可以分为原始数据和对象类型的数据,对于这些基础的原始数据来说,都是由程序的语言自身来进行控制的,所以在这里我们所提到的回收,主要还是指的是当前存活在我们堆区里的对象数据,因此这个过程我们是离不开对象操作的,而我们当前也知道,在V8当中,他对内存是做了上限的,所以我们接下来主要看一下他是怎样对垃圾进行回收的呢

  • 采用分代回收的思想 — 把我们当前的内存空间区按照一定的规则分成两类,一个是新生代存储区,一个是老生代存储区,至于如何划分我们后续会介绍到,有了这样的一个划分之后,它就会根据不同代采用更高效的一种GC算法,从而对不同对象进行一个回收的操作

  • V8常用GC算法

  • 分代回收 — 必须用到,因为要去做空间分代

  • 空间复制 — 后续会介绍

  • 标记清除

  • 标记整理

  • 标记增量 — 为了提高效率

V8如何回收新生代对象

=====================================================================

概述


  • V8内存空间一分为二

  • 小空间用于存储新生代对象(32MB || 16MB)

  • 新生代指的是存活时间较短的对象

  • 局部作用域内的变量就属于新生代

回收流程


  • 回收过程采用复制算法 + 标记整理

  • 首先将左侧的小空间也一分为二,大小相同

  • 使用空间为From,空闲空间为To

  • 将所有的变量对象都分配在From空间

  • 现在To是空闲状态,一旦我们的From空间应用到一定程度,去触发GC操作,这时会采用标记整理的操作将活动对象拷贝至To

  • 拷贝完成就意味着我们之前的From空间的对象有了一个备份,这时就可以考虑做一些回收操作了

  • 将From空间进行完全的释放,因为From里的对象在To里边有一个备份,From直接释放掉就不存在任何问题

  • 这时新生代对象就完成了一个回收操作

回收细节说明


  • 拷贝过程中可能出现晋升

  • 如果我们在拷贝时发现某一个变量对象所指定的空间呢,他在我们当前的老生代对象里边也会出现,这时我们就会出现一个所谓的 晋升 的操作

  • 晋升就是将新生代对象移动至老生代

  • 一轮GC还存活的新生代需要晋升

  • 当前在拷贝的一个过程中发现To空间的使用率超过了25%,那么在这时我们也需要将这一次的活动对象移动至老生代

  • 为什么是25%?

  • 因为我们在将来进行回收操作的时候需要把Form空间和To空间进行一个交换,也就是说以前的To会变成From,以前的From会变为To,这也就意味着我们的To使用率如果达到了80%,那么最终他变成活动对象的存储空间后,新的对象就存不进来了

V8如何回收老生代对象

=====================================================================

概述


  • 老生代对象存放在右侧老生代区域

  • 针对于老生代区域,在V8当中同样有一个内存大小的限制

  • 64位操作系统1.4G,32位操作系统700MB

  • 老生代对象其实指的就是当前存活时间较长的对象

  • 例如:全局对象下的变量,闭包内的变量数据

回收流程


  • 主要采用标记清除,标记整理,增量标记算法

  • 首先使用标记清除完成垃圾空间的回收

  • 存在空间碎片化的问题

  • 如果我们发现当他去想把我们新生代区域的内容,往老生代进行移动的时候,而且这个时间节点上我们老生代存储区域的共建又不足以来存放新生代存储区所以移动过来的对象,也就是我们之前提到过的晋升,这种情况就会触发标记整理,将之前的碎片化空间整理为地址上是连贯的

  • 采用增量标记进行效率优化

回收细节对比


  • 新生代区域垃圾回收使用空间换时间

  • 采用的是复制算法,意味着每时每刻内部都会有一个空闲空间的存在,但是由于新生代存储区,本身的空间就很小,所以分出来的空间就更小,所以这部分空间的浪费,相对于带来的时间上的提升是微不足道的

  • 老生代区域垃圾回收不适合复制算法

  • 老生代空间很大,一分为二的话就有几百MB的空间是浪费不用的,这样就太过于浪费

  • 老生代空间存储的数据其实比较多,所以在复制时消耗的时间也会更多

V8垃圾回收总结

==================================================================

  • V8是一款主流的JS执行引擎

  • V8内存设置上限

  • 本身是针对与浏览器设置的,所以在webAPP上,这个内存是充足的

  • 内部的垃圾回收机制来决定,如果内存再放大,那垃圾回收时间就可能会超过用户的感知,影响体验

  • V8采用分代回收思想实现垃圾回收

  • 新生代

  • 老生代

  • V8垃圾回收常见的GC算法

  • 新生代

  • 复制算法 + 标记整理算法

  • 老生代

  • 标记清除算法 + 标记整理算法 + 增量标记算法

Performance工具介绍

=========================================================================

  • 关于GC的目的就是为了实现内存空间的良性循环

  • 良性循环的基础是对内存空间有一个合理的使用

  • 时刻关注才能确定是否合理

  • Preformance提供多种监控方式

总结:

  • 通过Performance时刻监控内存,有这样一个操作后,我们就可以在程序的内存出现一些问题之后,直接想办法定位到当前问题所在的代码块

基本使用步骤

  • 打开浏览器输入目标网址

  • 进入开发人员工具面板,选择性能

  • 开启录制功能,访问具体界面

  • 执行用户行为,一段时间后定制录制

  • 分析界面中记录的内存信息

内存问题的体现

=================================================================

  • 页面出现延迟加载或者经常性暂停 — 频繁的垃圾回收

  • 页面持续性出现糟糕的性能 — 内存膨胀

  • 页面的性能随时间延长越来越差 — 内存泄漏

监控内存

==============================================================

界定内存问题的标准


  • 内存泄漏 — 内存使用持续升高

  • 内存膨胀 — 在多数设备上都存在性能问题

  • 频繁的垃圾回收 — 通过内存变化图进行分析

监控内存的几种方式


  • 浏览器任务管理器

  • 可以直接以数值的形式,将我们当前应用程序在执行过程中内存的变化体现出来

  • Timeline时序图记录

  • 直接把我们应用程序执行过程中所有内存的走势以时间点的方式呈现出来,有了这张图很容易做判断

  • 堆快照查找分离DOM

  • 有针对性的查找我们当前的界面对象中,是否存在分离的DOM,因为分离DOM的存在就是一种内存泄漏

任务管理器监控内存

===================================================================

  • 如果我们最后一列小括号内的数值一直增大,那就意味着这个内存是有问题的

  • 具体来说是什么问题当前这个工具就显得不是特别好用了,因为它只能帮助我们发现这个地方有没有问题,如果说我们想定位问题时,他就不太好用了

  • 在这个地方我们可以直接通过shift + esc调出任务管理器

  • 找到我们想要去监控的具体脚本,也就是说web页面

  • 选中之后如果说没有JS这一列我们可以直接右键然后勾选

  • 调整完后我们只需要关注两列

  • 第一列为当前DOM节点占用的内存,一般情况也是不变为好,如果要变的话就证明我们当前界面存在频繁的DOM操作

  • 第二列为最后的JS内存,在这里我们要关注的就是小括号内的数值,得出的结论就是如果小括号里的数值一直增加而没有变小的过程,就意味着我们的内存就一直往上走,而没有GC消耗,所以这个时候就有问题了

Timeline记录内存

======================================================================

  • 任务管理器可以帮助我们发现问题,但是具体定位的话就显得不是很方便

  • Timeline — 通过时间线记录内存变化的方式 — 更精确的定位到我们当前内存的问题与那一块代码是相关的,或者说在什么时间节点上发生的

堆快照查找分离DOM

====================================================================

  • 什么是分离DOM

  • 界面元素存活在DOM树上

  • 垃圾对象时的DOM节点

  • 分离状态的DOM节点

  • 总结:

  • 我们可以利用浏览器当中提供的堆快照的功能,然后把我们当前的堆进行性拍照,拍照结束后找这里边是否存在一些分离DOM,因为这个分离DOM在界面中不体现,但是在内存中的确存在,所以这个时候它是一种内存的浪费,我们要做的就是定位到我们的代码里的那些分离DOM所在的位置,然后想办法给清除掉

判断是否存在频繁GC

====================================================================

  • 为什么需要知道是否存在频繁的垃圾回收

  • 当GC工作时应用程序是停止的

  • GC频繁工作而且时间过长,对web应用很不友好

  • 因为它会处于一个假死的状态,对于用户来说就会感觉到整个应用有点卡顿

  • 所以就要想办法确定当前应用中是否存在着频繁的回收

  • Timeline中频繁的上升下降

  • 任务管理器中数据频繁的增加减小

Performance总结

=======================================================================

  • 谷歌浏览器提供的一个性能工具

  • Performance使用流程

  • 内存问题相关分析

  • 内存泄漏

  • 内存膨胀

  • 频繁的垃圾回收

  • Performance 时序图监控内存变化

  • 任务管理器监控内存变化

  • 堆快照查找分离DOM

代码优化介绍

================================================================

如何精准测试JS性能


  • 本质上就是采集大量的执行样本进行数学统计和分析,从而得到一个比对的结果,什么样的脚本执行效率更高,这样一个过程对于我们编码者来说显得有些麻烦,因为我们可能更多地只是关注该如何使用脚本实现某一个功能,而不是去做大量的数学统计

  • 所以在这里我们采用 基于 Benchmark.jshttps://jsperf.com/ 完成

Jsperf 使用流程


  • 使用GitHub账号登录

  • 填写个人信息(非必填)

  • 填写详细的测试用例信息(title、slug)

  • sulg — 必须是唯一的,因为他要生成一个空间来利于我们去访问自己的测试用例

  • 填写准备代码(DOM操作时经常使用)

  • 填写必要有setup与teardown代码

  • 填写测试代码片段

慎用全局变量

================================================================

  • 在程序执行过程中,如果针对于某些数据需要进行存储,那我们可以尽可能的把它放置在局部作用域中变成一个局部变量

  • 当我们在一个全局作用域内定义一个变量后,他其实就是存在于全局的执行上下文当中,是所有作用域链的顶端

  • 如果按照这种层级向上查找的一个过程来说,那下边某些局部作用域没有找到的变量,最终都会去查找到最顶端的全局执行上下文,所以这种情况下,我们的时间消耗是非常大的,这样一来就降低了我们当前代码的执行效率

  • 在当前全局上下文当中去定义的变量,他一直是存活于上下文执行栈,这个栈是直到我们当前程序退出之后才会消失的

  • 这对我们当前的GC工作来说也是非常不利的,因为只要我们的GC发现这样一个变量,属于一个存活的状态我们就不会把他当做垃圾对象进行回收,因此这样的做法也会降低我们当前的程序运行过程中对于内存的一个使用

  • 如果某个局部作用域当中出现了同名全局变量则有可能会遮蔽或污染全局

  • 总体来说,我们使用全局变量时需要考虑更多的事情,否则就会给我们带来一些意想不到的情况

缓存全局变量

================================================================

  • 在我们程序的执行过程中,有些地方,我们针对于这样的全局变量使用,是无法避免的,例如:查找DOM时,我们必须要使用document,而这个document并不是由我们自己直接定义的,而是存在于当前顶层对象下边,内置好了,可以直接用

  • 这时我们可以选择把大量需要重复使用的一个全局变量放置到一个局部作用域当中,从而达成一种缓存的效果

通过原型对象添加附加方法

======================================================================

  • 在JS当中存在三种概念

  • 构造函数

  • 原型对象

  • 实例对象

  • 在这个过程当中,我们的实例对象和构造函数都是可以指向原型对象的,所以这个过程中如果某一个构造函数的内部局有一个成员方法,让我们后续的实例对象都需要频繁的去进行调用,那么在这里可以直接把他添加在原型对象上,而不需要把他放在我们的构造函数内部,而这样两种实现方式呢,在进程上也会有所差异。

避开闭包陷阱

================================================================

  • 外部具有指向内部的引用

  • 在 外 部作用域内访问 内 部作用域的数据

function foo () {

var name = ‘xl’

function fn () {

console.log(name)

}

return fn

}

var a = foo()

a()

  • 关于闭包

  • 闭包是一种强大的语法

  • 闭包使用不当很容易出现内存泄露

  • 不要为了闭包而闭包

// function foo() {

// var el = document.getElementById(‘btn’)

// el.onclick = function () {

// console.log(el.id)

// }

// }

// foo()

/**

  • 标准闭包

    1. 外部对内部有引用
    1. 突破作用域的限制,在一个作用域中用到另一个作用于的数据
  • 因为它是闭包,所以会产生一个不好的影响,就是当前这样一个内存是会泄露的

  • 由于闭包语法的存在,导致我们函数中的一些变量是没有办法被回收

  • 这种情况下类似于这样的操作越来越多的时候,对我们的内存来说是非常不友好的

  • 当我们的内存一直被消耗,我们程序的性能肯定是越来越差

  • */

function foo() {

var el = document.getElementById(‘btn’)

el.onclick = function () {

console.log(el)

}

el = null

}

foo()

/**

  • 关于btn这个元素其实本身就已经存在于我们的DOM当中或者说在body里

  • 所以就算我们这里边不通过el再去引用这样的一个必填的DOM节点,那他本身也会有一个引用的存在

  • 而我们在这个地方是相当于在它的引用之上,又去让一个el也引用了这样一个DOM节点

  • 所以我们也可以理解为是这样一个DOM节点呗我们引用了两次,如果说在将来某一个时间点下

  • 我们这个DOM节点从我们的界面中消失了,也就是说删除或者清除的行为

  • 这是我们要想明白,我们在界面把元素删除,也就意味着在DOM上的引用就不存在了

  • 但是会有一个问题:代码里的el对我们之前的DOM还是有引用的,所以根据我们之前所提到的引用计数这个原则

  • 我们这个DOM对象其实只是减少了一个引用,而代码里边仍在引用这他,所以我们的垃圾回收机制在工作时无法回收

  • 因此我们的泄露依然存在,而如果说加了 el = null 时,当我们在界面当中去把页面的元素清除掉之后,DOM对它的引用就消失了

  • 而代码里边对他的引用也消失了,这个时候我们这个样一个内存空间得以释放,这也是这行代码存在的意义

  • */

  • 这就是如何避开闭包的陷阱,而提高整个程序的性能,不是我们两段代码在执行速度上的一个对比,指的是整个内存层面上的性能对比

避免属性访问方法使用

====================================================================

  • 关于属性访问语法,这是和面向对象相关的,为了更好地实现这样一个封装性,所以在更多的时候我们可能会讲一些对象的基本属性和方法,放在一个函数的内部,然后在外部暴露一个方法对当前这个属性进行增删改查,但是这个特性在JS特效中其实并不是那么的适用,因为在JS中不需要属性的访问方法,所有的属性在外部都是可见的

  • 而且在使用属性访问方法的时候,他就相当于是增加了一层重定义,对当前的访问控制是没有太多的一个意义,所以不去推荐

function Person1() {

this.name = ‘xl’

this.age = 18

this.getAge = function () {

return this. age

}

}

const p1 = new Person1()

const a = p1.getAge() // 属性访问方法

function Person2() {

this.name = ‘xl’

this.age = 18

}

const p2 = new Person2()

const b = p2.age // 直接访问

For循环优化

=================================================================

  • for循环是我们经常会去使用的语法结构,每当我们遇到一个数组结构或者类数组时,我们想要把其中一些值进行拿出使用时,都可以使用for循环对其进行遍历

for (var i = 0; i < btns.length; i++) {

console.log(i)

}

for (var i = 0, len = btns.length; i < len; i++) {

console.log(i)

}

选择最优的循环方法

===================================================================

  • for、foeEach、for…in…

var arrList = new Array(1, 2, 3, 4, 5, 6, 7, 8, 9)

arrList.forEach(function (item) {

console.log(item)

})

for (var i = arrList.length; i; i–) {

console.log(arrList[i])

}

for (var i in arrList) {

console.log(i)

}

// forEach > for > for…in…

文档碎片优化节点添加

====================================================================

  • 针对于web应用的开发来说,DOM节点的操作是很频繁的,而针对于DOM的交互操作又很消耗性能,特别是创建一个新的节点,将它添加至界面中时,这个过程会伴随着回流和重绘的出现

  • 回流和重绘对于性能的消耗又很大

for (var i = 0; i < 10; i++) {

var op = document.createElement(‘p’)

op.innerHTML = i

document.body.appendChild(op)

}

const fragEle = document.createDocumentFragment()

for (var i = 0; i < 10; i++) {

var op = document.createElement(‘p’)

op.innerHTML = i

fragEle.appendChild(op)

}

document.body.appendChild(fragEle)

克隆优化节点操作

==================================================================

for (var i = 0; i < 3; i++) {

var op = document.createElement(‘p’)

op.innerHTML = i

document.body.appendChild(op)

}

var oldP = document.getElementById(‘box’)

for (var i = 0; i < 3; i++) {

var newP = oldP.cloneNode(false)

op.innerHTML = i

document.body.appendChild(op)

}

直接量替换new Object

=========================================================================

var a = [1, 2, 3]

var a1 = new Array(3)

a1[0] = 1

a1[1] = 2

a1[2] = 3

// a > a1

JSBench使用

===================================================================

  • 一个可以在线测试JS代码执行效率的网站

  • JSBench.me

  • 为什么要使用JSBench

  • 因为我们一直使用jsperf这个网站已经不再维护了,所以没有办法使用了

  • 我们有一些JS相关的内容需要去分享,而通过整个网站可以很方便的去在我们去测试部分代码时直接去进行使用

  • 我们需要说明的是,其实测试我们这种代码的执行效率的网站其实有很多,比如我们可以直接使用代码进行时间统计,还有其他的在线网站

  • 选择这个网站也是综合考虑了他的打开速度以及在使用的快捷方面

  • 注意点:

  • 当前这个工具在做测试是尽量只保留一个标签页

  • 在执行代码测试时,尽可能让这个页面停留在这里,或者说当前页面不要关掉

  • 不能执行一遍得出的结论和我们想要的结果一样或者不一样时就认为它是合理的或者是最终的答案,我们应该让当前脚本多执行几次,然后取几率更高的结果

  • 性能测试过程中,我们不应该去纠结于这个代码的执行时间,我们当前去使用这个工具去运行的代码其实主要的还是跑一下代码执行的速度,但是对于我们的性能测试来说,我们关注的他并不是只有时间,他只是众多性能指标当中的一个,一段代码的执行速度快并不意味着他就很健壮

  • 对于我们的代码在执行的过程中所涉及到的性能,其实就是两个方面,要么拿空间换时间,要么拿时间换空间

堆栈中的JS执行过程

====================================================================

  • 这段我们来看一下关于代码在执行过程中偏底层发生的一些事情,这样的操作可以更加具象的表达出一段代码在栈内存和堆内存里是如何执行的,同样的也有利于我们理解GC回收内存的工作过程

let a = 10;

function foo (b) {

let a = 2

function baz © {

console.log(a + b + c)

}

return baz

}

let fn = foo(2)

fn(3)

减少判断层级

================================================================

  • 具体来说就是在我们编写代码的过程中,有可能会去出现判断条件嵌套的场景,往往再出现if…else多层嵌套的时候,我们都可以去通过提前return掉无效的条件来达到嵌套层级的优化效果

// function dys (part, chapter) {

// const parts = [‘ES2016’, ‘ES2017’, ‘ES2018’, ‘ES2019’, ‘ES2020’]

// if (part) {

// if (parts.includes(part)) {

// console.log(‘当前属于这个模块’)

// if (chapter > 5) {

// console.log(‘您需要提供VIP’)

// }

// }

// } else {

// console.log(“请确认模块信息”)

// }

// }

// dys(‘ES2016’, 6)

function dys (part, chapter) {

const parts = [‘ES2016’, ‘ES2017’, ‘ES2018’, ‘ES2019’, ‘ES2020’]

if (!part) {

console.log(“请确认模块信息”)

return

}

if (!parts.includes(part)) return

console.log(“当前属于这个模块”)

if (chapter > 5) {

console.log(“您需要提供VIP”)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

, ‘ES2019’, ‘ES2020’]

if (!part) {

console.log(“请确认模块信息”)

return

}

if (!parts.includes(part)) return

console.log(“当前属于这个模块”)

if (chapter > 5) {

console.log(“您需要提供VIP”)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-1f1mpjMy-1712172026819)]
[外链图片转存中…(img-Ed41N56d-1712172026819)]
[外链图片转存中…(img-UfnUMppO-1712172026820)]
[外链图片转存中…(img-1LINs6Xo-1712172026820)]
[外链图片转存中…(img-oFno0MS9-1712172026820)]
[外链图片转存中…(img-U38Ypwr5-1712172026821)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-MSaICYwd-1712172026821)]

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-Wnfy1EEX-1712172026821)]

[外链图片转存中…(img-VtqalvvK-1712172026822)]

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值