大前端学习1-2__JavaScript性能优化

javaScript 性能优化

    • javaScript 性能优化介绍
    • javascript 内存管理
      • 1. 内存为什么需要管理
      • 2. 什么是内存管理
      • 3. 内存空间生命周期的演示
    • javascript 中的垃圾回收
      • 垃圾标准:
      • 涉及定义可达对象和根
      • 代码解释 javascript 中的引用与可达
        • 引用
        • 可达
    • GC 算法
      • GC 定义于作用
      • GC 里的垃圾是什么?
      • GC 算法是什么?
      • 一. 引用计数算法
        • 1. 引用计数算法的原理
        • 2. 引用计数算法优缺点
      • 二. 标记清除算法
        • 1. 标记清除算法实现原理
        • 2. 标记清除算法优缺点
      • 三. 标记整理算法
        • 1. 标记整理算法实现原理
      • 常见 GC 算法的总结
    • V8 引擎
      • 1. 认识 V8 引擎
      • 2. V8 的垃圾回收策略
        • 1. 新生代空间如何优化垃圾回收
          • 新生代对象回收实现
        • 2. 老生代空间如何优化垃圾回收
          • 老生对对象回收实现
          • 老生代垃圾回收和新生代垃圾回收的细节对比
        • 3. 标记增量如何优化垃圾回收
    • Performance 工具
    • 内存问题的体现
    • 内存监控的方式
      • 常见内存监控的方式
      • 界定内存问题的标准
      • 一. 任务管理器监控内存
      • 二. TimeLine 记录内存
      • 三. 堆快照查找分离 DOM
    • 是否存在频繁的 GC 算法
    • Performance 使用总结
    • 代码优化
      • 1. 如何精准测试 javascript 性能
      • 2. 慎用全局变量
      • 3. 缓存全局变量
      • 4. 通过原型对象添加附加方法
      • 5. 避开闭包陷阱
      • 6. 避免属性访问方法使用
      • 7. For 循环优化
      • 8. 采用最优的循环方式
      • 9. 节点添加优化
      • 10. 直接量替换 Object 操作
      • 11.JSBench使用
      • 12. 堆栈中代码执行流程
      • 13. 减少判断层级
      • 14. 减少作用域链查找层级
      • 15. 减少数据读取次数
      • 16. 字面量与构造式
      • 17. 减少循环体活动
      • 18. 减少声明以及与语句数
      • 19 . 惰性函数
      • 20.采用事件委托

javaScript 性能优化介绍

  • 性能优化不可避免
  • 提高运行效率,降低运行开销的行为都可以看做为性能优化。
  • 在前端中性能优化是无处不在,包括在请求资源时的网络行为,数据传输,开发框架都可以进行优化。

本次学习为 javascript 语言本身的优化

内容概要 :

  1. 内存管理基本流程
  2. 垃圾回收与常见的 GC 算法
  3. V8 引擎的垃圾回收机制
  4. Performance 工具监控内存
  5. 代码优化实例

javascript 内存管理

  • 高级编程语言中都带有 GC 机制。

1. 内存为什么需要管理

function fn () {
    arrList = []
    arrList[10000] = 'lg is a coder'
}
fn()
选择比较大的下标是为了申请一块比较大的内存,代码在运行fn时内存占用是逐渐层高而未回落, 说明这个函数存在内存泄漏,而在代码中内存泄漏的代码多了会形成意向不到的bug, 所以内存管理是很有必要的。

2. 什么是内存管理

内存 : 由可读写单元组成,表示一片可以操作空间。
管理: 人为的去操作一片空间的申请 使用和释放。
内存管理 : 开发者主动去申请空间 使用空间 和释放空间。
管理流程: 申请 -> 使用 -> 释放

javascript 中的内存管理主要依靠垃圾回收机制,因为 ECMAScript 中并没有提供 API 来主动操作管理流程。

3. 内存空间生命周期的演示

// 定义一个变量来申请内存空间
let obj = {}
// 使用内存空间
obj.name = "lg"
// 释放内存空间
obj.name = null

