大前端【1-2笔记】ES 新特性与 TypeScript、JS 性能优化

ECMAScript2015

一、概述

1、ECMAScript2015 是JavaScript 的一种的新的标准规范,就是对 js 的写法上提出了新的语法要求和写法格式。是一种新的标准规范。

2、ES和JS的关系:ES是JS的一种规格,JS是ES的实现。

3、ES6和ES2015的关系,ES2015是在2015年发布的ES6的一个版本

ES5.1以后的变化

  • 解决原有语法的不足与问题(let、const的块级作用域)
  • 对原有语法进行增强(解构、展开、参数默认值、模板字符串)
  • 全新的对象(promise)、全新的方法(proxy)、全新的功能(Object.assign())
  • 全新的数据类型和数据结构(symbol、set,map)

二、使用

1、let与块级作用域

块级作用域,凡是带{}的都是一个作用域

1、在内部定义的变量a,在外部依旧可以访问

if(true){
    
    
    var a = '123' 
}
 console.log(a)  // 123

2、作用域内部重新定义外部变量,则不会对外部变量进行修改,循环体有两层嵌套的作用域,

for(let  i = 0; i < 3;i++){
    
    
    let  i = 'test'
    console.log(i)
}//输出三次‘test’,证明内部的i并没有改变for的循环条件中的i

分开解析,如下,结论:for循环有两层嵌套的作用域,一个是内部作用域一个是for循环条件的作用域

let i = 0;
if(i<3){
    
    
    //  i = 4 ,没有重新定义直接修改,则会直接改变外部值,最终输出4
    let i = 4; //如果重新定义i,则不会改变外部i的值,最终输出0
}
console.log(i)

3、使用var定义会引起变量声明提升

console.log(a) //沒有报错,打印undefined,证明此时a已经被声明,但是还没有赋值,所以var定义会引起变量声明提升
var a = 123

4、使用let不会引起变量提升,ES6规定要先声明变量,然后再使用变量

console.log(a) //报错,因为a没有被声明
let a = 123

为什么不在原有var的基础上进行修改,而是重新引入一个新的let?因为如果在原有基础上修改,之前使用var的项目将无法正常使用,所以使用新的关键词let

2、const

用来生成只读的常量或对象

1、const生成的常量不允许被修改

const a = 'test'
a = 'test2'  //不允许被修改

2、const生成的对象不允许修改指向的内存地址,但是可以修改内部属性

const obj = {
    
    }
obj.name = '123' //内部属性允许修改
console.log(obj)
obj = {
    
    } //指向的内存地址不允许修改,报错

3、let、const、var的选择

不用var,避免变量提升以及垃圾作用域,主用const(默认全部使用const),配合使用let(如果值需要修改,则使用let)

4、解构

ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这个过程被称之为解构

(1)完全解构,将数组中的每个值都对应到相应的变量

//完全解构
const arr = [1,2,3]
const [arr1,arr2,arr3,arr4] = arr
const [arr1,arr2,arr3,arr4 = '123'] = arr //设置默认值,如果取不到值,则会使用默认值
console.log(arr1,arr2,arr3) //1 2 3
console.log(arr4)//undefined,就像是访问数组中不存在的下标

const str = 'foo/boo/too'
const [a,b,c] = str.split("/")
log(a,b,c)  // foo boo too

(2)不完全解构,数组中的部分值对应到相应的变量,不取模式匹配的值需要使用逗号隔开

//不完全解构
const arr = [1,2,3]
const[,,arr4] = arr
console.log(arr4)  // 3

(3)对象的解构,没有的值也会打印undefined,也可设置默认值

//对象解构
const obj = {
    
     name:'test', age:18}
const {
    
    name} = obj
console.log(name)

可设置别名

const {
    
    name:myName} = obj //myName,匹配完name以后将值赋值给myName,
console.log(myName) // test

(4)模板字符串

const name = 'tom'
const str = `name is ${
      
      name}`
console.log(str) // name is tom
const str1 = `运算:${
      
      1+1}`
console.log(str1) // 2

(5)带标签的模板字符串

const name = 'tom'
const gender = true
function myTagFun(strings,name,gender){
    
    
    let sex = gender ? 'man':'woman'
    return strings[0]+name+strings[1]+sex
}
const str = myTagFun`hey,${
      
      name}is a ${
      
      gender}` //在模板字符串前边可以进行myTagFun的输出,并且将模板字符串作为参数传递给函数,name和gender分别接受${name}和${gender}的计算结果
