三、ECAMScript新特性、Typescript、性能优化、v8垃圾回收机制(笔记)

一、ECAMScript新特性

1.let、const

1)块级作用域
2)不能进行变量提升

const定义常量,一开始就要有初始值,不能修改

const obj = {}
obj.name = ‘hhh’

上述并没有修改obj的内存地址,所以是可以修改对象中的属性的。

2.数组的解构

3.对象的解构

4.模板字符串

  • 可以直接换行
  • 可以插入值
  • 通过 \ 转译
const str = `name is \`hahha\``

// 输出
name is `hahha`

5.对象字面量的增强

// 对象的增强
const name = 5
const obj = {
  a: 5, 
  name, // 同名缩写
  foo() {}, // 相当于foo: function() {},这是缩写形式 
  [name]: 'yang', // 计算属性名[]会把里面的值计算出来
}
console.log(obj)

6.Proxy

代理,就是实现对 对象 的一个“拦截操作”
Proxy和Object.defineProperty的对比:
1)Proxy代理功能远比Object.defineProperty强大,Object只能拦截对象中的某个属性,而proxy能够对整个对象进行拦截
2)Object.defineProperty只能进行简单的set, get操作,Proxy不仅可以读取和设置,还可以拦截 in ,delete等操作。
3)Objec.defineProperty做递归处理只能一开始做递归处理,而Proxy在调用的时候就可以直接做递归处理,在性能上Proxy更优秀。
4)对象上设置新属性时,Proxy监听得到,Object.defineProperty监听不到
5)数组增删修改时,Proxy监听得到变化,Object.defineProperty监听不到

7.Reflect

为对象专门设计的api,主要是替代OBJECT

1)Object上的内部方法,例如Object.defineProperty ,都放在Reflect上
2)修改某些Object方法的返回结果,让其变得合理
3)提供了统一的api操作对象,比如has,ownKeys
4)Reflect的静态方法和Proxy上的是对应的,一共13个

8.Set、WeakSet、Map、WeakMap

8.1Set

一种新数据类型,跟数组类似,每一项的值是不重复的。
基本属性:size、constructor
有四个操作方法,add、has、delete、clear
有四个遍历方法,keys、values,entries、forEach

8.2WeakSet

与Set的主要区别是,它的每一项只接受对象,并且不可遍历
主要是WeakSet中的对象不计入垃圾回收机制。
因此只有 has、get、delete方法,没有size和forEach以及遍历方法

8.3 Map

javascript的对象,与普通对象的区别就是值->值,Map对象中键不仅限于字符串
基本属性: size
操作方法: has、set、get、delete、clear
遍历方法:keys、values、entries、forEach

8.4WeakMap

WeakMap与Map对象最大的区别是,WeakMap中的键只能是对象(Null除外),同样WeakMap中键 不计入垃圾回收机制,所以也不能有遍历方法和size属性,因此WeakMap只有四个方法可用
get、set、has、delete

9.Symbol

新增的一种基本类型,是唯一的。

  • Symbol 作为属性名,遍历对象的时候,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
  • 可以使用Symbol.for()方法查找之前创建的symbol值,for方法是如果有就返回之前的symbol,如果没找到就重新创建一个symbol
    而 如果调用Symbol(‘foo’)100次,则创建了100个symbol值
  • 对象的Symbol.iterator属性指向该对象默认的遍历器方法

10.for…of… 方法

所有含有迭代器接口(即含有Symbol.iterator的方法)的都可以使用for…of…方法,但是一个普通object对象不能使用 此方法,会报错

es相关提案
es相关提案
已完成的提案
在这里插入图片描述

vscode 通过jsDoc增加智能提示,可以参考如下文档:https://www.jb51.net/article/163666.htm

二、Typescript

强类型的优势:

  • 错误能更早暴露
  • 代码更智能,编码更准确
  • 重构更牢靠
  • 减少不必要的类型推断。

Typescirpt定义
typescript是javascript的超集,是一个类型检测系统,能够在编译阶段进行代码类型检测

es6语法错误处理
在这里插入图片描述

  • 在tsconfig.json中配置lib
    在这里插入图片描述
    类 class
    1) private、protected、public关键字
    private只允许成员在当前类中访问,外部不能访问
    protected只允许在成员在当前类和子类中访问,外部不能访问
    publick公共,所有地方都可以访问
    2) 只读属性readonly
    只能在初始值或者构造函数中初始化,只能选其一

类的实现接口
类的实现接口就是一种契约,它可以强制一个类去符合某种契约。

interface Eat {
  eat(food: string): void
}

interface Run {
  run(distance: number): void
}

class Person implements Eat, Run {
  eat(food: string) {
    console.log('eat')
  }
  run(distance: number){
    console.log('run')
  }
}

抽象类
关键字abstract

  • 抽象类只能被继承不能被实例化
  • 抽象方法,就是在抽象类中的方法前加上abstract关键字,在子类中一定要实现,否则就报错
// 抽象类
abstract class Animal {
  eat(food: string){
    console.log(`eat->${food}`)
  }

