前端知识点——性能优化

写在前面

笔记内容大多出自于拉勾教育大前端高薪训练营的教程,因此也许会和其他文章雷同较多,请担待。

JS内存管理

  • 内存 -> 由可读写单元组成,表示一片可操作空间
  • 管理 -> 人为地去操作一片空间的申请、使用、释放
  • 内存管理 -> 开发者主动申请空间、使用空间、释放空间
// 申请内存空间
let obj = {}
// 使用内存空间
obj.name = 'aeorus'
// 释放内存空间
obj = null

引用计数算法

  • 设置引用数,判断当前引用数是否为0
  • 引用关系改变时修改引用数字
  • 引用数字为0时,立马回收垃圾
  • 优点 -> 发现垃圾时立即回收 / 最大限度减少程序暂停
  • 缺点 -> 无法回收循环引用的对象 / 时间开销大(需要时刻监控,而且当修改的值比较多时,那需要的时间也更长)

标记清除算法

  • 分标记和清除两个阶段完成
  • 遍历所有对象寻找活动对象进行标记(可达对象)
  • 遍历所有对象清除没有标记的对象(抹掉第一次遍历打上的标记)
  • 回收相应的空间
  • 优点 -> 可以解决引用计数算法循环引用的对象不能被回收的问题
  • 缺点 -> 空间碎片化

标记整理算法

  • 可以看做标记清除算法的增强
  • 标记阶段的操作和标记清楚算法一致
  • 清除阶段会先执行整理,移动对象位置,让它们在空间链表上的位置是连续的,不会产生碎片化问题

V8

  • 主流的JavaScript执行引擎
  • 采用即时编译
  • V8内存设限 -> 64位1.5G 32位800M

V8垃圾回收策略

  • 分代回收思想
  • 内存分为新生代、老生代
  • 针对不同对象采用不同算法
  • 新生代对象储存 -> 采用具体的GC算法
  • 老生代对象储存 -> 采用具体的算法
  • 分代回收 / 空间复制 / 标记清除 / 标记整理 / 标记增量

因为V8的内存是有上限的,所以要采用分代回收思想,不同的对象采用更加适合的算法,以此达到更加高效的垃圾回收机制

新生代对象 -> 存活时间较短的对象(例如函数内定义的变量)

  • 内存64位32M 32位16M
  • 使用空间From,空闲空间To
  • 活动对象存储于From空间
  • 标记整理后将活动对象拷贝至To
  • 释放From
  • From和To交换空间

老生代对象 -> 存活时间较长的对象(闭包或者全局定义的变量)

  • 内存64位1.4G 32位700M
  • 主要采用标记清除 / 标记整理 / 增量标记算法
  • 首先使用标记清除完成垃圾空间的回收
  • 采用标记整理进行空间优化(新生代晋升老生代时)
  • 采用增量标记进行效率优化

内存问题的表现

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

监控内存

内存泄漏 -> 内存使用持续升高

内存膨胀 -> 在多数设备上都存在性能问题

频繁垃圾回收 -> 通过内存变化图进行分析

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

V8引擎工作流程

渲染引擎 ---(utf-8 chunks)--->
Stream ---(utf-16 code utils)--->
Scanner ---(tokens)--->
(PreParser) -> Parser ---(AST)--->
lgnition ---(bytecode)--->
TurboFan

防抖和节流

防抖 -> 持续触发事件,不停止触发就不执行

  • 使用setTimeout定时器,一直调用fn,一直清除定时器直到清除完后不再调用fn时才能真正激活定时器内的fn
  • 因为从调用fn变成了调用debounce,所以this的指向也会改变,所以需要使用bind改回最初的this
function debounce(fn, delay) {
  let timer = null
  return function() {
    if (!timer) {
      clearTimeout(timer)
    } else {
      timer = setTimeout(fn.bind(this, ...arguments), delay)
    }
  }
}

节流 -> 持续触发事件,每隔一段时间,只执行一次事件

  • 使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 )
  • 如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行
function throttle(fn, delay) {
  let timer = null
  return function() {
    if (timer !== null) return
    timer = setTimeout(() => {
      timer = null
      fn.call(this, ...arguments)
    })
  }
}
function throttle(fn, delay) {
  let prev = 0
  return function() {
    const now = +new Date()
    if (now - prev >= delay) {
      prev = now
      fn.call(this, ...arguments)
    }
  }
}

减少判断层级

减少循环体活动

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值