ES新特性
最主要的四个方面:
- 解决原有语法上的一些问题或者不足
- 对原有语法进行增强
- 全新的对象、全新的方法、全新的功能
- 全新的数据类型和数据结构
作用域
作用域:某个成员能够作用的范围
块级作用域:let关键词声明变量,在花括号范围内生效的块级作用域
let变量声明不会提升。
const定义恒量。所以声明时就需要赋值。const声明的成员不能被修改,指的是是不允许再修改指向新的内存地址,但成员的属性值是可以修改的。
解构
关键字:[]。还可直接在解构的位置=指定默认值。
…展开。但只能用于最后的位置
数组的解构通过位置一一对应来结构赋值。
对象的解构通过属性名来解构赋值。为避免属性名冲突的问题,还可以在等号左边赋值的位置指定获取属性的新属性名称。
const obj = {name:'a',age:18}
const {name } = obj
//指定name属性新的变量名
const {name:newname} = obj
字符串
模板字符串
关键字:``
支持换行和变量取值:插值表达式${变量}
带标签的模板字符串:用插值表达式分割,将字符串以数组形式传递成模板函数的参数。
const name = 'tom'
const gender = true
function myTagFunc(string,name,gender){
return string[0] + name + string[1] + gender + string[2]
}
const result = myTagFunc`hey,${name} is a ${gender}.`
//hey,tom is a trues
字符串扩展
startsWith、endWith、iscludes
参数
在形参定义时通过=设置默认值。
tips:定义多个参数时,需要传递默认值的参数需要定义在最后。
剩余操作符:…
…args能展开参数伪数组,还能展开数组。
const arr = ['foo','bar','zz']
console.log(...arr)
//foo bar zz
箭头函数:
(参数) =》 {函数体}
箭头函数没有this的机制。永远指向定义箭头函数的作用域上下文。
字面量的增强
对象属性为变量赋值,可以直接赋值而不再需要通过属性名来指定变量为其赋值;
还可以通过[]进行计算属性名赋值。
const bar = '123'
const obj = {
foo:123,
bar,
[Math.random()]:123,
[bar+12]:333
}
//{'12312':333,foo:123,bar:123,'0.654326543':123}
assign
assign(对象1,对象2):用对象2覆盖对象1,并返回对象1.
- proxy
可监听数组的相关操作;监视属性不会入侵到被监视对象本身。 - realect
class类
实例方法:通过实例对象调用。静态方法:只能class自身调用,关键字static.
类的继承: class 子类名 extends 父类名. 在子类中通过super()继承父类构造器内的属性值。
class Person = {
constractor(name){
this.name = name
}
say(){
console.log(`hi,my name is ${name}`)
}
}
class student extends Person{
constractor(name,number){
super(name)
this.number = number
}
hello() {
super.say()
console.log(`my school number is ${number})
}
}
set
一个不重复的集合。类似于数组,但是数组中的数据不能重复。可用于去重。api:size、has、delete
map
键值对集合。类似于对象,但是键值可以不只是字符串。
const m = new Map()
const tom = {name:'tom'}
m.set(tom,90)
console.log(m) // Map {{name:'tom'} => 90}
console.log(m.get(tom)) //90
symbol
表示一个独一无二的值。用处:为对象添加独一无二的属性标志符。
for…of循环
遍历数组时,直接得到值。可以直接break退出循环。
遍历Map对象,得到键值对数组
const m = new Map()
m.set('foo','123')
m.set('bar','234')
for (const item of m){
console.log(item)
}
//结果:
[ 'foo', 123 ]
[ 'sda', 234 ]
可迭代接口
iteraator对象实现可迭代接口。迭代器内部有一个next方法输出迭代器结果接口(IterationResult).迭代器结果接口需要返回值value和迭代状态done。
const obj = {
[Symbol.iterator]: function(){
return {
next:function(){
return {
value:'aaa',
done:true
}
}
}
}
生成器
避免异步编程中回调嵌套过深。关键字:*。也是通过nex t调用。
生成器函数返回一个生成器对象,通过next()方法执行函数体,遇到关键词yield时暂停执行,yield后面的值作为next方法的结果返回。下次再调用next方法,从暂停的位置继续执行。
function *foo(){
console.log("sss")
return 100
}
常用运用场景:发号器。
ES2016
includes、指数运算符:底数 ** 指数(eg:2 ** 10 = 1024)
ES2017
padStart、padEnd以指定字符补齐位数对齐。
const books = {
html:5,
css:12,
js:1233
}
for(const [name,count] of Object.entries(books)){
console.log(`${name.padEnd(16,'-')} | ${count.toString().padStart(3,'0')}`)
}
//结果
html------------ | 005
css------------- | 012
js-------------- | 1233
TypeScript
flow:类型检查器
关键字:冒号类型注解
- 安装:
npm安装:
1.安装:pm install --g flow-bin (项目安装的话,把–g替换成-D)
2.npm init -y(-y默认全部yes快速创建)创建package.json,在文件中scripts中添加:
“scripts":{
"flow":"flow"
}
- 使用命令
npm run flow init 在项目路文件的根目录创建一个.flowconfig文件
npm run flow check 对所有文件夹进行类型检查
npm run flow 启动Flow服务
flow 执行flow检查
npm run flow stop 停止flow服务
- 标记需要进行检查的文件:
在被检查的文件最顶部添加@flow标识的注释对该文件进行检查 - 编译移除注解
- **方法一:**安装flow-remove-types:
npm install --save-dev flow-remove-types
运行flow-remove-types移除注解:
yarn run flow-remove-types src/ -d lis/
src:需要移除注解的文件位置
lis:打包文件的存放位置
yarn run flow-remove-types . -d dist
**方法二:**运用babel操作编译
安装babel:npm install @babel/core @babel/cli @babel/preset-flow
配置babel:新建一个babelrc配置文件。文件内配置:
{
"presets":["@babel/preset-flow"]
}
flow便捷安装:编辑器插件flow
flow原始类型
string、number、boolean、null、void、symbol
flow对结构对象注释:
数组:Array和number[]都表示只有数字的数组
TypeScript
-
初始化资源包:
npm init 或者yarn init -
安装typscript:
yarn add typescript
typescript文件扩展名是ts -
编译ts文件:
yarn tsc 文件名
typescript配置
yarn tsc --init自动生成配置文件tsconfig.json
outDir—编译结果输出文件夹
rootDir—源码文件夹
原始类型:
const a:string = 'foo'
const b:number = 100 //NaN Infinity
const c:boolean = true //false
//string、number、boolean都可以为空null.但一般检测是否为空,可以再配置文件中设置strictNullChecks.
const e:void = undefined
const f:null = null
const g:undefined = undefined
错误消息语言设置
在tsc命令后输入语言包名称
yarn tsc --locale zh-CN
作用域问题
将文件都以模块方式引入。或者放在立即执行的函数局部作用域中。
Object类型
不单指对象,而是指除了原始类型外的其他所有类型。
const foo:object = function(){}
//也可以用字面量语法的方式直接指定对象的每个属性类型
const obj:{foo:string,bar:numer} = {foo:'fod', bar:123}
数组类型
const arr1:Array<number> = [1,2,3]
const arr2:number[] = [1,2,3]
元祖类型
对数组的每个值进行字面量指定类型。
枚举类型
enum关键字定义枚举对象的值。枚举的值用等号赋值而不是冒号。
枚举类型编译后不会移除,会保存为键值对对象。如果希望被移除,可定义为常量枚举对象。
enum PostStatus{
Draft = 0,
Unpublished = 1,
Published = 2
}
const post = {
title:'pushlish',
status:PostStatus.Published
}
函数类型
参数类型限制在参数后声明,返回值类型在参数括号后声明。可选参数在参数名后加问号,或者指定默认值(指定默认值的参数本身就是可选,但需要放在最后)。
function func1(a:number,b?:string):string{
retrun 'func1'
}
任意类型any
可存放任意类型的值。但仍然是动态类型。
类型断言
变量 as 类型。告诉js变量为某种类型。
接口
对对象结构的一种约束。编译之后并不会保留。
可选成员:?
只读成员:readonly
动态属性:[prop:动态属性名类型]
interface Post {
title:string
content:string
subtitle?:string
readonly sunmmer:string
[prop:string]:string
}
const post:Post = {}
post.foo = 'value'
类与接口
interface定义接口约束对象结构。类中通过Implements实现接口约束,多个接口用逗号分割。
interface EatAndRun {
eat(food:string):void
run(distance:number):viod
}
class Person implements EatAndRun {
eat(food:string):viod{
console.log(`优雅地进餐:${food}`)
}
run(distance:number):viol{
console.log(`直立行走${distance}`)
}
}
class Animal implements EatAndRun {
eat(food:string):viod{
console.log(`呼噜地吃:${food}`)
}
run(distance:number):viol{
console.log(`爬行${distance}`)
}
}
抽象类
abstract class 类名。继承也是通过extends。
泛型
定义时不指定类型,使用时才传递具体类型。比如Array可以是数字也可以是字符串,为了使代码大成都复用。将泛型类型当做参数传递。
function creatArray<T>(length:number,value:T):T[]{
const arr = Array<T>(length).file(value)
return arr
}
const res = creatArray<string>(3,'foo')
类型声明
对于有些外部引入的第三方文件,可能没有指定类型声明。可以通过declare进行自定义声明。
import {camelCase} form 'lodash'
declare function camelCase (input:string):string
const res = camelCase('hello type')
js性能优化
GC:垃圾回收机制
找到内存中的垃圾、并释放和回收空间
GC中的垃圾:
- 程序中不在需要使用的对象
- 程序中不能再访问到的对象
常见GC算法:
- 引用计数
- 标记清除
- 标记整理
- 分代回收
引用计数
设置引用数,判断当前引用数是否为0.引用关系改变时修改引用数,引用数为0时立即回收。
优点:
- 发现垃圾立即回收
- 最大限度减少程序暂停
缺点:
- 无法回收循环引用对象
- 时间开销大
标记清除算法
- 分标记和清除两个阶段
- 遍历所有对象找标记活动对象
- 遍历所有对象清除没有标记对象
- 回收相应空间
优点:
可解决循环引用问题
缺点:
空间碎片化
标记整理算法
- 是标记清除的增强
- 标记阶段的操作和标记清除一致
- 清除阶段会先执行整理,移动对象位置
V8
- 是JavaScript执行引擎
- 采用即时编译
- 内存设限
v8中常见GC算法
- 分代回收
- 空间复制
- 标记清除
- 标记整理
- 标记增量
内存分配
- V8内存空间一份为二
- 小空间用于存储新生代对象(32M|16M)
- 新生代指的是存活时间较短的对象
新生代对象回收实现:
- 回收过程采用复制算法+标记整理
- 新生代内存区分为两个等大小空间
- 使用空间为From,空闲空间为To
- 活动对象存储于From空间
- 标记整理后将活动对象拷贝至To
- From与To交换空间完成释放
- 拷贝过程中可能存在晋升
- 晋升就是将新生代对象移动至老生代
- 一轮GC还存活的新生代需要晋升
- To空间的使用率超过25%
老生代对象:
- 老生代对象存放在右侧老生代区域
- 64位操作系统1.4G,32位操作系统70M
- 老生代对象就是指存活时间较长的对象
- 主要采用标记清除、标记整理、增量标记算法
- 首先使用标记清除完成垃圾空间的回收
- 采用标记整理进行空间优化
- 采用增量标记进行效率优化
performance内存性能监测
可录制查看性能变化曲线
memory内存查看堆快照
代码优化
性能测试网站:https://jsperf.com
优化手段:
- 慎用全局变量
- 缓存全局变量
- 通过原型对象添加附加方法
- 避开闭包陷阱
- 避免属性访问方法使用,直接使用属性名访问
- for循环优化:
循环长度缓存,避免每次循环前都去查找需要遍历的对象和长度。
//优化前:
for(var i =0;i<aBtns.length;i++{
console.log(i)
}
//优化后
for(var i = 0,length = aBtns.length;i<length;i++){
console.log(i)
}
- 采用最优的循环方法
只是循环读取数据,并不操作时,性能:forEach>for>for in
- 文档碎片优化节点添加
循环添加节点时,将要添加的父节点缓存,避免每次循环都多次往上查找父节点。
- 克隆优化节点操作
克隆相似节点比直接创建新节点性能高
- 直接量替换new Object
//优化前
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3
//优化后
var a = [1,2,3]
-
减少判断层级
判断不满足条件的直接处理或者return,减少不满足条件还浪费时间去判断嵌套的条件逻辑处理;
多个else if 且对应具体的枚举值时,建议使用switch case。else if尽量只适用于区间条件判断。 -
减少作用域链查找层级
-
减少数据读取次数
将要频繁使用的值提前缓存
-
使用字面量替换构造式
字面量数据比构造式引用类型省时 -
减少循环体中活动
-
减少声明及语句数
不需要频繁使用的变量不提前声明,在使用时直接获取;
一次声明多个变量,减少语句数。
- 多次调用时,使用惰性函数性能更优。惰性载入表示函数执行的分支只会在函数第一次调用的时候执行。一般是在第一次执行时将返回值存储在变量中,执行时直接获取。
- 采用时间委托
JSBench
网址:jsbench .me
栈堆执行过程:
按照执行上下文的作用域链的原理,依次查找执行。当不再有外部引用时进行垃圾回收。