====================================================================
-
相对于之前的引用计数算法,这个算法的原理实现更加简单,而且还能拿个解决一些相应的问题,在后续的V8当中会被大量的使用到
-
核心思想:分标记和清除两个阶段完成
-
遍历所有对象,找到活动对象,进行标记
- 活动对象和可达对象是一个道理
-
遍历所有对象,没有标记的对象进行清除
- 注意:第二阶段会把第一阶段设置的标记抹除,便于GC下次还能正常工作
-
-
通过两次遍历行为回收相应的空间,最终再交给相应的空闲列表进行维护,后续我们的程序代码可以实现使用
===================================================================
- 作为一个GC算法,它依然是做不到完美无瑕的
- 解决对象循环引用的回收操作
-
回收完成之后地址不连续,后续我们想去申请一片空间,刚好申请的空间大小是1.5格子,这种情况下,有的回收空间是两个格子,有的是一个格子,这个时候造成了一个当前标记清除算法中最大的问题 — 空间的碎片化
- 空间碎片化 ---- 由于我们当前所回收的垃圾对象,在地址上他本身是不连续的,由于这种不连续从而造成了,我们在回收之后他们分散在各个角落,后续我们想去使用时,新的生成空间刚好与他们大小匹配,那就可以直接使用,一旦是多了或者少了,就不太适合使用了,所以这就是我们标记清除算法当中的一个缺点
====================================================================
-
和标记清除算法一样,在V8当中经常会被使用到
-
标记整理其实可以看作为标记清除的增强操作
- 因为标记阶段的操作和标记清除是一样的,因为他们都是遍历所有对象,然后将当前的可达活动对象进行标记
-
清除阶段时,标记清除算法是直接将未被标记的垃圾对象做空间的回收,但是标记整理会在清除之前,先执行一个整理的操作,移动对象的位置,去让他们能够在地址上产生一个连续
==================================================================
-
引用计数
-
在内部去通过一个引用计数器来维护每个对象都存在的一个引用数值,通过这个数值是否为0来判断这个对象是否为垃圾对象,从而去回收他的一个垃圾空间
-
优缺点
-
可以即使回收垃圾对象
-
减少程序卡顿时间
-
无法回收循环引用的对象
-
资源消耗较大
-
-
-
标记清除
-
标记 — 遍历所有对象,然后给当前活动对象标记
-
清除 — 遍历所有对象,然后给没有被标记到的对象的空间回收
-
优缺点
-
可以回收循环引用的对象
-
容易产生碎片化空间,浪费空间
-
不能够立即回收垃圾对象
-
-
-
标记整理
-
标记 — 遍历所有对象,然后给当前活动对象标记
-
清除 — 遍历所有对象,在清除之前,先执行一个整理的操作,移动对象的位置,去让他们能够在地址上产生一个连续,然后给没有被标记到的对象的空间回收
-
优缺点
-
减少碎片化空间
-
不会立即回收垃圾对象
-
-
==============================================================
-
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当中,他对内存是做了上限的,所以我们接下来主要看一下他是怎样对垃圾进行回收的呢
- 采用分代回收的思想 — 把我们当前的内存空间区按照一定的规则分成两类,一个是新生代存储区,一个是老生代存储区,至于如何划分我们后续会介绍到,有了这样的一个划分之后,它就会根据不同代采用更高效的一种GC算法,从而对不同对象进行一个回收的操作
-
V8常用GC算法
-
分代回收 — 必须用到,因为要去做空间分代
-
空间复制 — 后续会介绍
-
标记清除
-
标记整理
-
标记增量 — 为了提高效率
-
=====================================================================
-
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当中同样有一个内存大小的限制
- 64位操作系统1.4G,32位操作系统700MB
-
老生代对象其实指的就是当前存活时间较长的对象
- 例如:全局对象下的变量,闭包内的变量数据
-
主要采用标记清除,标记整理,增量标记算法
-
首先使用标记清除完成垃圾空间的回收
- 存在空间碎片化的问题
-
如果我们发现当他去想把我们新生代区域的内容,往老生代进行移动的时候,而且这个时间节点上我们老生代存储区域的共建又不足以来存放新生代存储区所以移动过来的对象,也就是我们之前提到过的晋升,这种情况就会触发标记整理,将之前的碎片化空间整理为地址上是连贯的
-
采用增量标记进行效率优化
-
新生代区域垃圾回收使用空间换时间
- 采用的是复制算法,意味着每时每刻内部都会有一个空闲空间的存在,但是由于新生代存储区,本身的空间就很小,所以分出来的空间就更小,所以这部分空间的浪费,相对于带来的时间上的提升是微不足道的
-
老生代区域垃圾回收不适合复制算法
-
老生代空间很大,一分为二的话就有几百MB的空间是浪费不用的,这样就太过于浪费
-
老生代空间存储的数据其实比较多,所以在复制时消耗的时间也会更多
-
==================================================================
-
V8是一款主流的JS执行引擎
-
V8内存设置上限
-
本身是针对与浏览器设置的,所以在webAPP上,这个内存是充足的
-
内部的垃圾回收机制来决定,如果内存再放大,那垃圾回收时间就可能会超过用户的感知,影响体验
-
-
V8采用分代回收思想实现垃圾回收
-
新生代
-
老生代
-
-
V8垃圾回收常见的GC算法
-
新生代
- 复制算法 + 标记整理算法
-
老生代
- 标记清除算法 + 标记整理算法 + 增量标记算法
-
=========================================================================
-
关于GC的目的就是为了实现内存空间的良性循环
-
良性循环的基础是对内存空间有一个合理的使用
-
时刻关注才能确定是否合理
-
Preformance提供多种监控方式
总结:
- 通过Performance时刻监控内存,有这样一个操作后,我们就可以在程序的内存出现一些问题之后,直接想办法定位到当前问题所在的代码块
基本使用步骤
-
打开浏览器输入目标网址
-
进入开发人员工具面板,选择性能
-
开启录制功能,访问具体界面
-
执行用户行为,一段时间后定制录制
-
分析界面中记录的内存信息
=================================================================
-
页面出现延迟加载或者经常性暂停 — 频繁的垃圾回收
-
页面持续性出现糟糕的性能 — 内存膨胀
-
页面的性能随时间延长越来越差 — 内存泄漏
==============================================================
-
内存泄漏 — 内存使用持续升高
-
内存膨胀 — 在多数设备上都存在性能问题
-
频繁的垃圾回收 — 通过内存变化图进行分析
-
浏览器任务管理器
- 可以直接以数值的形式,将我们当前应用程序在执行过程中内存的变化体现出来
-
Timeline时序图记录
- 直接把我们应用程序执行过程中所有内存的走势以时间点的方式呈现出来,有了这张图很容易做判断
-
堆快照查找分离DOM
- 有针对性的查找我们当前的界面对象中,是否存在分离的DOM,因为分离DOM的存在就是一种内存泄漏
===================================================================
-
如果我们最后一列小括号内的数值一直增大,那就意味着这个内存是有问题的
-
具体来说是什么问题当前这个工具就显得不是特别好用了,因为它只能帮助我们发现这个地方有没有问题,如果说我们想定位问题时,他就不太好用了
-
在这个地方我们可以直接通过
shift + esc
调出任务管理器 -
找到我们想要去监控的具体脚本,也就是说web页面
-
选中之后如果说没有JS这一列我们可以直接右键然后勾选
-
调整完后我们只需要关注两列
-
第一列为当前DOM节点占用的内存,一般情况也是不变为好,如果要变的话就证明我们当前界面存在频繁的DOM操作
-
第二列为最后的JS内存,在这里我们要关注的就是小括号内的数值,得出的结论就是如果小括号里的数值一直增加而没有变小的过程,就意味着我们的内存就一直往上走,而没有GC消耗,所以这个时候就有问题了
-
======================================================================
-
任务管理器可以帮助我们发现问题,但是具体定位的话就显得不是很方便
-
Timeline — 通过时间线记录内存变化的方式 — 更精确的定位到我们当前内存的问题与那一块代码是相关的,或者说在什么时间节点上发生的
====================================================================
-
什么是分离DOM
-
界面元素存活在DOM树上
-
垃圾对象时的DOM节点
-
分离状态的DOM节点
-
-
总结:
- 我们可以利用浏览器当中提供的堆快照的功能,然后把我们当前的堆进行性拍照,拍照结束后找这里边是否存在一些分离DOM,因为这个分离DOM在界面中不体现,但是在内存中的确存在,所以这个时候它是一种内存的浪费,我们要做的就是定位到我们的代码里的那些分离DOM所在的位置,然后想办法给清除掉
====================================================================
-
为什么需要知道是否存在频繁的垃圾回收
-
当GC工作时应用程序是停止的
-
GC频繁工作而且时间过长,对web应用很不友好
-
因为它会处于一个假死的状态,对于用户来说就会感觉到整个应用有点卡顿
-
-
所以就要想办法确定当前应用中是否存在着频繁的回收
-
Timeline中频繁的上升下降
-
任务管理器中数据频繁的增加减小
-
=======================================================================
-
谷歌浏览器提供的一个性能工具
-
Performance使用流程
-
内存问题相关分析
-
内存泄漏
-
内存膨胀
-
频繁的垃圾回收
-
-
Performance 时序图监控内存变化
-
任务管理器监控内存变化
-
堆快照查找分离DOM
================================================================
-
本质上就是采集大量的执行样本进行数学统计和分析,从而得到一个比对的结果,什么样的脚本执行效率更高,这样一个过程对于我们编码者来说显得有些麻烦,因为我们可能更多地只是关注该如何使用脚本实现某一个功能,而不是去做大量的数学统计
-
所以在这里我们采用 基于
Benchmark.js
的https://jsperf.com/
完成
-
使用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. 外部对内部有引用
* 2. 突破作用域的限制,在一个作用域中用到另一个作用于的数据
*
* 因为它是闭包,所以会产生一个不好的影响,就是当前这样一个内存是会泄露的
*
* 由于闭包语法的存在,导致我们函数中的一些变量是没有办法被回收
*
* 这种情况下类似于这样的操作越来越多的时候,对我们的内存来说是非常不友好的
*
* 当我们的内存一直被消耗,我们程序的性能肯定是越来越差
* */
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 (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)
}
=========================================================================
var a = [1, 2, 3]
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3
// a > a1
===================================================================
-
一个可以在线测试JS代码执行效率的网站
- JSBench.me
-
为什么要使用JSBench
-
因为我们一直使用jsperf这个网站已经不再维护了,所以没有办法使用了
-
我们有一些JS相关的内容需要去分享,而通过整个网站可以很方便的去在我们去测试部分代码时直接去进行使用
-
-
我们需要说明的是,其实测试我们这种代码的执行效率的网站其实有很多,比如我们可以直接使用代码进行时间统计,还有其他的在线网站
-
选择这个网站也是综合考虑了他的打开速度以及在使用的快捷方面
-
注意点:
-
当前这个工具在做测试是尽量只保留一个标签页
-
在执行代码测试时,尽可能让这个页面停留在这里,或者说当前页面不要关掉
-
不能执行一遍得出的结论和我们想要的结果一样或者不一样时就认为它是合理的或者是最终的答案,我们应该让当前脚本多执行几次,然后取几率更高的结果
-
性能测试过程中,我们不应该去纠结于这个代码的执行时间,我们当前去使用这个工具去运行的代码其实主要的还是跑一下代码执行的速度,但是对于我们的性能测试来说,我们关注的他并不是只有时间,他只是众多性能指标当中的一个,一段代码的执行速度快并不意味着他就很健壮
-
对于我们的代码在执行的过程中所涉及到的性能,其实就是两个方面,要么拿空间换时间,要么拿时间换空间
-
====================================================================
- 这段我们来看一下关于代码在执行过程中偏底层发生的一些事情,这样的操作可以更加具象的表达出一段代码在栈内存和堆内存里是如何执行的,同样的也有利于我们理解GC回收内存的工作过程
let a = 10;
function foo (b) {
let a = 2
function baz (c) {
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")
}
}
dys('ES2016', 6)
/**
* 优化的核心思想和作用域链的查找和内存的空间的使用并不是那么的有关系或者直观
*
* 因为我们是从代码的整体的一个写法进行的改变
*
* 首先就是代码量
*
* 再有就是上边的代码就是一层一层的套,下边的直接去判断一层够了
*
* 相当于我们判断问题的算法得到一个改变
*
* 明确类条件的分支建议使用 switch..case...
*
* if..else...一般适合于区间的条件判断,如果我们明确了几个枚举值的时候建议使用switch...case...
*
* 这样代码会更加清晰,易于维护
*
* 不过易于维护的代码并不代表执行速度就很快,这点对于性能优化来说还是要看我们需要什么来决定的
* */
====================================================================
- 每当一个函数执行的时候会产生一个执行上下文,例如我们当前代码中的foo函数,他的上下文就定义了函数执行的环境,当这个该函数执行结束这个上下文就会被销毁掉,这也取决于是否有闭包的存在,因此我们多次调用函数时就会多次创建上下文,这些上下文他都是有自己的作用域的,这些作用域之间呢又可以去通过作用域链进行连接,所以在函数执行过程中,就会先去搜索他自己的作用域,例如fn函数中需要访问age和name,他首先会在自己内部找,发现有age那就用自己的就可以,没有name,这个时候会沿着作用域链向上查找,他上层就是父作用域,如果在父作用域内没有找到,就继续往上找,父的上层在我们的代码中其实就是全局,这样一层一层的查找,基于这样的查找过程,如果说变量存在于离我们当前fn更近的地方,那查找所消耗的时间就会更快一些,那代码的执行效率会更高
// var name = 'xl'
// function foo () {
// name = 'xl666' // name属于全局 --- 修改了全局变量
// function fn () {
// var age = 38
// console.log(age)
// console.log(name)
// }
// fn()
// }
// foo()
var name = 'xl'
function foo () {
var name = 'xl666' // name属于局局 --- 修改了局部变量
function fn () {
var age = 38
console.log(age)
console.log(name)
}
fn()
}
foo()
==================================================================
- 这种的快,是建立在我们空间的消耗上的提速,后续主要看我们的需求是看重速度还是空间
// var box = document.getElementById('skip')
// function hasbox (ele, cls) {
// return ele.className === cls
// }
// console.log(hasbox(box, 'skip'))
var box = document.getElementById('skip')
function hasbox (ele, cls) {
var clsname = ele.className
return clsname === cls
}
console.log(hasbox(box, 'skip'))
=================================================================
- 不同的数据声明方式和性能的关系,我们肯定都听过在编码是使用字面量来代替构造的方式创建数据,但是这种方式是有一些特定场景的,例如我们在处理日期,数组类型的数据的时候,无论我们是采用字面量还是构造的方式,我们最终得到的数据他们都是引用类型的
// let test = () => {
// let obj = new Object()
// obj.name = 'xl'
// obj.age = 18
// obj.slogan = '你好小鹿'
// return obj
// }
// console.log(test())
/**
* 上边的方式就好像是在调用一个函数,而下边在创建时其实就是直接去开辟空间在里边存东西
*
* 这个过程又涉及到了一个函数的调用,所以就相当于他做的事情更多一些
* */
let test = () => {
let obj = {
name : 'xl',
age : 18,
slogan : '你好小鹿'
}
return obj
}
console.log(test())
var str1 = 'xl说我为XX学代码'
/**
* 上边的代码其实就是一行字符串,下边的是一个对象
* */
var str2 = new String('xl说我为XX学代码')
==================================================================
-
主要讨论循环的这样一个功能,而不是现在要去采用那种结构实现一种循环,所以这里就只是采用了for结构
-
放在循环体中的,往往都是一些我们想要去重复执行的事情,再循环次数固定的情况下,循环体做的事情越多,那执行效率就会越慢,反之效率就会越高
-
所以第一个思路就是把每次循环都要去用到的或者说都要操作的数据值不变的都抽离到外部去完成,这个道理类似于数据缓存
// var test = () => {
// var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// for (var i = 0; i < arr.length; i++) {
// console.log(arr[i])
// }
// }
// test()
// var test = () => {
// var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
// i ,
// len = arr.length
// for (i = 0; i < len; i++) {
// console.log(arr[i])
// }
// }
// test()
var test = () => {
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
len = arr.length
while (len--) {
console.log(arr[len])
}
}
test()
==================================================================
-
代码编写中的小细节与性能的关系
-
变量的声明
-
语句数的多少,其实就是表达式的多少
-
-
我们都知道,程序员是可以记录、保存以及修改状态的,而正是这些状态让我们可以利用这些语言完成很多复杂的功能,很明显,如果我们要去记录这些功能,我们就应该消耗更多的空间,因此对于那些我们后续不需要轮番使用的数据,我们应该去使用的时候直接进行获取,而不是提前去做缓存
-
目的在于可以降低程序在执行时当前对内存的消耗
var box = document.getElementById('box')
var test = (ele) => {
let w = ele.offsetWidth
let h = ele.offsetHeight
return w * h
}
console.log(test(box))
var box = document.getElementById('box')
var test = (ele) => {
return ele.offsetWidth * ele.offsetHeight
}
console.log(test(box))
var test = () => {
var name = 'xl'
var age = 18
var slogan = 'xl666lx'
return name + ' ' + age + ' ' + slogan
}
console.log(test())
var test = () => {
var name = 'xl',
age = 18,
slogan = 'xl666lx'
return name + ' ' + age + ' ' + slogan
}
console.log(test())
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
-
代码编写中的小细节与性能的关系
-
变量的声明
-
语句数的多少,其实就是表达式的多少
-
-
我们都知道,程序员是可以记录、保存以及修改状态的,而正是这些状态让我们可以利用这些语言完成很多复杂的功能,很明显,如果我们要去记录这些功能,我们就应该消耗更多的空间,因此对于那些我们后续不需要轮番使用的数据,我们应该去使用的时候直接进行获取,而不是提前去做缓存
-
目的在于可以降低程序在执行时当前对内存的消耗
var box = document.getElementById('box')
var test = (ele) => {
let w = ele.offsetWidth
let h = ele.offsetHeight
return w * h
}
console.log(test(box))
var box = document.getElementById('box')
var test = (ele) => {
return ele.offsetWidth * ele.offsetHeight
}
console.log(test(box))
var test = () => {
var name = 'xl'
var age = 18
var slogan = 'xl666lx'
return name + ' ' + age + ' ' + slogan
}
console.log(test())
var test = () => {
var name = 'xl',
age = 18,
slogan = 'xl666lx'
return name + ' ' + age + ' ' + slogan
}
console.log(test())
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-LVEvXJTG-1714932599829)]
[外链图片转存中…(img-Y2lpov3u-1714932599829)]
[外链图片转存中…(img-nuJx1KHp-1714932599830)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!