系统学习大前端(4)---ES6+新特性、TS、JS性能优化

    技术2022-07-10  138

    1、ES6+新特性

    1.1 let、const

    块级作用域 变量提升 最佳实践:不使用var ,默认使用const ,明确会改变的使用let。

    1.2 解构

    数组 const arr = [1,2,3] const [a,b] = arr; const [,,c] = arr;

    使用…方式解构

    const [a,...rest] = arr; //默认值 const [a,b,c,d = 12] = arr 对象 const obj = { name:'zs', age:18 } const {name} = obj; // 重命名 const {name:rename} = obj;

    1.3 模板字符串

    let str = ` i am ${name} ` // 带标签的模板字符串 const str = console.log`hello world`

    1.4 字符串的扩展方法

    includes()startsWith()endsWith()

    1.5 参数默认值、 剩余参数

    … 操作符 function fn(...args){}

    1.6 展开数组

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

    1.7 箭头函数

    不会改变this的指向。 箭头函数不适用:

    构造函数没有arguments没有this

    1.8 对象字面量

    const obj = { foo:123, method1(){ console.log('1') }, //计算属性名 [Math.random](){ } }

    9、Object

    Object.assign 多个对象复制到一个目标对象。 const source1 = { a:123, b:23 } const target = { a:123, c:23 } const res = Object.assign(target,source1) Object.is 判断两个值是否一样

    1.10 proxy

    Object.defineProperty – ES5监听数据变更的API vue2的底层实现

    proxy – ES6 vue3的底层实现

    const person = { name:'ss', age:19 } const personProxy = new Proxy(person,{ get(target,key){ console.log(target,key) return 100; }, set(target,key,value){ console.log(target,key,value) } }) console.log(personProxy.name) personProxy.sex = '女'

    proxy的优势:

    defineProperty只能监视属性的读写,proxy可以监视delete、方法调用等 const person = { name:'ss', age:19 } const personProxy = new Proxy(person,{ deleteProperty(target,property){ console.log('delete') } }) delete personProxy.age

    proxy更好的监视数组 const list = [] const listProxy = new Proxy(list,{ set(target,property,value){ console.log(target,property,value) target[property] = value; return true//表示设置成功 } }) listProxy.push(10)

    1.11 reflect

    统一的对象操作API 静态类 内部封装了一系列对对象的底层操作 。

    const person = { name:'ss', age:19 } const personProxy = new Proxy(person,{ get(target,key){ // ... 自定义逻辑 return Reflect.get(target,key) }, set(target,key,value){ console.log(target,key,value) } }) console.log(personProxy.name) personProxy.sex = '女'

    Reflect.has Reflect.deleteProperty Reflect.ownkeys

    1.12 promise

    异步编程解决方案

    1.13 class

    // es5 function Person(name) { this.name = name } Person.prototype.say = function(){} // es6 class Person{ constructor(name){ this.name = name } say(){ } } const person = new Person('zhangsan'); 静态方法 class Person{ constructor(name){ this.name = name } // 实例方法 say(){ } // 静态方法 static create(name){ return new Person(name) } } 实例方法类的继承 – extends class Person{ constructor(name){ this.name = name } say(){ console.log('父say') } } class Student extends Person{ constructor(name,number){ super(name) this.number = number } hello(){ super.say(); console.log('hello') } } let stu = new Student('zhaosi',12342); stu.hello();

    1.14 set、map

    Set 不允许重复

    const set = new Set(); set.add(1).add(2).add(3) // 遍历 set.forEach(()=>{}) for(let i of set){ } set.size set.has() set.delete() set.clear() const arr = [1,2,3,1,4,2] const set1 = new Set(arr) Array.from(set1) const arr1 = [...set1]

    Map 类似对象,

    const obj = { } obj[true] = 'value' obj[123] = 'value' obj[{a:1}] = 'value' Object.keys(obj) const m = new Map() const tom = {name:'tom'} m.set(tom,99) m.has() m.delete() m.clear() m.forEach(()=>{})

    1.15 Symbol

    const a = Symbol() console.log(a) const obj = { } obj[Symbol('a')] = 123; obj[Symbol('b')] = 445; console.log(obj[Symbol('a')]) const a = Symbol.for('foo') const b = Symbol.for('foo') console.log(a===b) const obj1 = { [Symbol.toStringTag]:'XObject' } console.log(obj1.toString()) // [object XObject]

    私有属性

    唯一性for…in \ Object.key() 获取不到Object.getOwnPropertySymbol(obj)

    1.16 for…of — 迭代器

    const arr = [2,324,12,324,2,234] for(let i of arr){ console.log(i) if(i>188){ break } } // arr.forEach() //无法中断循环 // arr.some() // arr.every() // 遍历set、map

    可迭代接口 Iterable 实现Iterable接口,是使用for…of的前提

    const arr = [1,23,321,421] const iterator = arr[Symbol.iterator]() iterator.next()

    实现可迭代接口

    const obj= { store:['foo','bar','baz'], [Symbol.iterator]:function(){ let index = 0; const self = this; return { next:function(){ const res = { value:self.store[index], done:index>=self.store.length } index ++ return res; } } } } for(let item of obj){ console.log(item) }

    迭代器模式

    const todo = { life:['s','d'], learn:['e','w2'], work:['dfa'], each:function(cb){ const all = [].concat(this.life,this.learn,this.work) for(const item of all){ cb(item) } }, [Symbol.iterator]:function(){ const all = [...this.life,...this.learn,...this.work] let index = 0; return { next:function(){ return { value:all[index], done:index++>=all.length } } } } } //for(let item of to.life){} //for(let item of to.learn){} //for(let item of to.work){} todo.each(function(item){ console.log(item) }) for(let item of todo){ console.log(item) }

    1.17 生成器 generator

    避免回调嵌套

    function * foo(){ console.log('zcc') return 100 } const res = foo(); console.log(res) res.next() // 配合 yield function * boo(){ console.log('111') yield 100 console.log('222') yield 200 console.log('333') yield 300 } const gen = boo(); gen.next()

    生成器应用

    // 案例 : 发号器 function * createID(){ let id = 1; while(true){ yield id++ } } const id = createID(); id.next() // 实现iterator方法 const todo = { life:['s','d'], learn:['e','w2'], work:['dfa'], [Symbol.iterator]:function * (){ const all = [...this.life,...this.learn,...this.work] for(let item of all){ console.log(item) } } } for(let item of todo){ console.log(item) }

    1.18 ES Modules

    模块化

    1.19 ES2016 ES2017

    ES2016

    Array.prototype.includes指数运算符 Math.pow()** 2**10

    ES2017

    Object.values()Object.entries()Object.getOwnPropertyDescriptors()String.prototype.padStartString.prototype.padEnd函数参数中添加尾逗号 function foo(arg1,arg2,){ } async/await

    2、TypeScript

    解决JS类型系统问题

    强类型 VS 弱类型 (类型安全) 强类型有更强的类型约束。 强类型不允许隐式类型转换。

    静态类型 VS 动态类型 (类型检查) 变量声明时就是明确的。-- 静态类型

    弱类型的问题

    运行阶段才能发现错误类型不明确,造成函数功能发生改变对对象索引发生错误 const obj = {} obj[123] = 456 obj[123] // undefined 强类型的优势 错误更早的暴露代码更智能,编码更准确重构更牢靠减少不必要的类型判断

    TypeScript(语言)

    1、JS的超集

    可以对ES新特性编译(类似babel)功能强大、生态健全、完善任意JS环境都支持TS属于渐进式的【缺点】语言本省多了很多概念,如接口、泛型【缺点】项目初期,增加开发成本 2、基本使用 安装typescript yarn add typescript --dev tsc .\01-getting-start.ts

    3、配置文件 tsconfig.json

    tsc --init

    4、 原始类型

    const a:string = 'foo' const b: number = 12; const c:boolean = false; // 注意:严格模式 不能为空的 const d:void = undefined const e:null = null const f:undefined = undefined // 报错 const g:symbol = Symbol() // 解决:1、(tsconfig.json)target-->es2015 // 2、 (tsconfig.json) "lib": ['ES2015','DOM'], // const g:symbol = Symbol() // Promise

    标准库-- 就是内置对象所对应的说明 5、 TS中文错误消息 tsc --locale zh-Ch 或者 vscode中设置

    6、类型

    Object类型 // Object 泛指所有非原始类型 const foo :object = function(){} const obj :{foo:number,bar:string} = {foo:123,bar:'string'} 数组类型 const arr1 : Array<number> = [1,2,3] const arr2:number[] = [1,2,3] function sum(...args:number[]) { return args.reduce((pre,cur)=>pre+cur,0) } sum(1,2,3) 元组类型 // 元组类型 const tuple :[number,string] = [12,'str'] // const age = tuple[0] const [age,name01] = tuple 枚举类型 const post = { title:'hello ts', content:'ts is a typed', status:3 } // const PostStatus = { // Draft:0, // Unpublished:1 // } enum PostStatus{ Draft = 0, Unpublished = 1, Published = 2 } //常量枚举 const enum PostStatus1{} 函数类型 // 函数 // 可选参数 ? 或 参数默认值 function func1(a:number,b?:number):string { return 'str' } func1(1,2) // 任意参数 rest const func2 = (a:number):string=>{ return 'abc' } 任意类型 any

    隐式类型推断 类型断言

    // as 关键字 const num1 = res as number; //(推荐) //或 const num2 = <number>res; // 会和JSX标签冲突

    7、 接口

    // function printPost(post){ // console.log(post.title); // console.log(post.content); // } // 接口 interface Post{ title:string content:string subtitle?:string // 可选成员 readonly summary:string // 只读成员 } function printPost(post:Post){ console.log(post.title); console.log(post.content); } printPost({ title:'标题', content:'这是一段文字', summary:'摘要' }) // 任意成员 interface Cache{ [prop:string]:string }

    8、类

    // TS增强了class的特性 class Person { name:string age:number protected gendar:boolean constructor(name:string,age:number){ this.name = name this.age =age this.gendar = true } sayHi(msg:string):void{ } } // 类的访问修饰符 private(不能外部访问) // public(默认) // protected(不能外部访问,子类可访问) class Student extends Person{ constructor(name:string,age:number){ super(name,age) console.log(this.gendar); } static create(name:string,age:number){ return new Student(name,age) } } const tom = new Person('tom',10) console.log(tom.name); const jack = new Student('jack',13) // 只读属性 - readonly

    类和接口

    //用接口抽象类的共同属性 interface Eat { eat(food:string):void } interface Run{ run(distance:number) :void } class Person1 implements Eat,Run{ eat(food:string):void{} run(distance:number):void{} } class Animals implements Eat,Run{ eat(food:string):void{} run(distance:number):void{} }

    抽象类:约束子类必须有某些成员,只能被继承; 子类必须实现父类的抽象方法。

    abstract class Animals { eat(food:string):void{} abstract run(distance:number):void }

    9、泛型

    // 泛型 function createNumberArray(length:number,value:number):number[] { const arr = new Array<number>(length).fill(value); return arr } // function createNumberString(length:number,a:string):string[] { // const arr = new Array<string>(length).fill(a); // return arr // } // function createArray<T>(length:number,value:T):T[] { // const arr = new Array<T>(length).fill(value); // return arr // }

    10、类型声明

    3、 flow(工具)

    1、 JS的类型检查器 通过类型注解的方式,检查类型。

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

    通过babel去除注解代码。 2、 使用

    yarn add flow-bin --dev // 安装 yarn flow init //初始化配置文件 yarn flow yarn flow stop //停止命令

    3、 编译移除注解

    yarn add flow-remove-types --dev // 移除 yarn flow-remove-types . -d dist // 或者 通过babel方式移除 yarn add @babel/core @babel/cli @babel/preset-flow --dev yarn babel part01/module02/flow -d dist // 命令

    通过babel的 方式 要配置.babelrc文件,配置如下:

    { "presets":["@babel/preset-flow"] }

    4、开发工具插件 Flow Language Support ------------ VScode插件 5、 flow类型

    原始类型 /** * @flow */ const a : string= '12' const b :number = NaN // Infinity const c:boolean = true const d : null = null const e:void = undefined const f:symbol = Symbol() 数组类型 /** * @flow */ const arr:Array<number> = [12,3,4] const arr1 :number[] = [1,2,3] // 固定长度的数组 --- 元组 const foo:[string,number] = ['s',12] 对象类型 /** * @flow */ const obj:{ foo:string, bar:number } = { foo:'s', bar:125 } // 可选属性 const obj1:{ foo:string, bar?:number } = { foo:'s' } const obj3 :{[string]:string}= {} obj3.key ='value' obj3.key2 = 'value2' 函数类型 /** * @flow */ function fn(a:number,b:number) :number{ return a+b } function foo(cb:(string,number)=>void) { cb('str',100) } 特殊类型 /** * @flow */ // 字面量类型 const a :'foo' = 'foo'; // 联合类型 const type:'success'|'warning'|'danger' = 'success' const b :string | number = 100; type StrORNum = string|number; const c : StrORNum = 123; // maybe类型 可以为空 ? const gender : ?number = null 任意类型任意类型 // @flow // mixed 任意类型 --- 强类型 function passMixed(value:mixed){ if(typeof value === 'string'){ value.substr(1) } } passMixed('2ss') passMixed(20) // any --- 弱类型 function passMixe2(value:any){ } /** * any 和 mixed区别 * * any是弱类型的,mixed是强类型的。 * 尽量不要使用any * */

    6、 flow运行环境API

    // @flow const ele:HTMLElement | null = document.getElementById('app')

    4、JS性能优化

    1、内存管理 申请、使用、释放空间。

    2、垃圾回收 、常见GC算法

    JS中的垃圾 JS中内存管理是自动的对象不再被引用时是垃圾对象不能从根上访问到时是垃圾 可达对象 引用、作用域链全局执行上下文(根) let obj = {name:'xx'} let ali = obj; obj = null; // 可达对象 function objGroup(obj1,obj2) { obj1.next = obj2; obj2.pre = obj1; return { o1:obj1, o2:obj2 } } let obj00 = objGroup({name:'222'},{name:'333'}) GC算法 GC-垃圾回收GC可以在内存中找到垃圾 程序中不再使用的对象程序中不能访问到的对象 GC是一种机制(垃圾回收器完成的具体工作)工作内容就是查找垃圾释放空间、回收空间算法就是工作时查找和回收所遵循的规则 常见GC算法 引用计数

    [思想] 设置引用数,判断当前引用数是否为0

    引用计数器

    引用关系改变时修改引用数字

    引用数字为0 回收

    [优点] 发现垃圾时立即回收

    [优点] 最大限度减少程序暂停

    [缺点] 无法回收循环引用的对象循环引用

    [缺点] 时间开销大

    // 引用计数 const user1 = {age:11} const user2 = {age:22} const user3 = {age:33} const nameList = [user1.age,user2.age,user3.age] function fn(){ const num = 1 const num1 = 2 } fn();//调用后 num num2 引用为0 回收 // 循环引用 function fn() { const obj1 = {} const obj2 = {} obj1.name = obj2 obj2.name = obj1; return 'x' } fn() 标记清除 [原理] 分标记和清除两个阶段遍历所有对象找到标记活动对象遍历所有对象清除没有标记的对象回收相应的空间[优点] 解决循环引用的问题[缺点] 空间碎片化 标记整理 [原理] 标记清除的增强操作标记阶段和标记清除一样清除阶段会先执行整理,移动对象位置 分代回收 (V8)

    3、V8的垃圾回收

    V8 JS 执行引擎即时编译内存设限 V8垃圾回收策略 分代回收思想内存分新生代和老生代针对不同的生代采用不用的算法V8中常用算法 分代回收空间复制标记清除标记整理标记增量 新生代垃圾回收 V8内存分配 V8内存空间一分为二小空间用于存储新生代对象(32M | 16M)新生代指的是存活时间短的对象 回收实现 回收过程采用复制算法+标记整理新生代内存区分为2个等大小空间使用空间为From,空闲空间为To活动对象存储于From空间标记整理后将活动对象拷贝至ToFrom和To交换空间完成释放拷贝过程中可能会出现晋升(将新生代对象移动至老生代)一轮GC还存活的新生代对象需要晋升To空间使用率超过25% — 晋升 老生代垃圾回收 老生代对象存放在右侧老生代区64位操作系统1.4G 32位 700M老生代对象就是存活时间较长的对象垃圾回收实现 主要采用标记清除、标记整理、增量标记算法首先使用标记清除完成垃圾空间的回收采用标记整理进行空间优化(对象晋升时)采用增量标记进行效率优化 VS 新生代 新生代—空间换时间老生代不适合复制算法(空间浪费、复制量大)

    4、performance工具

    为啥使用performance GC的目的是为了实现内存空间的良性循环良性循环的基石是合理利用时刻关注才能确定是否合理performance提供多种监控方式 performance使用 打开浏览器输入目标网址F12面板 — 性能开启录制功能,访问具体页面执行用户行为,一段时间后停止录制分析界面中记录的内存信息 内存问题的体现 页面出现延迟加载或经常性暂停页面持续性出现槽糕的性能页面性能随时间延长越来越差 界定内存问题的标准 内存泄漏 内存使用持续升高内存膨胀 在多数设备上都存在性能问题频繁垃圾回收 通过内存变化图分析 监控内存的几种方式 浏览器任务管理器 shift+esc 打开任务管理器面板右键点击 — 选择JS使用内存 Timeline 时序图记录 堆快照查找分离DOM 什么是分离DOM 界面元素存活在DOM树上垃圾对象时的DOM节点分离状态的DOM节点 搜索(deta) 判断是否存在频繁的垃圾回收 为什么确定频繁垃圾回收? GC工作时应用程序是停止的频繁且过长的GC会导致应用假死用户使用中感知应用卡顿 确定频繁的垃圾回收 timeline中频繁的上升下降任务管理器中数据频繁的增加减小

    5、代码优化

    如何精准测试JS性能?

    本质上就是采集大量的执行样本进行数学统计和分析使用基于Benchmark.js的https://jsperf.com完成

    jsperf使用流程

    使用GitHub账号登录填写个人信息(非必填)填写详细的测试用例信息(title、slug)填写准备代码(DOM操作时经常使用)填写必要有setup与teardown代码填写测试代码片段

    慎用全局变量

    全局变量定义在全局执行上下文,是所有作用域链的顶端全局执行上下文一直存在于上下文执行栈,直到程序退出如果某个局部作用域出现了同名变量则会出现遮蔽或污染全局

    缓存全局变量

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

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

    避开闭包陷阱

    闭包的特点 外部具有指向内部的引用在‘外’部作用域访问‘内’部作用域的数据 关于闭包 闭包是一种强大的语法闭包使用不当很容易出现内存泄漏不要为了闭包而闭包

    避免属性访问方法使用

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

    for循环优化

    选择最优的循环方法

    for(中)for … in (低)foreach (高)

    文档碎片优化节点添加

    克隆优化节点

    直接量替换new Object

    var a = [1,2,3]// 推荐 var a1 = new Array(3) a1[0] = 1; a1[1] = 2; a1[2] = 3;
    Processed: 0.010, SQL: 9