  // 抽象方法
  abstract run(distance: number): void
}

class Cat extends Animal {
  run(distance: number): void {
    throw new Error("Method not implemented.")
  }
}

let cat = new Cat()
cat.run(100)

三、性能优化

3.1 内存管理介绍

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

3.2 JavaScript中的垃圾回收

3.2.1javascript中的垃圾

  • Javascript中内存管理是自动
  • 对象不再被引用时是垃圾
  • 对象不能从根上访问到时是垃圾

3.2.2 javascript中的可达对象

  • 可访问到的对象就是可达对象(引用、作用域链)
  • 可达对象的标准就是从根出发是否能被找到。
  • Javascript中的根就可以理解为是全局变量对象

3.2.3 GC算法

GC的定义和介绍:

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

GC里的垃圾是什么:
在这里插入图片描述
GC算法是什么

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

常用GC算法
引用计数、标记清除、标记整理、分代回收

1)引用计数算法
核心思想: 设置引用数,当引用关系发生改变时,修改计数,如果引用数为0就立马进行回收。
优点:

  • 发现垃圾立即回收
  • 最大限度减少程序占满。(当内存占满时,引用计数算法会立即找那些引用数为0的对象,进行回收,从而保证内存不会永远被占满)

缺点

  • 无法回收循环引用的对象
/* 循环引用 */
function fn() {
  const obj1 = {} 
  const obj2 = {}
  obj1.name = obj2
  obj2.name = obj1
}

fn()
  • 时间开销大(因为它时刻要保持对象的计数)

2)标记清除算法
核心思想: 分标记和清除两个阶段完成

  • 遍历所有对象标记活动对象
  • 遍历所有对象清除所有没有被标记的对象,并清除活动对象的标记
  • 回收相应的空间

优点:

  • 相对引用计数方式,解决了循环对象的问题。

缺点:

  • 容易产生碎片化空间,浪费空间
  • 不会立即回收垃圾对象
    在这里插入图片描述
    B可达对象,A/C没有被引用的对象
    当A、C被标记清除算法释放后,会被放到空闲链表,但是A的大小是2个,C的大小是1个,这样地址的不连续性,导致后面如果申请内存地址时只能申请2个或者1个,如果是1.5个则无法满足。

3)标记整理算法
核心思想: 标记整理算法可以看做是标记整理算法的增强

  • 标记阶段的操作和标记清除算法一致
  • 清除阶段会先进行整理,移动对象位置, 使得释放的空间连续

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

在这里插入图片描述
优点:
减少碎片化空间
缺点
不能立即回收垃圾对象
4)分代回收算法

3.2 V8引擎

3.1.1 认识v8

  • v8是一款主流的javascript执行引擎
  • v8是即时编译的,速度很快
  • v8设有内存上限,64位系统一般不超过1.5g,32位系统一般不超过800M

3.1.2 V8垃圾回收策略

  • 采用分代回收的思想
  • 内存分为新生代、老生代
  • 根据不同代的内存区采用不同的GC算法
    在这里插入图片描述

v8中常见GC算法

  • 分代回收
  • 空间复制
  • 标记清除
  • 标记整理
  • 标记增量

v8内存分配

  • v8内存空间一分为二
  • 小空间用存储新生代对象(64位 32M | 32位 16M)
  • 新生代指的的是存活时间比较短的对象,比如一个函数内的对象,执行完不用的
    在这里插入图片描述
    左侧两个为新生代对象内存区域

v8中对新生代对象回收实现

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

回收细节:

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

v8中对老年代对象回收实现
老年代对象的说明:

  • 老年代对象存放在右侧老生代区域
  • 内存大小,64位操作系统1.4g,32位操作系统700M
  • 老年代对象指的就是存活时间比较长的对象

实现:

  • 主要采用标记清除、标记整理、增量标记算法
  • 首先采用标记清除方法完成垃圾空间的回收
  • 采用标记整理进行空间优化,当新生代对象晋升到老生代区域,并且老生代空闲空间不满足新生代晋升对象的空间时,这个时候就会进行老生代空间的标记整理,整理碎片化空间给新生代晋升对象使用。
  • 采用增量标记效率优化,
    垃圾回收,会阻塞程序的执行,标记增量算法,就是将之前一口气执行的垃圾回收操作,分段执行,大大提升效率
    在这里插入图片描述

3.1.3 Performance的使用步骤

performance主要是监听浏览器内存,进行内存性能优化
在这里插入图片描述

内存问题的外在表现
  • 页面出现延迟加载或经常性暂停(垃圾回收)
  • 页面持续性出现糟糕的性能(内存膨胀)
  • 页面的性能随时间延长越来越差(内存泄漏)
界定内存问题的标注
  • 内存泄漏:内存使用持续升高
  • 内存膨胀(为了使代码运行最优突然申请使用大量内存):如果在多数设备上都存在性能问题就代表是代码的问题,否则就是设备的问题
  • 频繁垃圾回收:通过内存变化图进行分析