javascript 中的垃圾回收

javascript 中内存管理是自动的,创建变量会自动分配内存空间

垃圾标准:

  • 对象不再被引用时此时可以看做是垃圾会被垃圾回收机制回收
  • 对象不能从根上访问到时是垃圾

涉及定义可达对象和根

  • 可达对象 : 可以访问到的对象就是可达对象可以通过引用或者当前作用域链,
    可达的标准是判断是否可以从根上找到。

  • 根 : javascript 中的根就可以理解为全局变量对象或者全局执行上下文。

垃圾回收时先找到垃圾然后通过 javascript 引擎进行释放和回收以及再分配。

代码解释 javascript 中的引用与可达

引用

let obj = { name: 'xiaoming' }  // 这块空间是小明的空间 obj是根上可达对象

let alice = obj  // 对象obj新增一次引用

obj = null  // 虽然obj的值为null 但是小明的空间仍然是可达的, 因为alice仍然在引用

可达

function objGroup(obj1, obj2) {
    obj1.next = obj2
    obj2.prev = obj1
    return {
        o1: obj1,
        o2: obj2
    }
}

let obj = objGroup({
    name: 'obj1'
}, {
    name: 'obj2'
})

console.log(obj)
// 打印为
{
    o1: { name: 'obj1', next: { name: 'obj2', prev: [Circular] } },
    o2: { name: 'obj2', prev: { name: 'obj1', next: [Circular] } }
}

GC 算法

GC 定义于作用

  • 定义:GC 即为垃圾回收机制的简写
  • 作用:GC 可以找到内存中的垃圾 并释放和回收空间

GC 里的垃圾是什么?

  1. 程序中不在需要使用的对象(程序需求)
function fn() {
     name = 'lg'
    return `${name} is a coder`
}

fn()
  1. 程序中不能再访问到的对象
function fn() {
    const name = 'lg'
    return `${name} is a coder`
}

GC 算法是什么?

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

常见的 GC 算法 引用计算 标记清除 标记整理 分代回收。

一. 引用计数算法

1. 引用计数算法的原理

  • 核心思想 : 设置引用数 判断当前引用数是否为 0 来判断是否为垃圾对象。
  • 引用计数器 : 用来统计对象被引用次数的一个数值。

引用关系改变时修改引用数值 有对该对象的引用时引用数值加 1 否则则减 1,知道引用数字为 0 , 引用数字为 0 时会被立刻回收。

// reference count

const user1 = {
age: '1'
}
const user2 = {
age: '2'
}
const user3 = {
age: '3'
}

const nameLsit = [user1.age, user2.age, user3.age] // 因为数组仍然引用这上面三个对象所以上面三个对象不会被当做垃圾进行回收

function fn() {
// num1 = 1
// num2 = 2
const num1 = 1
const num2 = 2 // 前面加了 const 函数 fn 调用了之后外部无法访问 内部两个变量 所以会直接回收
}
fn()

2. 引用计数算法优缺点

  • 优点

    1. 发现垃圾时立即回收。
    2. 最大限度减少程序暂停 : 程序运行会消耗内存, 如果内存沾满程序会暂停而计数算法可以立即回收垃圾对象。
  • 缺点

    1. 无法回收循环引用的对象。
    2. 时间消耗更大 数值修改需要时间 而且监听也需要时间。
  1. 循环引用的对象
function fn() {
const obj1 = {}
const obj2 = {}
// obj1 和 obj2 fn 执行结束后在根上已经找不到这两个对象了 , 但是也没有被回收因为下面代码仍对它们有引用而垃圾回收机制是没办法进行回收 obj1 和 obj2 的因为它们处在循环引用中。
obj1.name = obj2
obj2.name = obj1
    return 'ok'
}
fn()

二. 标记清除算法

1. 标记清除算法实现原理

  • 核心思想 : 分标记和清除两个阶段。
    1. 遍历所有对象标记活动对象(活动对象和可达对象一样)。
    2. 遍历所有对象清除没有标记对象以及把已经标记的清除标记。
    3. 回收相应的空间, 把空闲的空间放进空闲列表中方便下一次的分配空间。