console.log(str) //hey,tom is a man

5、字符串的扩展方法

startWith、endsWith、includes

const message = 'my name is tom'
console.log(
    message.startsWith('my'), //true 
    message.endsWith('123'), //false
    message.includes('is') //true
)

6、参数默认值

可以直接在参数名后添加默认值,设置默认值的参数一定要放到最后

function foo(str,name = 123){
    
     //直接在name参数后添加默认值123,注意:设置默认值的参数一定要放到最后
    console.log(str,name)
}
foo('str')
foo('str','hello')

7、剩余参数

参数不固定的情况下可以使用‘…’进行标识,arg会以数组的形式接受相应的参数

function foo(...arg){
    
    
    console.log(arg) // [1,2,3,4]
}
foo(1,2,3,4)

8、展开数组

直接使用‘…’对数组进行展开输出

const arr = [1,2,3,4]
console.log(...arr) //1 2 3 4

9、箭头函数

出现的作用除了让函数的书写变得很简洁,可读性很好外;最大的优点是解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题

const add1 = (n1,n2) => n1+n2
console.log(add1(3,4))  // 7

10、箭头函数和this对象

this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this指向的是window;当函数被作为某个对象的方法调用时,this就等于那个对象

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。箭头函数外面的 this 是什么,箭头函数内的 this 还是什么。

11、对象字面量的增强

当变量名与属性名一致的时候,可以直接写属性名, 见代码,[ ]中可计算,这种属性名称之为计算属性名

const bar = 'bar'
const obj = {
    
    
    name:123,
    bar, //等同于:bar:'bar'
    myLog(){
    
    
        console.log(this) //指向当前对象obj
    },
    [1+1+'str']:'12' //等同于 str2:‘12’,[]中可计算,,计算属性名
}

12、Object.assign( )

将多个源对象的属性复制到一个目标对象中,从源对象中取数据,放到目标对象中,如果有多个对象,则将后边对象的参数全部赋值给源对象

const  source1 = {
    
    a:123,b:456}

const target = {
    
    a:456,c:789}
Object.assign(target,source1) //有返回值,返回值与target相同
console.log(target) //{ a: 123, c: 789, b: 456 }
const  source2 = {
    
    a:123,b:456,d:0}
Object.assign(target,source1,source2) //多个参数的情况
console.log(target) //{ a: 123, c: 789, b: 456, d: 0 }

13、Object.is

比较两个值是否完全一致

console.log(NaN===NaN)//false
console.log(+0===-0) //true

console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false

14、Proxy

使用new Proxy进行创建一个对象的代理对象,其中第一个参数为所要代理的对象,第二个参数为处理函数

const person = {
    
     name:'test',age:12} //定义

const personProxy = new Proxy(person,{
    
    
  get(target,property){
    
    
    return property in target ? target[property]:'default' //返回值
  },
  set(target,property,value){
    
    
    if(property === 'age'){
    
    
        if(!Number.isInteger(value)){
    
    
            throw new TypeError("age not a num")  //可以对传递的数据进行校验
        }
    }
    console.log(target,property,value)
  }
})

console.log(personProxy.name) //test
console.log(personProxy.qq) //default
personProxy.name='hello'
personProxy.age='12'

15、Reflect

静态类,通过类型.方法名调用

const myObj = {
    
    
    name:'Tom',
    age:18
}
console.log(
    Reflect.has(myObj,'name'), // true
    Reflect.deleteProperty(myObj,'name'), // true
    Reflect.ownKeys(myObj) // ['age']
)

16、Promise

promise是ES6中提供的一个内置对象。

17、类

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。constructor方法默认返回实例对象(即this)。

class Person{
    
    
    constructor(name){
    
    
        this.name = name
    }
    say(){
    
    
      return 'hello'
    }
}

const p = new Person('test')

18、静态方法

使用关键字static修饰,可以直接通过类名.方法名

static logMethond(){
    
    
        console.log('我是静态方法')
}
Person.logMethods() // ‘我是静态方法’

19、类的继承

一个类继承另一个类,则会拥有父类所有的属性和方法,可以重写方法或者重新定义新的属性和方法

class Tom extends Person{
    
    
    constructor(name){
    
    
        super(name)
    }
}

const tom =  new Tom(12)
tom.say()  //使用父类的方法

20、Set

Set是一种数据结构,用来存放不重复的数据,常用来去重

