JavaScript 性能优化

JS内存管理

内存:可读写单元组成,代表一片可操作空间
管理:由人去操作一片空间的申请、使用和开放
内存管理:开发者主动的去申请、使用、释放空间
流程:申请-使用-释放
由于JS没有提供对应的Api,因此不能手动调用。但是可以通过脚本语言演示
申请
let obj = {}
使用
obj.name = "tom"
释放
obj =null

JS的垃圾回收

JS的内存管理是自动的
当我们生命的数据不再被引用的时候就会被看成垃圾
对象不能从根上访问到时是垃圾
JS中的可达对象
可以被访问的对象(引用、作用域链)
标准是从根上出发是否能够被找到
JS的根可以片面的理解为全局变量对象(全局执行上下文)

function objGroup(o1, o2) {
	o1.next = o2
	o2.pre = o1
	return {
		obj1 : o1,
		obj2 : o2,
	}
}
let obj = objGroup({name:'o1'}, {name:'o2'})
console.log(obj)

在这里插入图片描述

GC算法介绍

GC:垃圾回收机制的简写
GC可以帮我找到内存中的垃圾、释放并回收空间
GC中的垃圾标准值得是
程序中不再需要使用的对象
function fun(){
	name = 'lj'
	return `${name } is a code`
}
fun()
程序中不能访问到的对象
function fun(){
	const name = 'lj'
	return `${name } is a code`
}
fun()

GC是一种机制,垃圾回收器完成具体的工作
工作的内容就是查找垃圾,释放空间然后回收空间
算法就是工作中查找和回收所遵循的规则
引用计数
标记清除
标记整理
分代回收

引用计数

核心思想:设置引用数,判断当前引用数是否为0
引用计数器
引用关系改变时修改引用数字(判断一个对象是否有对应的引用指向当前)
当引用数字为0时 ,GC立即工作进行回收

const obj1 = {age:12}
const obj2 = {age:12}
const obj3 = {age:12}
const arr = [obj1.age,obj2.age,obj3.age]
/**function fun(){
	num1 = 1
	num2 = 2
}*/一套代码执行完毕之后num1 num2 挂在在全局变量下,一直被引用
function fun(){
	const num1 = 1//一套代码执行完毕之后,num1 num2不会被访问到会被回收掉
	const num2 = 2
}
fun()

引用计数优点
	发现垃圾立即进行回收
	最大限度减少程序的暂停
引用计数缺点
	时间开销巨大,速度比较慢
	无法回收循环引用的对象
	function fun(){
		const o1 = {}
		const o2 = {}
		o1.next = o2
		o2.pre = o1
		return ''
	}
	fun()
	

标记清除

核心思想:分标记和清除两个阶段完成
遍历所有对象寻找标记活动对象
遍历所有对象清除没有标记的对象
回收相应的空间

标记清除优点
	解决之前对象循环引用的操作
标记清除缺点
	不会立即清除垃圾对象,清除时停止工作
	
	产生标记碎片化的问题,导致回收的空间不连续,位置分配在各个角落,杂乱无章

在这里插入图片描述
在这里插入图片描述

标记整理

标记整理可以看做标记清除的增强
前期依然是遍历整个对象,并且标记可达对象
但是清除阶段时会先执行整理,移动对象的位置
缺点:也是无法回收垃圾对象

整理前
在这里插入图片描述
整理后
在这里插入图片描述

V8

V8是一款优秀的JS执行引擎
V8采用的是即时编译(传统的代码编译是将源码转成字节码,然后再转成机器认识的机器码,V8引擎则是跳过这个步骤)
V8内存设有上限(64位操作系统是1.5g,32位操作系统则是800M)
目的一是为了浏览器
而是在这个极限范围内垃圾回收效率最高

V8垃圾回收策略

采用分代回收的策略
内存分为新生代和老生代两种
针对不同对象采用不同的算法

在这里插入图片描述

V8中常用的GC算法
分代回收
空间赋值
标记清除
标记整理
标记增量

回收新生代对象

在这里插入图片描述

V8内部空间一分为二(小空间存放新生代,64位操作系统存放32M,32位操作系统存放16M)
左边的是新生代对象,右边的是老生代对象
新生代对象指的是存活时间较短的对象(比如说一些简单的局部变量,方法执行完成后就会被回收,全局对象等退出当前应用才会被回收)

回收过程采用复制算法和标记整理
新生代内存去分为两个等大的空间
使用空间为from,空闲空间为To
活动对象分配至from空间,
标记整理后将活动对象拷贝到To
from与To交换空间完成释放

from ABCD ->拷贝到 To编程ACDB被释放,两者交换后from ACD

回收细节:拷贝的过程中可能会出现晋升
晋升就是讲新生代对象移动到老生代对象中去
一轮GC还存获得新生代需要晋升
To空间的使用率达到25%

回收老年代对象

老年代对象存放在右侧区域内部
内存空间:64位操作系统1.4G,32位操作系统0.7G
老年代对象指的是存活时间较久的对象
主要采用标记清除、标记整理和增量标记算法
首先使用标记清除完成垃圾空间的回收
采用标记整理进行空间优化
采用增量标记进行效率优化

细节对比是新生代是空间换时间(每时每刻都会有空间)
老年代空间比较大,采用空间换时间第一是浪费大部分空间,第二是消耗大量时间。所以不适合复制算法
在这里插入图片描述
垃圾回收时会阻塞我们代码的执行,所以会有一个空档期
增量标记是将当前垃圾回收拆分成多个小部分,组合完成当前的垃圾回收
实现垃圾回收与程序执行交替执行

performance