2. 标记清除算法优缺点

  • 优点

    1. 解决对象循环引用的回收操作
  • 缺点

    1. 标记清除算法回收的空间易有地址不连续, 对后续申请空间并使用并不友好也容易造成内存浪费。

空间碎片化: 当前回收的垃圾对象在地址上时不连续的, 由不连续所以空闲空间会分散, 如果新申请的空间和碎片化空间匹配
自然好,如果不匹配就不适合使用了。

三. 标记整理算法

1. 标记整理算法实现原理

  • 标记整理可以看到是标记清除的增强
  • 标记阶段操作和标记清除一致
  • 清除会先执行整理,移动对象,就是把所有的活动对象标记后整理进行移动,可以最大限度的对回收后的空闲空间进行利用以及优化标记清除算法的空间碎片化。

常见 GC 算法的总结

  • 引用计数:通过引用计数器来维护每个对象的引用计数,通过判断引用计数是否为 0 来判断这个对象是否是垃圾对象。如果为垃圾对象就会立刻进行回收以及释放空间,为再一次分配空间做准备。
  • 标记清除:遍历所有对象,对活动对象进行标记,然后对活动对象进行清除 存在问题是容易造成空间碎片化。
  • 标记整理: 遍历所有对象,标记活动对象,对活动对象进行整理排序。进行清除 避免了空间随便化。

V8 引擎

1. 认识 V8 引擎

  • V8 是一款主流的 javascript 执行引擎 chrome 以及 node 都在使用 V8 引擎
  • V8 可以高效执行 javascript
  • V8 采用即时编译 速度和快
  • V8 内存是设限的 64 位系统 1.5G 32 位系统 800M

设限原因:

  • V8 主要服务于网页的, 当前设置限制大小对网页应用来说足够了 。
  • V8 的垃圾回收机制也决定了设置内存限制是合理的当 内存消耗达到 1.5G 假设使用增量标记的算法回收也只需要 50ms,如果使用非增量标记算法需要 1s。

2. V8 的垃圾回收策略

  • 采用分代回收的思想 ,内存分为新生代和老生代 ,从而针对不同对象采用不同算法。

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

1. 新生代空间如何优化垃圾回收

  • V8 内存分配为一分为二。
  • 小空间用于存储新生代对象 (64 位操作系统 32M| 32 位操作系统 16M)。
  • 新生代是指相对存活时间较短的对象 例如局部作用域内的变量 函数执行结束就会被回收而全局的变量则要等程序退出才会进行回收。
新生代对象回收实现
  • 回收采用复制算法 + 标记整理算法。

  • 新生代内存区分为两个等大的空间, 使用空间为 From 空闲空间为 To, 将活动对象存储在 From 空间中, 当 From 空间达到定饱和度的时候就会进行标记整理。

  • 将标记整理后的活动对象拷贝至 To 空间, 然后开始回收,回收的体现就是 From 空间和 To 空间的交换完成释放。

  • 回收细节说明:

    • 拷贝过程中可能出现晋升 : 就是在拷贝的过程中发现一个对象 在老生代中也存在 ,就会出现一个晋升的操作,也就是把新生代的对象移动至老生代进行村存储。

    • 触发晋升操作场景:

        1. 一轮 GC 还存活的新生代需要晋升。
        1. To 空间的使用率超过 25 %。

注: 如果 To 空间中的使用率超过 80 % 后 From 空间和 To 空间交换之后 新的活动对象可能就存不进当前的 From 对象中了。

2. 老生代空间如何优化垃圾回收

  • 老生代对象说明

    1. 老生代对象放在右侧老生代区域,同样对内存大小由限制,64 位操作系统位 1.4G, 32 位操作系统为 700M。
    2. 老生代对象就是存活时间较长的对象 例如 闭包内的变量和全局变量。