(1)创建与添加元素,add()方法返回一个set对象,所以可以链式调用add

const set = new Set()
set.add(1).add(2).add(3).add(4) //添加元素

(2)遍历

//第一种方式
set.forEach(element => {
    
    
    console.log(element)
});
//第二种方式
for(let i of set){
    
    
   console.log(i)
}

(3)获取长度,size

console.log(set.size)

(4)判断是否含有某个元素,has

console.log(set.has(1)) // true

(5)删除某个元素,delete

set.delete(1)

(6)清除集合中的数据,clear

set.clear()

(7)set与array的转化

//将set转化为数组
let arr = [...set]
let arr1 = Array.from(set)
//将数组转化为set
let arr = [1,2,3,4]
let set1 = new Set(arr)
console.log(set1)

21、Map

Map是一种数据结构

let m = new Map() //创建实例
m.set("name","tom") //存储值
console.log(m.get("name"))//获取name
console.log(m.has('name'))//是否有name键
m.clear()//清空整个map
m.forEach((value,key)=>{
    
      //遍历
    console.log(key+":"+value)
})

22、Symbol

(1)Symbol不是一个构造函数,不能使用new来创建

const symbol = Symbol()
console.log(symbol) //Symbol()

(2)Symbol可以传递一个字符串参数,参数作用是对symbol类型的描述,便于区分这个symbol是哪一个,

const s1 = Symbol('foo')
const s2 = Symbol('bar')
console.log(s1,s2)   // Symbol(foo) Symbol(bar)

(3)Symbol类型的值具有唯一性,是一个独一无二的值,每一个 Symbol 的值都不相等。相同参数 Symbol() 返回的值不相等

const s1 = Symbol('foo')
const s2 = Symbol('foo')
console.log(s1===s2) // false

(4)Symbol私有变量

const name = Symbol()
const obj = {
    
    
    [name]:'123',
    sayHi(){
    
    
        console.log(this[name])
    }
}
console.log(obj.name) //不能直接访问
obj.sayHi() //只能调用其它非私有的方法

(5)for in 获取不到symbol值

for(let key in obj){
    
    
   console.log(key)
}   //sayHi


(6)keys也获取不到值

console.log(Object.keys(obj))   // ['sayHi']

23、for…of循环

(1)遍历数组

//数组遍历
const arr = [1,2,3,4,5]
for (const item of arr) {
    
    
    console.log(item) // 1,2,3,4,5
}

(2)遍历set集合

const set = new Set([1,2,3,4,5])
for (const item of set) {
    
    
    console.log(item)//1,2,3,4,5
}

(3)遍历Map集合

let map = new Map()
map.set("name","Tom")
map.set("age",12)
for (const [key,value] of map) {
    
    
    console.log(key+":"+value)
}

(4)执行原理,调用iterator方法得到一个迭代器,从而遍历内部所有的数据。所以只有实现了Iterator接口才可以进行for…of的遍历

