ES6
class类
在ES6(ESMASCript2015)新标准中使用了class关键字来直接定义类,但本质上依旧是构造函数、原型链的语法糖而已
class Person{// 构造函数constructor(name,age){this.name=name;this.age=age;}// 方法running(){console.log('running')}// 访问器get address(){return this._address;}set address(value){this._address=value;}// 静态方法static createPerson(){return new Person();}}
类的构造函数
每个类都可以有构造函数,这个方法的名称固定为
constructor每个类只能有一个构造函数,如果包含多个会抛出错误
类的方法
类的方法自动加入到原型对象中,无须担心占用额外内存
类的访问器方法
类似对象的访问器设置,可以对属性的获取和设置操作进行拦截处理
类的静态方法
可通过类对象调用其静态方法
extends关键字
类的继承
- JS引擎在解析子类时有要求,如果我们有实现继承,那么子类的构造方法中,在
this/return使用之前需调用父类构造方法,否则抛出错误
class Person{···}class Student extends Person{···}
super关键字
子类/派生类调用父类方法/构造函数
class Student extends Person{constructor(name,age,uid){super(name,age);this.uid=uid;}// 重写方法method(){// 调用父类方法super.method();console.log('stuMethod')}}
ES6转ES5
Babel · The compiler for next generation JavaScript (babeljs.io)
字面量增强(Enhanced object literals)
属性缩写(Property Shorthand)
let name='rv'let age=20let obj={name,age}
方法缩写(Method Shorthand)
let person={running(){console.log("running")}/*等价于running:function(){···}*/}
计算属性名(Computed Property Names)
let name='rv'let age=20let obj={[name+age](){console.log('abc')}}obj[name + age]
解构(Destructuring)
ES6解构赋值转ES5
/* ES6 解构赋值 */var nums[1,2,3]var [item1,item2,item2]=nums/* 转ES5代码 */var item1=nums[0]var item2=nums[1]var item3=nums[2]
数组解构
- 解构顺序按照数组索引顺序(可通过
','解构后面的元素) - 解构出数组可利用扩展运算符
- 解构可提供默认值
var nums=[1,2,3]/* 顺序解构 */var [item1,,item3]=numsconsole.log(item1,item3) // 1 3/* 解构出数组 */var [,...items]=numsconsole.log(items) // [2,3]/* 提供默认值 */var [,,,item4='a']=numsconsole.log(item4) // 'a'
对象解构
- 解构顺序任意,仅需属性名相同即可
- 解构属性可重命名
- 解构可提供默认值
var obj={name: 'rv',age: 20}/* 解构顺序任意 */var { age,name }=objconsole.log(name,age) // 'rv' 20/* 重命名 */var { age:myAge }=objconsole.log(myAge) // 20/* 默认值 */var {address='地球村'}=objconsole.log(address) // '地球村'
let/const
let
从直观讲,let与var效果相同,均为声明变量
只是let拥有块级作用域
相比var不可重复声明变量
const
const关键字是
constant的单词的缩写,表示常量、衡量的意思其被初始化后,值将不可更改(基本数据类型则不可修改,引用数据类型则地址索引不可修改)
相比var不可重复声明变量
let/const作用域提升问题
let、const和var的另一个重要区别是作用域提升
console.log(foo)/*ReferenceError:Cannot access 'foo' before initializationfoo不可在初始化之前访问*/let foo='foo'
let/const存在作用域提升吗
其实在ECMA262对let和const的描述中有如下:
这些变量会被创建在包含他们的词法环境被实例化时,但是是不可以访问它们的,直到词法绑定被求值;因此在执行上下文的词法环境创建出来的时候,变量事实上已经被创建了,只是这个变量是不能被访问的
let/const对window对象影响
众所周知,在全局作用域中使用var声明变量,会在window对象上添加属性,并存储在全局window对象上
但let/const并不会给window添加任何属性,而是会保存在VariableMap中
按照规范的描述则是:
声明的环境记录是被保存到变量环境中
环境记录:变量或者函数
变量环境:Variable Environment,在以前规范中称之为VO,现在则为VE
而规范中并没有规定变量环境是什么,在V8引擎中,是用
VariableMap(C++底层的hashmap)来实现变量的存储
暂时性死区
它表达的意思是一个代码中,使用let/const声明变量,在声明之前,变量都是不可以访问的,而这种现象称之为暂时性死区(TDZ,temporal dead zone)
块级作用域特殊情况
for,for of,for in的const初始值
在for循环中初始化变量,如果使用const,则循环递进条件条件中不可更改变量的值
而在for of,for in中则没有关系
/* for循环 */for(const i=0;i<10;i++){···}//TypeError: Assignment to constant variable./* for of,for in */var nums=[1,2,3,4]for(const i in nums){console.log(i)}
模板字符串
基本使用
- 支持多行字符串
- 支持变量和函数的插入
const name='rv'const age=()=>20const str=`my name is ${name},my age is ${age()}`
标签模块字符串基本使用
react中流行all in js,其中就有css in js就利用该技术调用函数处理
function foo(m,n){console.log(m,n)}foo`` // [''] undefinedfoo`hello` // ['hello'] undefinedconst name='rv'const age=20foo`he${name}ll${age}o` // ['he','ll','o'] 'rv'/*参数:参数1:模板字符串整个字符串,只是切成多块以数组形式传入参数2:模板字符串中第一个${}参数3...*/
函数中默认参数
基本使用
function foo(a='abc'){console.log(a)}foo() // 'abc'foo('cba') // 'cba'
对象参数的默认值与解构
// 方式一,效果等效于obj={name:'rv'},如果传入任何对象则不使用默认值function foo({name,age}={name:'rv'}){console.log(obj.name,obj.age)}// 方式二,可针对对象属性进行默认值,不会因为传入空对象而阻止默认值// 注意对象参数后的'={}',否则使用默认值和解构不规范,JS引擎解析不出function bar({name='rv',age}={}){console.log(name,age)}foo() // rv undefinedbar() // rv undefinedfoo({}) // undefined undefinedbar({}) // rv undefinedfoo({name:'abc'}) // abc undefinedbar({name:'abc'}) // abc undefinedfoo({age:20}) // undefined 20bar({age:20}) // rv 20
默认值对函数length值影响
/*从第一个默认值参数及以后的参数均不计入function.length中*/function foo1(a,b,c){}console.log(foo1.length) // 3function foo2(a,b,c=3){}console.log(foo2.length) // 2function foo3(a,b,c=3,d,e){}console.log(foo3.length) // 2
函数中剩余参数(rest parameter)
function foo(a,...args){console.log(a,args);}foo(1,2,3,4); // 1 [2,3,4]
rest parameter和arguments
剩余参数(rest parameter)
- 只包含那些没有对应形参的实参
- 真正的数组结构
- 需放置在参数最后,否则报错
- ES6中提供并希望以此代替arguments
arguments对象
- 包含了传给函数的所有实参
- 类数组对象结构
数值的表示
/* ES6规范了进制表示 */// 十进制const num1=100// 二进制const num2=0b100// 八进制const num3=0o100// 十六进制const num4=0x100console.log(num1,num2,num3,num4) //100 4 64 256/* ES12新增大数值连接符 */const num=10_000_000console.log(num) // 10000000
Symbol函数
Symbol的作用
ES6之前对象的键值只可为字符串,容易造成属性名冲突,导致覆盖原有属性
Symbol函数可生成独一无二的值,防止属性名出现冲突
const s1=Symbol()const s2=Symbol()console.log(s1===s2) // false
Symbol描述
ES10中,创建Symbol时还可以添加描述
const s3=Symbol('description')console.log(s3.description) // 'description'
Symbol作为属性名的使用方式
const s1=Symbol()const s2=Symbol()const s3=Symbol()// 方式一const obj={[s1]:'symbol1'}// 方式二obj[s2]='symbol2'// 方式三Object.defineProperty(obj,s3,{value:'symbol3'})// 获取方法console.log(obj[s1])
Symbol的遍历问题
使用Symbol作为属性名,在遍历(Object.keys()等)中是获取不到Symbol值的
但提供了Object.getOwnPropertySymbols()方法,获取作为属性名的Symbol的数组
Symbol.for(),Symbol.keyFor()
用于需要使Symbol在某些时候相等的情况
const s1=Symbol.for('a')const s2=Symbol.for('a')console.log(s1===s2) // trueconst s3=Symbol.for('b')console.log(s1===s3) // falseconsole.log(Symbol.keyFor(s1)) // 'a'
Set,Map
ES6之前,JavaScript存储结构只要有数组和对象
Set
Set是ES6新增的数据结构,类似数组,特征是其元素不能重复
Set常见方法
// 创建Setconst set=new Set()// size:获取Set长度set.size// add:添加元素set.add(1)// delete:删除元素(需传入元素)set.delete(1)// has:判断包含某属性set.has(1)// clear:清除set所有元素set.clear()// Set数据结构转数组结构Array.from(set)/* Set结构遍历 */// 方式一set.forEach(item=>{console.log(item)})// 方式二for(const item of set){console.log(item);}
WeakSet
WeakSet也是Set的一种,同样内部元素不能重复
Set,WeakSet主要区别
相比Set可存放任意类型元素,WeakSet只能存放对象元素
相比Set是强引用,WeakSet存放的对象是一个弱引用(即不计入GC回收器的引用)
强引用(strong reference)
弱引用(weak reference)
WeakSet常见方法
// 创建WeackSet数据结构const wset=new WeakSet()// add:添加元素,返回WeakSet自身wset.add({})// delete:删除元素,返回booleanwset.delete({})// has:判断存在元素,返回booleanwset.has({})/*WeakSet无法进行遍历,因为其为弱引用,因此无法进行获取元素*/
WeakSet应用场景
// 强制不能通过非类的构造方法创建出来的对象调用类方法const personSet=new WeakSet()class Person{constructor(){···personSet.add(this)}running(){if(!person.has(this)){throw new Error("不能通过非构造方法创建的对象调用该running方法")}···}}/* 虽然此处用Set可以实现同样的效果,但Set的强引用会导致GC无法回收对象实例 */
Map
Map是ES6新增数据结构,用来存储映射关系,类似于对象
Map,对象的区别
- 相比对象的key只能为字符串或Symbol,Map的key可以为任意值
Map常见方法
// 创建Mapconst map=new Map()// 创建Map,传入Entitryconst map2=new Map([key1,value1],[key2,value2])// size:获取长度map.size// set:新增映射关系map.set(key,value)// get:获取映射关系map.get(key)// has:判断key是否存在map.has(key)// delete:通过key删除映射关系,返回booleanmap.delete(key)// clear:清空mapmap.clear()/* 遍历操作 */// 方式一:forEachmap.forEach((value,key)=>{console.log(key,value)})// 方式二:for offor(const item of map){// 此处item为数组console.log(item[0],item[1])}for(const [key,value] of map){console.log(key,value)}
WeakMap
WeakMap和Map类型相似,同样是以键值对的形式存在
WeakMap,Map区别
- 相比Map的key可为任意值,WeakMap的key只能为对象(类比Set,WeakSet)
- WeakMap的key中对对象的引用为弱引用
WeakMap常见方法
// 创建WeakMapconst wmap=new WeakMap()// set:新增映射关系wmap.set(key,value)// get:获取映射关系wmap.get(key)// has:判断key是否存在wmap.has(key)// delete:通过key删除映射关系,返回booleanwmap.delete(key)/* 无法遍历 */
WeakMap应用场景
/* Vue3响应式原理 */const obj={name:'rv',age:20}function objNameFun(){}function objAgeFun(){}// 收集结构const wmap=new WeakMap()const map=new Map()// 收集操作map.set('name',[objNameFun])map.set('age',[objAgeFun])wmap.set(obj,map)// 当obj.name发生改变,进行监听(Proxy),再通过obj获取weakMap,再通过name获取函数,重新调用,实现响应式
ES7 (ESMA2016)
Array.prototype.includes()
查找数组中是否存在value元素,可选从index索引开始查找
用于替换曾经的
indexOf()方法,区别在于indexOf无法判断NaN元素,而includes可以
Array.prototype.includes(value,?index)
指数运算符**
const result1=Math.pow(3,3)const result2=3 ** 3console.log(result1===result2) // true
ES8 (ESMA2017)
Object.values()
类似Object.keys(),可以获取对象的所有value,同时可以将字符串转字符数组
// 获取对象所有valueObject.values({name:'rv',age:20}) // ['rv',20]// 获取数组本身Object.values(['rv',20]) // ['rv',20]// 将字符串转字符数组Object .values('abc') // ['a','b','c']
Object.entries()
获取entries数组
Object.entries({name:'rv',age:20})// [['name','rv'],['age',20]]Object.entries(["abc","cba"])// [['0','abc'],['1',cba]]Object.entries("abc")// [['0','a'],['1','b'],['2','c']]
字符串填充(String Padding)
字符串新增两个方法padStart(),padEnd(),用于给字符串首尾进行填充
const message='abc'/* 填充首部字符串至5长度 */const msg1=message.padStart(5)console.log(msg1) // ' abc'/* 规定填充符号 */const msg2=message.padEnd(5,'*')console.log(msg2) // 'abc**'
结尾逗号(Trailing Commas)
了解即可,ES8支持函数参数设置和调用时以逗号结尾
function foo(m,n,){}foo(1,2,)
Object.getOwnPropertyDescriptors()
获取对象自身所有属性描述符
Async Function: async,await
详见专题内容[]
ES9 (ESMA2018)
Async iteratora
详见专题内容[async-await]
对象展开运算符(Object spread operators)
上文已讲述该内容
Promise finally
详见专题内容[promise]
ES10 (ECMA2019)
flat flatMap
Array.prototype.flat()
Array.prototype.flat()可按照一个指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
(实现降维的目的)
const nums=[10,[20,[30,40],50]]/* 降维一次 */const nums1=nums.flat()console.log(nums1) // [10,20,[30,40],50]/* 多次降维 */const nums2=nums.flat(2)console.log(num2) // [10,20,30,40,50]
Array.prototype.flatMap()
Array.prototype.flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组
(类似map(),但会将结果降维成新数组)
const nums=[10,20,30,40]const nums1=nums.flatMap(item=>{return item*2})console.log(nums1) // [20,40,60,80]const msg=['abc','a b c']const msg1=msg.flatMap(item=>{item.splice(' ')})console.log(msg1)// ['abc','a','b','c']
Object fromEntries
ES8中提出Object.entries(),可将对象转成entry数组
而此方法Object.fromEntries()为其逆向方法,可将entry数组转成对象
/* 应用场景:解析URL参数 */const queryStr='name=rv&age=20'const queryParams=new URLSearchParams(queryStr)console.log(queryParams) // [['name','rv'],['age',20]]const paramObj=Object.fromEntries(queryParams)console.log(paramObj) // {name:'rv',age:20}
trimStart,trimEnd
类似String.prototype.trim()
Symbol description
上文
Symbol板块已有,用于给Symbol添加描述
Optional catch binding
详见专题内容[try-catch]
ES11 (ECMA2020)
BigInt
早期JavaScript里无法正确表示过大数字,number类型最大精度只有Number.MAX_SAFE_INTEGER(9007_1992_5474_0991)
const bigInt=1_0000_0000_0000_0000nconsole.log(bigInt) // 10000000000000000n
BigInt无法与其他类型进行计算
JavaScript之所以允许1+'a'等计算,是因为其存在隐式转换,而当前(2022年)还不支持bigint的隐式转换
console.log(10n+10)// TypeError: Cannot mix BigInt and other types, use explicit conversions(显示转换)console.log(10n+BigInt(10)) // 20n
空值合并运算符??(Nullish Coalescing Operator)
const foo=0/*ES11以前默认值赋值使用或逻辑运算会将0,''等转换成布尔false值,无法正确判断空值*/const bar1=foo||'default value'console.log(bar1) // 'default value'/*ES11的空值合并运算符 ??可以解决这个问题*/const bar2=foo??'defalut value'console.log(bar2) // 0
可选链?.(Optional Chaining)
主要作用是让代码在进行null和undefined判断时更加清晰简洁
console.log(null.method) // 报错console.log(null?.method) // undefined
获取全局对象(Global This)
JavaScript在不同环境下获取全局对象方法是不同的
- 浏览器环境:
全局作用域的this,window对象 - Node环境:
global对象
/* ES11以前兼容不同环境获取全局对象 */function golbalObject(){if(window!==undefined){return window}else{return global}}/* ES11新增globalThis */console.log(globalThis)
for-in标准化
以前ECMA中并没有for in的规范,如今ES11中对其规范话,规定循环的值获取为key
动态导入(Dynamic Import)
详细看专题[ES Module模块化]
Promise.allSettled
详细看专题[promise]
import meta
详细看专题[ES Module模块化]
ES12 (ECMA2021)
FinalizationResgistry
新增类FinalizationRegistry,可用于监听注册对象的回收操作
const finalRegistry=new FinalizationRegistry((value)=>{/*创建对象时可传入callback当finalRegistry内注册的对象回收时,就会执行此回调函数*/console.log(`${value}对象已被GC回收`)})let obj={name:'rv'}let foo={age:20}// 注册时可传入value,在回调函数中可获取区分回收的对象finalRegistry.register(obj,'obj')finalRegistry.register(foo,'foo')obj=nullfoo=null
弱引用(WeakRef)
新增类WeakRef,可用于创建弱引用
let obj={name:'rv'}// 创建弱引用,可使用FinalizationRegistry验证let info=new WeakRef(obj)// 获取引用对象,无法直接获取info.name // undefinedinfo.deref().name // 'rv'
逻辑赋值运算(logical assign operator)
||=逻辑或赋值运算&&=逻辑与赋值运算??=逻辑空赋值运算
/* 理解类比 +和+= */let num=1n=n+1n+=1/* ||=逻辑或赋值运算 */let foo=undefinedfoo=foo||'default value'foo||='default value'/* &&=逻辑与赋值运算 */// 由于逻辑与运算没啥赋值的使用情况,因此使用频率极低,没啥应用场景/* ??=逻辑空赋值运算 */// 使用场景同空值合并运算符,可用于替换逻辑或赋值运算无法正确判断空值情况let bar=0bar=bar??'default value'bar??='default value'
数字分割符_(Numeric Separator)
const num=1_00_000_0000