监控内存的几种方式
浏览器任务管理器

在这里插入图片描述

TimeLine时序图记录

可以观察js内存的在哪个时间节点突然出现问题,一般,正常的是有上有下。如果一直向上就有可能是内存泄漏了,然后定位在那个时间节点就可一乐

在这里插入图片描述

堆快照查找分离DOM

界面元素存活在DOM树上
垃圾对象的DOM节点:从当前DOM树上脱离了,并且没有被引用
分离的DOM节点:从当前的DOM树上脱离了,但是被引用着(实际上就是内存泄漏)

测试代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <button id="btn">点击</button>
  </div>
  <script>
    var tmpFile

    function fn() {
      var ul = document.createElement('ul')
      for(var i =0; i< 10; i++) {
        var li = document.createElement('li')
        ul.append(li)
      }
      tmpFile = ul
      tmpFile = null
    }

    document.getElementById('btn').addEventListener('click', fn)
  </script>
</body>
</html>

步骤:开始之前点一次,操作之后点一次
在这里插入图片描述
搜索detach,也就是分离的意思,搜到了就代表有分离DOM
在这里插入图片描述
点击按钮再拍一次快照
在这里插入图片描述

清空就把分离的变量设置为null就行了

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

GC工作,程序就会暂停,如果频繁的进行GC操作,就会出现以下现象
1)应用假死
2)用户感知上应用卡顿

如何确定:
1)TimeLine中频繁的上升下降
2)任务管理器中数据频繁的增加减小

如果出现这种情况就要定位到哪个时间节点出现这种问题。

3.1.4 代码优化

https://jsperf.com/ 停止维护了
另外一个网站JSBench https://jsbench.me/
在这里插入图片描述

慎用全局变量

在这里插入图片描述

// 优化前
var i, str = ''
for (i = 0; i < 1000; i++) {
  str += i
}


// 优化后
for (let i = 0; i < 1000; i++) {
  let str = ''
  str += i
}
将使用中无法避免的全局变量缓存到局部变量中
// 优化前
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 = document
      let oBtn1 = obj.getElementById('btn1')
      let oBtn3 = obj.getElementById('btn3')
      let oBtn5 = obj.getElementById('btn5')
      let oBtn7 = obj.getElementById('btn7')
      let oBtn9 = obj.getElementById('btn9')
    }
通过原型对象添加附加方法

在原型对象上新增实例对象需要的方法

// 优化前
var fn1 = function() {
  this.foo = function() {
    console.log(11111)
  }
}

let f1 = new fn1()

// 优化后
var fn2 = function() {}
fn2.prototype.foo = function() {
  console.log(11111)
}

let f2 = new fn2()
避开闭包陷阱

在这里插入图片描述

避免属性访问方法使用
// 优化前
function Person() {
  this.name = 'icoder'
  this.age = 18
  this.getAge = function() {
    return this.age
  }
}

const p1 = new Person()
const a = p1.getAge()

// 优化后
function Person() {
  this.name = 'icoder'
  this.age = 18
}
const p2 = new Person()
const b = p2.age
for循环优化
// 优化前
var arrList = []
arrList[10000] = 'icoder'

for (var i = 0; i < arrList.length; i++) {
  console.log(arrList[i])
}


// 优化后
for (var i = arrList.length; i; i--) {
  console.log(arrList[i])
}

采用最优的循环方式

forEach > for > for in
在这里插入图片描述

文档碎片优化节点添加
// 优化前
   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('box1')
    for (var i = 0; i < 3; i++) {
      var newP = oldP.cloneNode(false)
      newP.innerHTML = i 
      document.body.appendChild(newP)
    }

字面量替换new Object()
// 优化前
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3


// 优化后
var a = [1, 2, 3]
减少if判断层级

如果有大量的if…else…判断,可以提前把不满足的条件return掉

// 优化前
function doSomething (part, charpter) {
  const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']
  if (part) {
    if (parts.includes(part)) {
      console.log('属于当前课程')
      if (charpter > 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)
减少作用域链查找层级
// 优化前
var name = 'zs'
function fn() {
  name = 'ls'
  function children(age) {
    console.log(name, age)
  }
  children()
}

// 优化后
var name = 'zs'
function fn() {
  let name = 'ls'
  function children(age) {
    console.log(name, age)
  }
  children()
}

fn()
减少数据读取次数

在这里插入图片描述

字面量与构造式

字面量 形式 比 new 构造式 快些
对象: {} 形式快与 new Object() 形式,因为new Object相当于调用函数,而字面量形式直接是在开辟了一个使用内存

减少循环体活动
  • 尽量把不变的值提取到循环体外,缓存出来。
  • 不考虑执行顺序的话,用while循环会比for循环更快些
减少声明及与语句数

减少内存空间的开辟。根据具体需求和维护成本上来判定如何优化

惰性函数与性能

惰性函数的优化程度并没普通的快,所有要考虑使用场景
在这里插入图片描述

采用事件绑定

利用addEventLisener事件委托 捕获或者冒泡来执行事件

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值