老生对对象回收实现
  1. 主要采用标记清除,标记整理,增量标记算法。
  2. 首先使用标记清除完成垃圾空间的回收,回收后会产生一些空间碎片化的问题。
  3. 新生代的晋升会触发标记整理, 整理标记清除回收后空间碎片化的空间, 达到空间优化以存入新生代晋升的对象。
  4. 之后会采用增量标记进行效率优化。
老生代垃圾回收和新生代垃圾回收的细节对比
  1. 新生代区域垃圾会后使用空间交换时间, 新生代采用空间交替的方法,所以实时都会有空闲的空间,造成空间浪费

  2. 老生代区域垃圾回收不适合赋值算法, 老生代中的对象比较多,而且老生代空间比新生代要大的多,如果实时都有空闲的太过浪费

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

  • 垃圾回收会阻塞 javascript 执行, 程序完成后停下来进行回收垃圾,而标记增量可以使垃圾回收和程序执行交替进行,而不必像之前那样程序执行不做垃圾回收,垃圾回收时不做程序执行,这样带来的时间消耗更为合理。

Performance 工具

为什么使用 Performance 工具 ?
GC 算法的目的就是为了实现内存精简的良性循环,良性循环的基石就是合适使用内存空间, 但是 javascript 并没有提供这系列的 API 来进行操作。我们想去判断内存使用是否合理,需要时刻关注内存使用的变化,而 Performance 这个工具就可以让我们可以时刻关注到内存使用的变化,以便于定位由内存使用有问题的代码块。

  • Performance 使用步骤
  1. 打开浏览器输入目标地址
  2. 进入开发人员工具面板 选择性能
  3. 开启录制功能 访问具体界面
  4. 执行用户行为 一段时间后停止录制
  5. 分析界面中记录的内存信息
  • chrome 浏览器
    步骤
  1. 打开开发者工具 点击 Performance
  2. 点击圆点 开始一个新的录制
  3. 发起请求模拟用户操作后结束录制
  4. 勾选 Screenshots 和 memory 查看内存使用的线图分析内存使用问题

内存问题的体现

配合 Performance 定位问题

  1. 页面出现延迟加载或者经常性的暂停(网络环境正常且频繁操作 GC 的垃圾回收) :意味着代码中有些代码瞬间让内存爆掉了,触发了 GC 垃圾回收机制。

  2. 页面持续性出现糟糕的性能(底层出现内存膨胀)。

内存膨胀:当前界面为了达到最佳使用速度去申请内存空间 ,申请的空间大小超过了设备提供的大小。

  1. 页面性能随时间延长越来越差 (代码出现内存泄漏)。

内存监控的方式

常见内存监控的方式

  • 浏览器任务管理器 可以以数值来体现当前页面运行时内存的变化
  • Timeline 时序图记录 可以把内存的走势以时间来记录
  • 浏览器中有堆快照查找分离 DOM 分离 DOM 其实就是一种内存泄漏

界定内存问题的标准

  1. 内存泄漏: 内存使用持续升高 可以拿到内存走势图可以从走势图中看到

  2. 内存膨胀: 在多数设备上都存在性能问题 有可能是当前设备不具备申请空间的条件 应该多更换其他设备进行测试 如果多设备上都存在性能问题,则为内存膨胀

  3. 频繁垃圾回收: 通过内存变化图进行分析 界面无法感知

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

一. 任务管理器监控内存

  • shift + esc 打开任务管理器
    
    

大前端学习1-2__JavaScript性能优化_第1张图片

  • 页面介绍 : 我们比较关注的是内存和 javascript 内存 , 内存是指本来 dom 节点的内存 ,如果内存的值不算增加表示一直在创建新的 dom 节点, javascript 内存指的是 js 的堆我们要看的是小括号中的值。小括号中的值是当前页面所有可达对象正在使用的内存值 , 如果这个值一直增大表示当前页面的内存是有问题的, 但是具体问题出现在哪里,任务管理器是不能帮助我们看到的 。

二. TimeLine 记录内存

  • 作用 :具体定位内存出现问题的时间和代码

  • 模拟场景:创建大量 dom 节点模拟内存消耗和数组遍历存入大量数据

    
    

