性能优化介绍

  • 性能优化是不可避免的
  • 无处不在的前端性能优化
function func(){arr = []arr[100000] = 'lg is a dog'
}
func()

JavaScript语言的优化

1、内存管理

内存管理介绍

  • 内存:由可读写单元组成,表示一片可操作空间
  • 管理:人为的去操作一片空间的申请、使用和释放
  • 内存管理:开发者主动申请空间、使用空间、释放空间
  • 管理流程:申请-使用-释放
    JavaScript中的内存管理
  • 申请内存空间
  • 使用内存空间
  • 释放内存空间
    //申请
    let obj = {}
    //使用
    obj.age = 18
    //释放
    obj = null
    

2、垃圾回收与常见GC算法

垃圾回收
JavaScript中的垃圾:

  • JavaScript中内存管理是自动的
  • 对象不再被引用时是垃圾
  • 对象不能从根上访问到时是垃圾
    JavaScript中的可达对象:
  • 可以访问到的对象就是可达对象(引用、作用域链)
  • 可达的标准就是从根出发是否能够被找到
  • JavaScript中的根就可以理解为是全局变量对象
//引用
let obj = {age: 34}
let obj2 = obj
obj = null
//可达
function objGroup(obj1,obj2){obj1.next = obj2obj2.prev = obj1return {o1: obj1,o2:obj2}
}
let obj = objGroup({name:'obj1},{name:'obj2'})
console.log(obj)

GC算法
Gc定义与作用:

  • GC就是垃圾回收机制的简写
  • GC可以找到内存当中的垃圾、并释放和回收空间

GC里的垃圾:

  • 程序中不再需要使用的对象
function func(){name = 'lg'return `${name} is a coder`
}
func()
  • 程序中不能再访问到的对象
function func(){const name = 'lg'return `${name} is a coder`
}
func()

GC算法的定义:

  • GC是一种机制,垃圾回收器完成具体的工作
  • 工作的内容就是查找垃圾、释放空间、回收空间
  • 算法就是工作时查找喝回收所遵循的规则

常见GC算法:

  • 引用计数
  • 标记清除
  • 标记整理
  • 分代回收

引用计数算法:

  • 核心思想:设置引用数,判断当前引用数是否为0
  • 引用计数器
  • 引用关系改变时修改引用数字
  • 引用数字为0时立即回收
    const a1 = {age:12}
    const a2 = {age:13}
    const a3 = {age:15}const nameList = [a1.age,a2.age,a3.age]function func(){num1 = 1num2 = 2
    }
    func()
    

引用计数算法优点:

  • 发现垃圾时立即回收
  • 最大限度减少程序暂停
  • 减少程序卡顿时间

引用计数算法缺点:

  • 无法回收循环引用的对象
  • 时间开销大
  • 资源消耗较大
function func(){const o1 = {}const o2 = {}o1.name = o2o2.name = o1return 'lg is a coder'
}
func()

标记清除算法:

  • 核心思想:分标记和清除两个阶段完成
  • 遍历所有对象找标记活动对象
  • 遍历所有对象清除没有标记对象
  • 回收相应的空间
    标记清除算法优点:可以回收循环引用的对象
    标记清除算法缺点:
  • 会产生空间碎片化问题,不能让空间最大化的使用
  • 不会立即回收垃圾对象

标记整理算法:

  • 标记整理可以看做是标记清除的增强
  • 标记阶段的操作和标记清除一致
  • 清除阶段会先执行整理,移动对象位置
    标记整理的优点:
  • 减少碎片化空间
    标记整理的缺点:
    +不会立即回收垃圾对象

3、V8引擎的垃圾回收

V8

  • V8是一款主流的JavaScript执行引擎
  • V8采用即时编译
  • V8内存设置上限

V8垃圾回收策略

  • 采用分代回收的思想
  • 内存分为新生代、老生代
  • 针对不同对象采用不同算法

    V8中常用GC算法
  • 分代回收
  • 空间复制
  • 标记清除
  • 标记整理
  • 标记增量

V8如何回收新生代对象
V8内存分配:

  • V8内存空间一分为二,左边为新生代存储区,右边为老生代存储区
  • 小空间用于存储新生代对象(32M|16M)
  • 新生代指的是存活时间较短的对象(如局部变量)

新生代对象回收实现

  • 回收过程采用复制算法+标记整理
  • 新生代内存区分为二个等大小空间
  • 使用空间为From,空闲空间为To
  • 活动对象存储于From空间
  • 标记整理后将活动对象拷贝至To
  • From与To交换空间完成释放

回收细节说明:

  • 拷贝过程中可能出现晋升
  • 晋升就是将新生代对象移动至老生代
  • 一轮GC还存活的新生代需要晋升
  • To空间的使用率超过25%

V8如何回收老生代对象
老生代对象说明

  • 老生代对象存放在右侧老生代区域
  • 64位操作系统1.4G,32位操作系统700M
  • 老生代对象就是指存活时间较长的对象(全局变量、闭包)

老生代对象回收实现:

  • 主要采用标记清除、标记整理、增量标记算法
  • 首先使用标记清除完成垃圾空间的回收
  • 采用标记整理进行空间优化
  • 采用增量标记进行效率优化

细节对比:

  • 新生代区域垃圾回收使用空间换时间
  • 老生代区域垃圾回收不适合复制算法

标记增量如何优化垃圾回收:

4、Performance工具

为什么使用Performance

  • GC的目的是为了实现内存空间的良性循环
  • 良性循环的基石是合理使用
  • 时刻关注才能确定是否合理
  • Performance提供多种监控方式
  • 通过Performance时刻监控内存

Performance使用步骤

  • 打开浏览器输入目标网址
  • 进入开发人员工具面板,选择性能
  • 开始录制功能,访问具体界面
  • 执行用户行为,一段时间后停止录制
  • 分析界面中记录的内存信息

内存问题的体现

内存问题的外在表现:

  • 页面出现延迟加载或经常性暂停(频繁垃圾回收,内存爆表)
  • 页面持续性出现糟糕的性能(内存膨胀)
  • 页面的性能随时间延长越来越差(内存泄漏)

监控内存的几种方式

界定内存问题的标准:

  • 内存泄漏:内存使用持续升高
  • 内存膨胀:在多数设备上都存在性能问题
  • 频繁垃圾回收:通过内存变化图进行分析

监控内存的几种方式:

  • 浏览器任务管理器
  • Timeline时序图记录
  • 堆快照查找分离DOM
  • 判断是否存在频繁的垃圾回收

1、任务管理器监控内存:
启动浏览器任务管理器:Shift+Esc
2、Timeline记录内存变化:
3、堆快照查找分离DOM
什么是分离DOM:

  • 界面元素存活在DOM树上
  • 垃圾对象时的DOM节点
  • 分离状态的DOM节点

4、判断是否存在频繁的垃圾回收:

  • GC工作时应用程序是停止的
  • 频繁且过长的GC会导致应用假死
  • 用户使用中感知应用卡顿

确定频繁的垃圾回收:

  • Timeline中频繁的上升下降
  • 任务管理器中数据频繁的增加减小

5、代码优化

如何精准测试JavaScript性能

  • 本质上是采集大量的执行样本进行数学统计和分析
  • 使用基于Benchmark.js的https://jsperf.com/完成

Jsperf使用流程

  • 使用GitHub账号登录
  • 填写个人信息(非必须)
  • 填写详细的测试用例信息(title,slug)
  • 填写准备代码(DOM操作时经常使用)
  • 填写必要有setup与teardown代码
  • 填写测试代码片段

慎用全局变量

  • 全局变量定义在全局执行上下文,是所有作用域链的顶端
  • 全局执行上下文一直存在于上下文执行栈,直到程序退出
  • 如果某个局部作用域出现了同名变量则会遮蔽或污染全局
    //全局变量
    var i, str = ''
    for (i = 0; i < 1000; i++) {str += i
    }//局部变量
    for (let i = 0; i < 1000; i++) {let str = ''str += i
    }
    

缓存全局变量
将使用中无法避免的全局变量缓存到局部。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>缓存全局变量</title>
</head><body><input type="button" value="btn" id="btn1"><input type="button" value="btn" id="btn2"><input type="button" value="btn" id="btn3"><input type="button" value="btn" id="btn4"><p>1111</p><input type="button" value="btn" id="btn5"><input type="button" value="btn" id="btn6"><p>2222</p><input type="button" value="btn" id="btn7"><input type="button" value="btn" id="btn8"><p>3333</p><input type="button" value="btn" id="btn9"><input type="button" value="btn" id="btn10"><script>function getBtn() {let oBtn1 = document.getElementById('btn1')let oBtn3 = document.getElementById('btn3')let oBtn5 = document.getElementById('btn5')let oBtn7 = document.getElementById('btn7')let oBtn9 = document.getElementById('btn9')}//缓存function getBtn2() {let obj = documentlet oBtn1 = obj.getElementById('btn1')let oBtn3 = obj.getElementById('btn3')let oBtn5 = obj.getElementById('btn5')let oBtn7 = obj.getElementById('btn7')let oBtn9 = obj.getElementById('btn9')}</script>
</body></html>

通过原型对象添加附加方法
在原型对象上新增实例对象需要的方法

//在对象内部添加方法
var func1 = function () {this.foo = function () {console.log(1111);}
}
let f1 = new func1()//在原型对象上添加方法
var func2 = function () {func2.prototype.foo = function () {console.log(2222);}
}
let f2 = new func2()

避开闭包陷阱
闭包特点:

  • 外部具有指向内部的引用
  • 在“外”部作用域访问“内”部作用域的数据
    function foo(){var name = 'good'function fn(){console.log(name)}return fn
    }
    var a = foo()
    a()
    

关于闭包

  • 闭包是一种强大的语法
  • 闭包使用不当很容易出现内存泄漏
  • 不要为了闭包而闭包
    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>闭包</title>
    </head><body><button id="btn">Add</button><script>//闭包function func() {var el = document.getElementById('btn')el.onclick = function () {console.log(el.id);}//优化,避免闭包产生内存泄漏el = null}func()</script>
    </body>
    </html>
    

避免属性访问方法使用
JavaScrip中的面向对象

  • JS不需属性的访问方法,所有属性都是外部可见的
  • 使用属性访问方法只会增加一层重定义,没有访问的控制力
    //使用属性访问方法
    function Person() {this.name = 'jdjd'this.age = 45this.getAge(){return this.age}
    }
    let p1 = new Person()
    const a = p1.getAge()//通过对象实例访问属性
    function Person2() {this.name = 'jdjd'this.age = 45
    }
    let p2 = new Person2()
    const a = p2.age
    

For循环优化
```javascript

<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>For循环优化</title>
</head><body><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><p class="btn"></p><script>var btns = document.getElementsByClassName('btn')for (var i = 0; i < btns.length; i++) {console.log(i);}// 优化for (var i = 0, len = btns.length; i < len; i++) {console.log(i);}</script>
</body>
</html>
```

采用最优循环方式

var arr = new Array(1, 2, 3, 4, 5)//最优
arr.forEach(item => console.log(item))//次优
for (var i = arr.length; i; i--) {console.log(arr[i]);
}//慢
for (var i in arr) {console.log(arr[i]);
}

文档碎片优化节点添加
节点的添加操作必然会有回流和重绘。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>优化节点添加</title>
</head><body><script>//不做任何优化for (var i = 0; i < 10; i++) {var dom = document.createElement('p')dom.innerHTML = idocument.body.appendChild(dom)}//优化方式const fragEle = document.createDocumentFragment()for (var i = 0; i < 10; i++) {var dom = document.createElement('p')dom.innerHTML = ifragEle.appendChild(dom)}document.body.appendChild(fragEle)</script>
</body></html>

克隆节点优化操作

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>克隆优化节点</title>
</head><body><p id="box1">old</p><script>//传统方式for (var i = 0; i < 3; i++) {var eleP = document.createElement('p')eleP.innerHTML = idocument.body.appendChild(eleP)}//克隆节点方式var oldP = document.getElementById('box1')for (var i = 0; i < 3; i++) {var newP = oldP.cloneNode(false)newP.innerHTML = idocument.body.appendChild(newP)}</script>
</body>
</html>

直接量替换Object操作

//new关键字
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3//字面量方式--更优
var a = [1, 2, 3]

JSBench使用

堆栈中的JS执行过程

let a = 10
function foo(b) {let a = 2return function baz(c) {console.log(a + b + c);}
}let fn = foo(2)
fn(3) //7


减少判断层级

//优化前
function doSomething(part, chapter) {const parts = ['科学', 'vue', 'js', 'React', 'Node']if (part) {if (parts.includes(part)) {console.log('属于当前课程');if (chapter > 5) {console.log('你需要提供VIP身份');}}} else {console.log('请确认模块信息');}
}//优化后
function doSomething(part, chapter) {const parts = ['科学', 'vue', 'js', 'React', 'Node']if (!parts.includes(part)) returnconsole.log('属于当前课程');if (chapter > 5) {console.log('你需要提供VIP身份');}
}doSomething('科学', 6)
// 属于当前课程
// 你需要提供VIP身份


减少作用域链查找层级

//优化前
var name = 'zce'
function foo() {name = 'hello' //全局变量function baz() {var age = 38console.log(age);console.log(name);}baz()
}
//优化后
var name = 'zce'
function foo() {var name = 'hello' //全局变量function baz() {var age = 38console.log(age);console.log(name);}baz()
}foo()
//38
//hello


减少数据读取次数
在JS中经常使用到的数据表现形式:字面量、局部变量、数组元素、对象属性。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>减少数据读取次数</title>
</head><body><div id="skip" class="skip"></div><script>var oBox = document.getElementById('skip')//优化前function hasEle(ele, cls) {return ele.className == cls}//优化后function hasEle(ele, cls) {var className = ele.className  //提前缓存return className == cls}console.log(hasEle(oBox, 'skip'));  //true</script>
</body></html>


字面量与构造式

//构造式
var test = () => {let obj = new Object()  //调用函数obj.name = 'rock'obj.age = 18obj.school = 'xinlian'return obj
}//字面量式
var test = () => {let obj = {name: 'rock',age: 18,school: 'xinlian'}return obj
}console.log(test());
//{ name: 'rock', age: 18, school: 'xinlian' }var str1 = 'hello world' //(更优)
var str2 = new String('hello world')
console.log(str1);  //hello world
console.log(str2);  //[String: 'hello world']


减少循环体中活动

//优化前
var test = () => {var ivar arr = [12, 45, 38]for (i = 0; i < arr.length; i++) {console.log(arr[i]);}
}
//优化后
var test = () => {var ivar arr = [12, 45, 38]var len = arr.length //把重新不变的操作从循环体中提出来for (i = 0; i < len; i++) {console.log(arr[i]);}
}
test()


更优的方案:

var test = () => {var ivar arr = [12, 45, 38]var len = arr.lengthfor (i = 0; i < len; i++) {console.log(arr[i]);}
}
//更优的方案
var test = () => {var ivar arr = [12, 45, 38]var len = arr.lengthwhile (len--) {console.log(arr[len]);}
}test()


减少声明及语句数
减少声明:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>减少声明及语句数</title>
</head><body><div id="box" style="width: 10px;height:20px;"></div><script>var oBox = document.getElementById('box')//优化前var test = (ele) => {const w = ele.offsetWidthconst h = ele.offsetHeightreturn w * h}//优化后var test = (ele) => {return ele.offsetWidth * ele.offsetHeight}console.log(test(oBox));  //200</script>
</body></html>


减少语句数:

//优化前var test = () => {var name = 'tom'var age = 18var school = 'xinlian'return name + age + school}//优化后var test = () => {var name = 'tom',age = 18,school = 'xinlian'return name + age + school}console.log(test());  //tom18xinlian


惰性函数与性能

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>惰性函数与性能</title>
</head><body><button id="btn">点击</button><script>var oBtn = document.getElementById('btn')function foo() {console.log(this);}//优化前function addEvent(obj, type, fn) {if (obj.addEventListener) {obj.addEventListener(type, fn, false)} else if (obj.attachEvent) {obj.attachEvent('on' + type, fn)} else {obj['on' + type] = fn}}//优化后function addEvent(obj, type, fn) {if (obj.addEventListener) {addEvent = obj.addEventListener(type, fn, false)} else if (obj.attachEvent) {addEvent = obj.attachEvent('on' + type, fn)} else {addEvent = obj['on' + type] = fn}return addEvent}addEvent(oBtn, 'click', foo)  //<button id="btn">点击</button></script>
</body></html>


采用事件绑定

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>采用事件委托</title>
</head><body><ul id="ul"><li>ZCE</li><li>HCG</li><li>ABG</li></ul><script>var list = document.querySelectorAll('li')//传统方式for (let item of list) {item.onclick = showTxt}function showTxt(ev) {console.log(ev.target.innerHTML);}//委托方式var oUl = document.getElementById('ul')oUl.addEventListener('click', showTxt, true)function showTxt(ev) {var obj = ev.targetif (obj.nodeName.toLowerCase() === 'li') {console.log(ev.target.innerHTML);}}</script>
</body></html>

JavaScript性能优化详解相关推荐

  1. 一零四、前端性能优化详解

    1 前端性能优化 介绍 页面性能优化 浏览器 浏览器的主要作用 浏览器的组成结构 浏览器是多进程的 浏览器的渲染机制 重排reflow与重绘repaint 页面加载缓慢的原因 浏览器部分 代码部分 优 ...

  2. logback性能优化详解

    前言 不正确的日志打印不但会降低程序运行性能,还会占用大量IO资源和硬盘存储空间. 本文主要总结一些能提高日志打印性能的手段. 一.通过AsyncAppender异步输出日志 我们通常使用的Conso ...

  3. 转载:SqlServer数据库性能优化详解

    本文转载自:http://blog.csdn.net/andylaudotnet/article/details/1763573 性能调节的目的是通过将网络流通.磁盘 I/O 和 CPU 时间减到最小 ...

  4. T- SQL性能优化详解

    http://www.cnblogs.com/Shaina/archive/2012/04/22/2464576.html 故事开篇:你和你的团队经过不懈努力,终于使网站成功上线,刚开始时,注册用户较 ...

  5. MYSQL性能优化详解(二)

    接着上一篇学习:http://www.cnblogs.com/quanzhiguo/p/6401453.html 七.MySQL数据库Schema设计的性能优化 高效的模型设计 适度冗余-让Query ...

  6. Android UI性能优化详解

    此文来自于MrPeak杂货铺,由于没法转载,只能贴这了,妄作者见谅:http://mrpeak.cn/android/2016/01/11/android-performance-ui 设计师,开发人 ...

  7. postgresql 开启大页_Postgresql-11.X 性能优化详解

    系统优化 修改 /etc/grub.conf 关闭 numa=off ,修改磁盘IO调度方式 elevator=deadline 修改方法: grubby --update-kernel=ALL -- ...

  8. SQL--数据库性能优化详解

    转:http://blog.csdn.net/andylaudotnet/article/details/1763573 转载于:https://www.cnblogs.com/zcttxs/p/32 ...

  9. MySQL之SQL优化详解(二)

    目录 MySQL之SQL优化详解(二) 1. SQL的执行顺序 1.1 手写顺序 1.2 机读顺序 2. 七种join 3. 索引 3.1 索引初探 3.2 索引分类 3.3 建与不建 4. 性能分析 ...

最新文章

  1. python 初试 2
  2. ios 标准 #pragma mark的用法
  3. MonkeyRunner在Windows下的Eclipse开发环境搭建步骤(兼解决网上Jython配置出错的问题)...
  4. Go的内置函数/内建函数:len new make
  5. 苹果手机视频传输教程-通过无线将电脑上视频传到手机
  6. “‘天池·TEENTOP杯’AI少年挑战赛”正式启动!
  7. Python实现Adaboost
  8. 微海鼠标自动点击器 支持录制和循环播放
  9. 记录搭建Odoo框架
  10. ASP.NET AJAX,WCF,ADO.NET Entity 开发实例
  11. php cms 新闻采集,自动新闻采集软件快速入门图文详细教程
  12. 速看,成都市关于促进低效工业用地企业提质增效的信息
  13. 苹果笔记本开机出现闪烁的问号文件夹解决方法
  14. 论文写作: Abstract 和 Introduction 的 区别
  15. Microsoft Visusl C++2010运行程序时,调试弹出黑框自动闪退无法看见运行结果的解决方法
  16. [C语言]Pow函数的实现
  17. WEB 3D JS 使用【3Dmol.js】
  18. Python Flask开源博客系统Blog_mini
  19. 大疆无人机飞行+摄影测量航测不完全教程
  20. postgres物理备份与恢复

热门文章

  1. exchange 2013 批量启用AD域中已存账号的邮箱(转)
  2. 小数点用计算机怎么用,计算机如何精确到小数点后一百位
  3. 有哪些好的科研习惯?
  4. input使用自动输入autofill的时候,修改背景颜色
  5. 11个最流行的静态(博客)网站生成工具
  6. pptp和l2tp协议有什么区别?
  7. app屡次停止运行怎么解决_吸尘电动清扫车不运行怎么解决?
  8. POI推荐文献阅读笔记3:Predicting Human Mobility via Graph Convolutional Dual-attentive Networks
  9. 在 Oracle 中新建 SDE 用户
  10. Unity报错解决方法