const arr = [1,2,3]
const iterator = arr[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

24、ES2016

includes方法,查询有没有相关目标,有则返回true,无则返回false

指数运算,可以直接像加减乘除一样运算

let arr = [1,2,3,4,NaN]
console.log(arr.includes(1))  //true
console.log(arr.indexOf(NaN))  // -1
console.log(arr.includes(NaN)) // true

//指数运算
console.log(Math.pow(2,10)) // 1024
console.log(2 ** 10) //1024

25、ES2017

(1)Object.values(),返回一个对象所有value的数组

const obj = {
    
    
    name:'tom',
    age:12
}
console.log(Object.values(obj) // [ 'tom', 12 ]

(2)Object.entries(),返回属性值对应的数组

console.log(Object.entries(obj))   //  [ [ 'name', 'tom' ], [ 'age', 12 ] ]
let map = new Map(Object.entries(obj))
console.log(map) //  Map { 'name' => 'tom', 'age' => 12 }	

(3)Object.assign()的弊端,只能复制属性值,方法不能被赋值,通过输出值可以看出,输出的fullName不是一个函数,而是一个固定的字符串

const p = {
    
    
    firstName:'Lei',
    lastName : 'Wang',
    get fullName(){
    
    
        return this.firstName+" "+this.lastName
    }
}

console.log(p.fullName) // Lei Wang
let p2 = {
    
    }
Object.assign(p2,p)
console.log(p2) // { firstName: 'Lei', lastName: 'Wang', fullName: 'Lei Wang' }

(4)使用 Object.getOwnPropertyDescriptors与Object.defineProperties

const descriptor = Object.getOwnPropertyDescriptors(p)
console.log(descriptor)
const copyP = Object.defineProperties({
    
    },descriptor)
console.log(copyP)  // { firstName: 'Lei', lastName: 'Wang', fullName: [Getter] }
copyP.firstName = 'qiuqiu'
console.log(copyP.fullName)

(5)填充

const books = {
    
    
    html:5,
    css:16,
    javascript:128
}

for (const [name,count] of Object.entries(books)) {
    
    
    console.log(name+" "+count)
}
//html 5
//css 16
//javascript 128

for (const [name,count] of Object.entries(books)) {
    
    
    console.log(`${
      
      name.padEnd(16,'-')}|${
      
      count.toString().padStart(3,'0')}`)
}
//html------------|005
//css-------------|016
//javascript------|128

(6)允许在参数中添加逗号

const arr = [
    1,2,3,4,5,
]
console.log(arr) // [ 1, 2, 3, 4, 5 ]

TypeScript

一、概述

TypeScript是一门基于JavaScript之上的语言,重点解决JS自有系统的不足,通过TypeScript可以大大提高代码的可靠程度

二、知识点

1、强弱类型

类型安全角度分类,分为强类型和弱类型。

强类型不允许任意的隐式类型转化,在语言层面限制函数的实参类型必须与形参类型相同(Java类中的函数),弱类型则允许隐式类型转化,强类型有更强的类型约束,弱类型几乎没有什么约束

例如:Java中的形参与实参在类型和个数上一定要保持一致,但是JavaScript则没有这些限制

2、静态动态类型

类型检查角度分类分为静态类型和动态类型

静态类型:一个变量声明以后他的类型就是明确的,声明过后,它的类型不允许修改

动态类型:运行阶段才能够明确变量类型,而且变量类型也可以随时变化,变量是没有类型的,但是变量存放的值是有类型的。

3、JS类型系统特征

弱类型并且是动态类型,没有编译环节,

4、弱类型的问题

(1)没有编译过程,所以错误只会在执行的过程中被抛出,例如下边的代码,会在运行一秒之后才会报错。

const obj = {}
setTimeout(()=>{
    obj.fun()
},1000)

(2)由于类型不固定造成的功能可能发生改变

function add(a,b){
    
    
    return a+b
}
console.log(add(1,2)) //3
console.log(add(1,"2")) // 12

5、强类型的优点

(1)错误更早暴露

(2)代码更智能,编码更准确

(3)重构更牢靠

(4)减少不必要的类型判断

6、Flow

(1)安装使用

 npm install flow-bin   //安裝
 npm run flow init  //初始化配置文件
 npm run flow status 
 npm run flow check
 npm run flow stop

(2)使用示例,注释添加 @flow,方法中添加参数类型

// @flow
//参数类型为number,返回值类型也为number
function addSum:number(a:number,b:number){
    
    
    return a+b
}
console.log(addSum(1,'2'))
let num:number = 1   //变量

(3)yarn安装

npm install -g yarn   // 全局安装
yarn --version //查看系统版本 
/**  yarn : 无法加载文件 C:\Users\张艳杰\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Exec 
ution_Policies。
**/

报错处理:

1、搜索powershell ,右键以管理员身份运行
2、将计算机上的 执行策略更改为 RemoteSigned,执行:set-ExecutionPolicy RemoteSigned
3、查看执行策略:get-ExecutionPolicy,即可解決

继续查看版本

yarn --version   //1.22.4

(4)移除注释

添加过注释的文件上hi没有办法运行的,所以要将注释移除掉,添加flow-remove-types模块

yarn add flow-remove-types --dev
yarn flow-remove-types . -d dist   // .表示当前目录,dist表示去除注释以后的js文件的输出路径

(5)只有通过命令才能检测到数据,比较麻烦,所以为了避免这个问题,我们可以安装插件”Flow Language Support“,在代码保存过后就直接进行检测

(6)类型推断

在我们不标注参数类型的情况下,直接报错,说明在编译的过程中进行了校验,这种行为叫做类型推断

function power(a,b){
   return a**b
}

power(1,'3') 

(7)类型注解

函数中参数可加

function addSum(a:number,b:number){
    
    
   return a + b
}

变量可加

let num : number = 100
num = '12'  //红色波浪线提示

函数返回值可加

function addSum :number(a:number,b:number){
    
    
   return a + b+""   //红色波浪线提示
}

(8)原始类型

const a: String = 'str'
const b: Number = 100 //NaN  
const c: Number = Infinity
const d: null = null
const e: void = undefined
const f: symbol = Symbol()

(9)数组类型

两种方式,一种是Array<>,一种是数组定义,也可以在一个数组中有多个值的类型定义

const arr: Array<number> = [1,2,3,4,'12']

const arr1: number[] = [1,2,'3']
const arr2:[string,number] = ['foo',12]

(10)对象类型

声明为一个对象使用{ }

const obj: {
    
    }= {
    
    name:'123'}

声明对象中必须要有某一个属性

const obj1: {
    
    name:String,age:number} = {
    
    name:'123'} //沒有age属性,红色波浪

声明一个对象可有可无,屬性后添加?即可

const obj1: {
    
    name:String,age?:number} = {
    
    name:'123'} 

对属性类型进行校验

const obj2: {
    
    [string]:number} = {
    
    }

(11)函数类型

function fun(callback:(string,number)=>number){
    
    
  callback('string',100)
}

fun(function(str,n){
    
       //必須传递参数为strign和number的值
    return n
})

(12)特殊类型

字面量类型(“|”表示或者)

let str: 'success' |'fail' = 'fail12'  //红色波浪  值只能为success和fail中的一个

可以别名使用

type types = string | number
let num: types = '23'

(13)Mixed和Any

Mixed,所有类型的集合,但是使用的时候也要进行类型的判断才可以,所以说是强类型

function testMixed(value: mixed){
    
    
   if(typeof value === 'number'){
    
    
      value*value
   }
   if(typeof value === 'string'){
    
    
        value.substr(1)
   }
}

Any,与mixed一样,但是使用的时候不用进行类型的判断

function testAny(value: any){
    
    
    value*value
    value.substr(1)
 }


7、TypeScript

typeScript是一门基于JavaScript之上的一门语言。是JS的扩展集和超集。

yarn init --yes
yarn add typescript --dev   //安装typeScript模块
yarn tsc .\01-prepare.ts  
yarn tsc --init //tsconfig.json

typeScript使用步骤:

安装typeScript模块,模块提供了tsc的命令,用来编译ts文件,编译过程中会先检查类型使用异常,移除掉类型扩展的语法,自动转化ES的新特性

(1)原始数据类型

const a: string = '123'
const b: number = 123 // NaN Infinity
const c: boolean = true //false
const d: void = undefined
const e: null = null
const f: undefined = undefined

(2)作用域

不同文件当中会有相同变量名称问题,使用立即执行函数

(function(){
    
    
  let a = 1
})()

或者

let a = 123
export {
    
    } //确保跟其他示例成员没有冲突

(3) Object 类型

object类型指的是除了原始类型以外的所有类型,可以通过字面量的方式定义

const foo: object = function(){
    
    } // [] {}
const obj: {
    
     foo : number } = {
    
     foo : 123 }

(4)数组类型

const arr:number[] = {
    
    1,2,3}
const arr1:Array<Number> = {
    
    1,2,3}

(5)元组类型

元组:明确元组数量以及每个元组类型的数组,类型可以不相同

const  tuple: [number,string] = [18,'123'] //类型不一致或者是个数不一致都会导致报错

(6)枚举类型

一种常见的数据结构,如果设置第一个值为x,如果不设置,x=0,则后续值分别加1,调用时候用 枚举名称.枚举的成员名称,字符串也可以进行枚举,此时需要每个都要进行赋值。使用枚举类型,编译转换的时候不会被移除掉,

enum MyStatus{
    
    
 one=1,
 two,
 three
}
console.log(MyStatus.three)

(7)函数类型

直接定义在参数后,返回值也可固定类型

function add(num1:number,num2:number) :number{
    
       
  return num1+num2
}  
add(100,200)  //参数固定,类型固定

function add1(num1:number,num2:number = 10) :number{
    
       
   return num1+num2
}

add1(1)

(8)任意类型

any不会进行类型检查,所以会出现安全问题,

function trans(str:any){
    
    
    JSON.stringify(str)
}

(9)隐式类型推断

如果不声明变量的类型,则会根据赋值进行类型推断,并不可进行改变

let tes = 18
tes = '123' //报错,因为tes被隐式推断为了number,相当于已经定义了,let tes:number = 18

但是如果不声明变量的类型,并且不赋值,则类型为any,可以赋值任意类型的数据

let test2
test2 = 18
test2 = '123'

(10)接口

interface,约定对象的结构,可选成员要添加?,只读成员,属性不能被修改,添加readonly

interface Post {
    
    
	title: string
	content: string,
    time?:string,
    readonly name:string
}

(11)类

使用class关键字

class Person {
    
    
	name: string
	number: number
	constructor(name: string,age:number) {
    
    
		this.name = name
		this.age =age
	}
	sayHi(mes: string):void {
    
    

	}
}

访问修饰符

public:默认修饰符 公有的
private:私有的 类的内部访问 外部不能访问
protected:受保护的 外部不能访问 允许子类访问

抽象类使用abstract修饰,抽象类,抽象方法,需要子类去实现

三、JavaScript性能优化

性能优化是不可避免的,任何一种可以提供运行效率降低运行开销的行为都可以称为性能优化。JavaScript中内存管理是自动的。对象不再被引用的时或对象不能从根上访问到的时候都被称之为垃圾。

JavaScript中的可达对象

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

1、GC算法

GC就是垃圾回收机制的简写,GC可以找到内存中的垃圾,并释放和回收空间,GC中的垃圾:程序中不再需要使用的对象、程序中不能访问到的对象。常见的GC算法:引用计数、标记清除、标记整理、分代回收

1、引用计数

核心思想是设置引用数,判断当前引用数是否为0,引用关系改变的时候会修改引用数字,引用数字变为0立即回收。

缺点:对象之间互有引用的情况,即使对象本身没有被使用,但是引用存在就导致了引用计数不为0,无法被回收的情况。

详:如果一个对象的引用数为0,则该对象将被回收。程序运行时,对内存的消耗除逻辑代码外,也包括了GC算法的消耗,引用计数固然可以在程序出现垃圾的时候可以及时回收释放内存,也因内存可以不断得到释放而减少了程序暂停的时间,但是GC同时也需要维护‘roots’表来统计引用计数,当代码中引用较多时,也会带来损耗。同时对于 对象之间互有引用的情况,即使对象本身没有被使用,但是引用存在就导致了引用计数不为0,无法被回收的情况。

2、标记清除

标记清除算法会递归的寻找对象之间的引用获取所有可达对象,并为其做上标记, 但是标记清除算法所回收的内存地址在内存上不一定是连续的,这就导致了内存空间的碎片化(类似磁盘碎片), 浪费空间,并且标记清除法是不会立即回收对象的,而且当标记清除法运行当时候,程序会被暂停。

  • 分为标记/清除俩阶段
  • 遍历所有对象,寻找活动的对象
  • 遍历所有对象清除没有被标记的对象
  • 回收相应的空间

3、标记整理算法

标记整理算法增强了标记清除算法,其在回收对象前会将对象的地址进行移动,使其在地址上连续,然后再回收.

2、V8

V8是一款主流的JavaScript的执行引擎,v8采用及时编译,V8内存设有上限,

V8垃圾回收策略

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

V8中常用的GC算法

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

新生代回收实现

新生代对象:存活时间比较短

1、回收过程采用复制算法+标记整理

2、新生代内存区分为两个等大小的空间

3、使用空间为from,空闲空间为to

4、活动对象存储于From空间

5、标记整理后将活动对象拷贝到To

6、From与To交换空间完成释放

老生代回收实现

1、主要采用标记清除、标记整理、增量标记的GC算法

2、首先采用标记清除完成垃圾空间的回收

3、采用标记整理进行空间优化

老生代区域大小约1.4G(32位大小为700M),老生代存放的对象为存活时间较长的对象,一般为window下的变量或被闭包保存的变量。
老生代区域采用标记清除,标记整理,增量标记的GC算法。首先使用标记清除完成对垃圾空间的回收,当新生代区域出现晋升现象时,如果老生代空间不足,则会使用标记整理进行空间优化。同时在老生代变量进行回收的时候也会采用增量标记算法进行效率优化。
增量标记是对标记清除算法对优化,让其不会一口气的去寻找到所有活动对象。而是会穿插在程序的运行中执行,降低了程序的卡顿,当标记彻底采集完毕之后,才会把程序停下来,进行垃圾回收。

猜你喜欢

转载自blog.csdn.net/qiuqiu1628480502/article/details/107285041
今日推荐