大前端学习1-2__JavaScript性能优化_第2张图片

  • 根据 TimeLine 的图标分析可以看到内存何时增加何时回落 , 以及内存使用是否正常可以定位到有问题的代码.

三. 堆快照查找分离 DOM

什么是分离 DOM

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

找到 js 堆产生照片的留存,对分离 dom 的查找行为。

    
    

大前端学习1-2__JavaScript性能优化_第3张图片

操作: 打开浏览器中的 memory 点击 take snapshot 拍摄当前的堆快照, 在新增 dom 后拍摄新增后的堆快照, 新增后的堆快照可以通过搜索 deta 确认是否存在分离 dom

是否存在频繁的 GC 算法

如何确定频繁垃圾回收?
GC 工作时应用程序会停止,频繁且过长的 GC 会导致应用假死,用户使用中感知应用卡顿。

  1. TimeLine 中频繁的上升下降
  2. 任务管理器中数据频繁的增加减小

Performance 使用总结

  • Performance 的使用流程
    • 内存问题的相关分析,内存泄漏,内存膨胀,频繁的 GC 操作。
    • Performance 时序图监控内存变化,监控内存是否是一直上升没有下落。
    • 任务管理器监控内存变化,从 javascript 的数值来推算内存变化是否正常。
    • 堆快照查找分离 DOM,分离 DOM 必然存在内存泄漏 ,通过堆快照来定位问题代码。

代码优化

1. 如何精准测试 javascript 性能

  • 本质上就是采集大量执行样本进行数学统计和分析,而对于写代码的我们可能并不擅长做大量的数据统计和分析,我们可以使用基于 Benchmark.js 的https://jsperf.com/完成,它是一个在线的js脚本的性能测试网站。

jsperf 的使用流程

  • 使用 GitHub 账号登录
  • 填写个人信息(非必须)
  • 填写详细的测试用例信息( title slug)
  • 填写准备代码(DOM 操作时经常使用)
  • 填写必要有 setUp 与 teardown 代码 setUp 理解为操作前准备工作 ,teardown 就是用完后的销毁动作
  • 填写测试代码片段

2. 慎用全局变量

程序执行过程中如果有些数据需要存储尽量使用局部作用域中变量进行存储。

为什么要慎用?

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

for (let i = 0; i < 1000; i++) {
let str = ''
str += i
}

大前端学习1-2__JavaScript性能优化_第4张图片
大前端学习1-2__JavaScript性能优化_第5张图片

使用 jsperf 测试哪一段代码的执行速度更高
结果局部变量的代码执行速度远高于全局变量

3. 缓存全局变量

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

使用两种方法写入代码使用 jsperf 去测试两端代码的运行速度。

    
    
    
    
    

11111

22222

33333

大前端学习1-2__JavaScript性能优化_第6张图片
大前端学习1-2__JavaScript性能优化_第7张图片

结果是使用局部缓存全局变量的运行速度更快。

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

javascript 中有构造函数,原型对象,实例对象三种概念。

通过原型对象添加附加方法:如果某实例对象需要频繁使用一个方法可以在原型对象上新增实例对象需要的方法

方法挂载到原型上和普通构造函数上的运行速度比较

var fn1 = function () {
    this.too = function () {
        console.log('111');
    }
}

let f1 = new fn1()

var fn2 = function () {
    fn2.prototype.foo = function () {
        console.log('1111');
    }
}

大前端学习1-2__JavaScript性能优化_第8张图片

5. 避开闭包陷阱

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

function foo() {
var name = 'zs'

    function fn() {
        console.log(name);
    }
    return fn

}
var a = foo()
a()

关于闭包

  • 闭包是一种强大的语法
  • 闭包使用不当容易造成内存泄漏
  • 不要为了闭包而闭包
    
    

6. 避免属性访问方法使用

js 不需属性的访问方法, 所有属性都是外部可见的

使用属性访问方法只会增加一层重定义, 没有访问的控制力。
大前端学习1-2__JavaScript性能优化_第9张图片