GC使用目的是为了实现内存空间的良性循环
然而良性循环的基石时合理使用
时刻关注内存是否合理使用
Performance提供多种监控方式
通过performance进行浏览器的实时监控

内存问题的体现

外在体现

页面出现了延迟加载或经常性暂停(浏览器内存出现了问题,与浏览器频繁的进行垃圾回收操作有关,代码有问题)
页面出现了持续的糟糕表现(存在内存膨胀,当前界面为了达到最大的使用速度,去申请一定的内存空间,但是申请的空间超过了我们浏览器所能承受的范围)
页面的性能随着时间延长越来越差,伴随着内存泄漏

监控内存的方式

界定内存问题的标准

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

监控内存的几种方式

浏览器任务管理器,可以监控内存的实时变化
Timeline时序图记录
堆块照查找分离DOM
判断是否存在频繁的垃圾回收

任务管理器监控内存

shift+esc调出浏览器自带的任务管理器右键选中js内存
在这里插入图片描述

TimeLine记录内存变化

performance(性能)进行对应时间的录制

堆快照查找分离Dom

什么是分离DOM
界面元素存活在DOM树上
垃圾对象时的DOM节点
分离状态的DOM节点
浏览器内存Memory
Heap snapshot
搜索deta,查找是否存在分离dom

判断是否存在频繁GC

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

TimeLine中频繁的上升下降
任务管理器中数据频繁的增加减少

JS代码优化

慎用全局变量

全局变量定义在全局上下文是所有作用域链的顶端
全局上下文一直存在于上下文执行栈,直到退出
局部作用域定义了局部变量,会造成命名污染
可以用 jsperf 检查脚本的运行效率

缓存全局变量

let obj = document

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

原型对象新增方法

var f1 = function(){
	this.foo = function(){
		console.log(111)
	}
}
f1 =new f1()

var f2 = function(){}
f2.prototype.foo = function(){
	console.log(222)
} 
f2 =new f2()

避开闭包陷阱

闭包的特点:
外部具有指向内部的引用
在外部作用域可以访问内部作用域的数据

function fn(){
	let name = 'aasa'
	function f(){
		console.log(name)
	}
	return f()
}
var f1 = fn()
console.log(f1)

闭包是一种强大实用的语法
使用不当会造成内存泄漏

<button id ='btn'>add</button>
<script>
	function fn(){
		let el = document.getElementBtId('btn')
		el.onclick = function(){
			console.log(el.id)
		}
	}	
	function fn(){
		let el = document.getElementBtId('btn')
		el.onclick = function(){
			console.log(el.id)
		}
		el = null
	}	
	fn()
</script>

避免属性访问方法使用

JS的面向对象
js不需要属性的访问方法,所有属性都是外部可见的
属性访问方法只会增加一层重定义,没有访问的控制力

function Person(){
	this.name = 'p'
	this.age = 20
	this.getAge = function() {//定义一个成员属性返回age 
		return this.age 
	}
} 
const p1 = new Person()
const a = p1.getAge()

function Person1(){
	this.name = 'p'
	this.age = 20
} 
const p2 = new Person1()
const a1 = p2.age

for循环优化

let arr = [1,3,45,3,12]
for(var i = 0 ; i < arr.length ; i++ ) {
	console.log(i)
}
for(var i = 0 ,len = arr.length; i <  len; i++ ) {
	console.log(i)
}

循环方式对比

let arr = [1,3,45,3,12]
arr.forEach(i => {//效果更优
	console.log(i)
})
for(var i = arr.length; i ; i-- ) {
	console.log(i)
}
for(var i in arr) {
	console.log(i)
}

文档碎片优化节点添加

<script>
for(var i = 0;i<10;i++){
	let op = document.createElement(p)
	op.innerHtml = i
	document.body.appendChild(op)//这样导致整个body频繁的操作dom导致reflow
}
//优化,先创建一个容器,将操作的dom放进去,最终在对body进行操作
let frage = document.createDocumentFragment()
for(var i = 0;i<10;i++){
	let op = document.createElement(p)
	op.innerHtml = i
	frage.appendChild(op)
}	
document.body.appendChild(frage)
</script>

克隆优化节点操作

<script>
<p id="sp"></p>
for(var i = 0;i<10;i++){
	let op = document.createElement(p)
	op.innerHtml = i
	document.body.appendChild(op)//这样导致整个body频繁的操作dom导致reflow
}
let oldP= document.getElementById("sp")
for(var i = 0;i<10;i++){
	let op = oldP.clone(flase)//克隆相应的节点
	op.innerHtml = i//
	document.body.appendChild(op)
}	
</script>

直接量去替换object

var arr = new Array(3)
arr[0] = 1
arr[1] = 2
arr[2] = 3

var arr = [1, 2, 3]

减少判断层级

function doSomthing(part, chapter){
	const charts = ['1', '2', '3', '4', '5', '6']
	if(part){
		if(charts.includes(part)){
			console.log('当前数据~')
			if(chapter>3){
				console.log('请输入数据~')
			}
		}
	}else{
		console.log('请输入有效信息~')
	}
}
doSomthing('2', 4)
function doSomthing(part, chapter){
	const charts = ['1', '2', '3', '4', '5', '6']
	if(!part){
		console.log('请输入有效信息~')
		return
	}
	if(!charts.includes(part)){
		return
	}
	console.log('当前数据~')
	if(chapter>3){
		console.log('请输入数据~')
	}
}

减少作用域链查找层级

var name = 'baz'
function foo(){
	name = 'zec'
	function fn(){
		var age = 30
		console.log(age)
		console.log(name)
	}
	fn()
}
foo()
function foo(){
	var name = 'zec'
	function fn(){
		var age = 30
		console.log(age)
		console.log(name)
	}
	fn()
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值