属性访问方法的运行速度并不入直接访问属性来的快

7. For 循环优化

for 循环中优化为把数组的 length 用变量声明使用 jsperf 来看下两个执行速度.

    

add

add

add

add

add

add

add

add

add

add

add

大前端学习1-2__JavaScript性能优化_第10张图片

结果存储 length 速度要快于普通的 for 循环。

8. 采用最优的循环方式

如何判断最优循环方式:大量数据遍历,遍历的方法有很多.

可以通过一段逻辑相同的代码使用不同的遍历方法来看下哪个循环方式是最优的

var arrList = new Array([1,2,3,4,5,6,7])

arrList.forEach((item) => {
console.log(item);
})

for (var index = arrList.length; index; index--) {
console.log(arrList[index]);
}

for (var index in arrList) {
console.log(arrList[index]);
}

大前端学习1-2__JavaScript性能优化_第11张图片

forEach for for…in 进行对比 foreach 性能最高 其次是 for 再然后是 for in。

9. 节点添加优化

操作 dom 是非常耗性能的,这个过程必然会有回流和重绘

    

大前端学习1-2__JavaScript性能优化_第12张图片

10. 直接量替换 Object 操作

// 第一种方式直接量
    var a = [1, 2, 3]
// 第二种new创建数组根据下标写入
    var a1 = new Array(3)
    a1[0] = 1
    a1[1] = 2
    a1[2] = 3

大前端学习1-2__JavaScript性能优化_第13张图片

直接量的速度或者说执行效率要优先。

11.JSBench使用

  • JSBench : 在线测试js代码执行效率的网站 , 网站地址https://jsbench.me/。

为什么学习JSBench使用?

  1. jsperf目前已经停止维护
  2. 有些关于js性能的内容需要分享

JSBench页面介绍:
1. SetUp是指比较代码前准备工作,比如有html和js 把代码放进相应的SetUpHTML和SetUpJS当中
2. TestCase里面放要比较的js代码 enter test case name 放标题
3. TeardownJS中放入类似共同调用的函数方法之类的公共的比较js代码后续处理工作的js
4. Output(DOM)里面进行dom操作一般是不需要使用的, 下面是有RUN按钮用来开始执行比较的js代码。
大前端学习1-2__JavaScript性能优化_第14张图片

注意点:

  1. 建议使用JSBench时只打开一个标签,打开其他标签会抢占资源可能会影响代码执行速度
  2. 执行代码测试的时候建议页面停留在当前,不要切换因为浏览器是有挂起功能的,也可能会影响比较执行速度。
  3. 尽可能多执行几次脚本去多数结果为准,一次执行可能会受到各种情况影响并不一定准确
  4. 一段代码的执行速度快并不代表代码的健壮性就好,不要太重视执行速度。

12. 堆栈中代码执行流程

let a = 10

function foo(b) {
    let a = 2

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

上段代码执行过程:

  1. 开始执行这段代码会先创建一个ECStack 执行环境栈
  2. 初始化全局执行上下文就是在栈中划分一块内存
VO:
    存储 a = 10 
    foo = AB1[[scope]] VO
    fn = AB2
    fn(3)
  1. foo函数需要再堆中划分一块地址为AB1
foo = AB1 [[scope]] VO
     AB1中:
     function foo (b) { ... }
     name: foo
     length:1

  1. foo中需要划分一个EC(foo)
this = window <
    foo.ao, vo >
    AO:
    Arguments: [0, 2]
b = 2
a = 2
  1. baz函数需要在队中划分一块内存
baz = AB2 [[scope]] foo.AO
     function baz(c) { ... }
     name: baz
     length:1
  1. baz中的代码需要划分一个EC
this = window
    < baz.ao, foo.ao, vo >
    AO:
    /     Arguments: [0: 3]
// c = 3
console.log(a + b + c)
3+2+2 ===> 7

13. 减少判断层级

表现为使用return无效值来优化

function doSomething(part, chapter) {
    const parts = ['ES2016', '工程化', 'Vue', '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 = ['ES2016', '工程化', 'Vue', 'React', 'Node']
    if (!part) {
        console.log('请确认模块信息');
        return
    }
    if (!parts.includes(part)) return
    console.log('属于当前课程');
    if (chapter > 5) {
        console.log('您需要提供VIP身份');
    }
}

doSomething('ES2016', 6)

大前端学习1-2__JavaScript性能优化_第15张图片

14. 减少作用域链查找层级

关注为什么减少层级以及减少层级怎么达到优化代码的 ?

// 关注内存的情况下 这种情况会更佳
var name = 'zce'

function foo () { 
     name = 'zce666' // 此时name是在修改了全局name的值
    function baz() { 
        var age = 38
        console.log(age);
        console.log(name);
    }
    baz()
}

foo()

// 优化后的

var name = 'zce'

function foo () { 
    var name = 'zce666' // 新增了var之后等于在foo函数中新增了name变量 在执行console.log(name);时找baz函数找不到就向上层找 找到了,直接打印, 同时不影响全局的值。
    function baz() { 
        var age = 38
        console.log(age);
        console.log(name);
    }
    baz()
}

foo()

大前端学习1-2__JavaScript性能优化_第16张图片

  1. 从执行速度来看优化后的比较快
  2. 从内存占用情况下来看 第一种情况更优
  3. 从垃圾回收的情况看 第二种优化后的foo函数没有其他占用, 执行过后就会被回收了.从内存占用情况下来看。

从不同的角度来说各有各的优势在自己的项目中需要取舍

函数执行中会产生一个执行上下文 多次调用同一个函数会创造多个执行上下文,在执行函数过程中会先在自己的执行上下文中去找需要的变量, 如果没有找到则找上一级。

15. 减少数据读取次数

关注为什么要去减少数据读取次数,以及减少数据读取次数后对性能的影响?

  • 减少数据读取次数即为尽可能减少对象成员的查找次数以及嵌套层级和嵌套深度。

  • 方法就是提前缓存对象数据

  • 需求: 进入一般都有一个广告的欢迎页页面, 上面有跳过按钮, 判断当前是否有跳过按钮, 如果没有进行其他操作

 

    

大前端学习1-2__JavaScript性能优化_第22张图片
大前端学习1-2__JavaScript性能优化_第23张图片

对比结果为优化后执行速度更快。

19 . 惰性函数

惰性函数被看为是高阶的语法 反而执行速度没有更快

 var btn = document.getElementById('btn')
        function foo() {
            console.log(this);   // 
        }


        function addEvent(obj, type, fn) {
            if (obj.addEventLisener) {
                obj.addEventLisener(type, fn, false)

            } else if (obj.attachEvent) {
                obj.attachEvent('on' + type, fn)
            } else {
                obj['on' + type] = fn
            }
        }

        //    如果调用次数多的情况下每次进来都要做很多的判断,每次判断条件很多对性能有影响

        // 优化后

        function addEvent(obj, type, fn) {
            if (obj.addEventLisener) {
                addEvent = obj.addEventLisener(type, fn, false)

            } else if (obj.attachEvent) {
                addEvent = obj.attachEvent('on' + type, fn)
            } else {
                addEvent = obj['on' + type] = fn
            }
            return addEvent
        }
        addEvent(btn, 'click', foo)

        // 打印结果相同
        // 但是如果只调用一次 优化前速度更快 
        // addEvent(btn, 'click', foo)
        // addEvent(btn, 'click', foo)
        // addEvent(btn, 'click', foo)

大前端学习1-2__JavaScript性能优化_第24张图片

20.采用事件委托

事件委托的本质就是利用js的冒泡机制把本来需要给子元素注册的事件委托给父元素来完成事件监听,
优点:

  1. 减少内存占用 减少事件注册

考点一般就是给一堆li不考虑内存占用给每个li都绑定事件达到性能最优

	 
  • ZCE
  • 28
  • 世界很大想去看看

你可能感兴趣的:(大前端学习,javascript,js)