ES6

class类

在ES6(ESMASCript2015)新标准中使用了class关键字来直接定义类,但本质上依旧是构造函数、原型链的语法糖而已

  1. class Person{
  2. // 构造函数
  3. constructor(name,age){
  4. this.name=name;
  5. this.age=age;
  6. }
  7. // 方法
  8. running(){
  9. console.log('running')
  10. }
  11. // 访问器
  12. get address(){
  13. return this._address;
  14. }
  15. set address(value){
  16. this._address=value;
  17. }
  18. // 静态方法
  19. static createPerson(){
  20. return new Person();
  21. }
  22. }

类的构造函数

  • 每个类都可以有构造函数,这个方法的名称固定为constructor

  • 每个类只能有一个构造函数,如果包含多个会抛出错误

类的方法

类的方法自动加入到原型对象中,无须担心占用额外内存

类的访问器方法

类似对象的访问器设置,可以对属性的获取和设置操作进行拦截处理

类的静态方法

可通过类对象调用其静态方法

extends关键字

类的继承

  • JS引擎在解析子类时有要求,如果我们有实现继承,那么子类的构造方法中,在this/return使用之前需调用父类构造方法,否则抛出错误
  1. class Person{···}
  2. class Student extends Person{···}

super关键字

子类/派生类调用父类方法/构造函数

  1. class Student extends Person{
  2. constructor(name,age,uid){
  3. super(name,age);
  4. this.uid=uid;
  5. }
  6. // 重写方法
  7. method(){
  8. // 调用父类方法
  9. super.method();
  10. console.log('stuMethod')
  11. }
  12. }

ES6转ES5

Babel · The compiler for next generation JavaScript (babeljs.io)

字面量增强(Enhanced object literals)

属性缩写(Property Shorthand)

  1. let name='rv'
  2. let age=20
  3. let obj={
  4. name,
  5. age
  6. }

方法缩写(Method Shorthand)

  1. let person={
  2. running(){
  3. console.log("running")
  4. }
  5. /*
  6. 等价于
  7. running:function(){···}
  8. */
  9. }

计算属性名(Computed Property Names)

  1. let name='rv'
  2. let age=20
  3. let obj={
  4. [name+age](){
  5. console.log('abc')
  6. }
  7. }
  8. obj[name + age]

解构(Destructuring)

ES6解构赋值转ES5

  1. /* ES6 解构赋值 */
  2. var nums[1,2,3]
  3. var [item1,item2,item2]=nums
  4. /* 转ES5代码 */
  5. var item1=nums[0]
  6. var item2=nums[1]
  7. var item3=nums[2]

数组解构

  • 解构顺序按照数组索引顺序(可通过','解构后面的元素)
  • 解构出数组可利用扩展运算符
  • 解构可提供默认值
  1. var nums=[1,2,3]
  2. /* 顺序解构 */
  3. var [item1,,item3]=nums
  4. console.log(item1,item3) // 1 3
  5. /* 解构出数组 */
  6. var [,...items]=nums
  7. console.log(items) // [2,3]
  8. /* 提供默认值 */
  9. var [,,,item4='a']=nums
  10. console.log(item4) // 'a'

对象解构

  • 解构顺序任意,仅需属性名相同即可
  • 解构属性可重命名
  • 解构可提供默认值
  1. var obj={
  2. name: 'rv',
  3. age: 20
  4. }
  5. /* 解构顺序任意 */
  6. var { age,name }=obj
  7. console.log(name,age) // 'rv' 20
  8. /* 重命名 */
  9. var { age:myAge }=obj
  10. console.log(myAge) // 20
  11. /* 默认值 */
  12. var {address='地球村'}=obj
  13. console.log(address) // '地球村'

let/const

let

  • 从直观讲,let与var效果相同,均为声明变量

  • 只是let拥有块级作用域

  • 相比var不可重复声明变量

const

  • const关键字是constant的单词的缩写,表示常量、衡量的意思

  • 其被初始化后,值将不可更改(基本数据类型则不可修改,引用数据类型则地址索引不可修改)

  • 相比var不可重复声明变量

let/const作用域提升问题

let、const和var的另一个重要区别是作用域提升

  1. console.log(foo)
  2. /*
  3. ReferenceError:Cannot access 'foo' before initialization
  4. foo不可在初始化之前访问
  5. */
  6. 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中则没有关系

  1. /* for循环 */
  2. for(const i=0;i<10;i++){
  3. ···
  4. }
  5. //TypeError: Assignment to constant variable.
  6. /* for of,for in */
  7. var nums=[1,2,3,4]
  8. for(const i in nums){
  9. console.log(i)
  10. }

模板字符串

基本使用

  • 支持多行字符串
  • 支持变量和函数的插入
  1. const name='rv'
  2. const age=()=>20
  3. const str=`
  4. my name is ${name},
  5. my age is ${age()}
  6. `

标签模块字符串基本使用

react中流行all in js,其中就有css in js就利用该技术调用函数处理

  1. function foo(m,n){
  2. console.log(m,n)
  3. }
  4. foo`` // [''] undefined
  5. foo`hello` // ['hello'] undefined
  6. const name='rv'
  7. const age=20
  8. foo`he${name}ll${age}o` // ['he','ll','o'] 'rv'
  9. /*
  10. 参数:
  11. 参数1:模板字符串整个字符串,只是切成多块以数组形式传入
  12. 参数2:模板字符串中第一个${}
  13. 参数3...
  14. */

函数中默认参数

基本使用

  1. function foo(a='abc'){
  2. console.log(a)
  3. }
  4. foo() // 'abc'
  5. foo('cba') // 'cba'

对象参数的默认值与解构

  1. // 方式一,效果等效于obj={name:'rv'},如果传入任何对象则不使用默认值
  2. function foo({name,age}={name:'rv'}){
  3. console.log(obj.name,obj.age)
  4. }
  5. // 方式二,可针对对象属性进行默认值,不会因为传入空对象而阻止默认值
  6. // 注意对象参数后的'={}',否则使用默认值和解构不规范,JS引擎解析不出
  7. function bar({name='rv',age}={}){
  8. console.log(name,age)
  9. }
  10. foo() // rv undefined
  11. bar() // rv undefined
  12. foo({}) // undefined undefined
  13. bar({}) // rv undefined
  14. foo({name:'abc'}) // abc undefined
  15. bar({name:'abc'}) // abc undefined
  16. foo({age:20}) // undefined 20
  17. bar({age:20}) // rv 20

默认值对函数length值影响

  1. /*
  2. 从第一个默认值参数及以后的参数均不计入function.length中
  3. */
  4. function foo1(a,b,c){}
  5. console.log(foo1.length) // 3
  6. function foo2(a,b,c=3){}
  7. console.log(foo2.length) // 2
  8. function foo3(a,b,c=3,d,e){}
  9. console.log(foo3.length) // 2

函数中剩余参数(rest parameter)

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

rest parameter和arguments

剩余参数(rest parameter)

  • 只包含那些没有对应形参的实参
  • 真正的数组结构
  • 需放置在参数最后,否则报错
  • ES6中提供并希望以此代替arguments

arguments对象

  • 包含了传给函数的所有实参
  • 类数组对象结构

数值的表示

  1. /* ES6规范了进制表示 */
  2. // 十进制
  3. const num1=100
  4. // 二进制
  5. const num2=0b100
  6. // 八进制
  7. const num3=0o100
  8. // 十六进制
  9. const num4=0x100
  10. console.log(num1,num2,num3,num4) //100 4 64 256
  11. /* ES12新增大数值连接符 */
  12. const num=10_000_000
  13. console.log(num) // 10000000

Symbol函数

Symbol的作用

ES6之前对象的键值只可为字符串,容易造成属性名冲突,导致覆盖原有属性

Symbol函数可生成独一无二的值,防止属性名出现冲突

  1. const s1=Symbol()
  2. const s2=Symbol()
  3. console.log(s1===s2) // false

Symbol描述

ES10中,创建Symbol时还可以添加描述

  1. const s3=Symbol('description')
  2. console.log(s3.description) // 'description'

Symbol作为属性名的使用方式

  1. const s1=Symbol()
  2. const s2=Symbol()
  3. const s3=Symbol()
  4. // 方式一
  5. const obj={
  6. [s1]:'symbol1'
  7. }
  8. // 方式二
  9. obj[s2]='symbol2'
  10. // 方式三
  11. Object.defineProperty(obj,s3,{
  12. value:'symbol3'
  13. })
  14. // 获取方法
  15. console.log(obj[s1])

Symbol的遍历问题

使用Symbol作为属性名,在遍历(Object.keys()等)中是获取不到Symbol值的

但提供了Object.getOwnPropertySymbols()方法,获取作为属性名的Symbol的数组

Symbol.for(),Symbol.keyFor()

用于需要使Symbol在某些时候相等的情况

  1. const s1=Symbol.for('a')
  2. const s2=Symbol.for('a')
  3. console.log(s1===s2) // true
  4. const s3=Symbol.for('b')
  5. console.log(s1===s3) // false
  6. console.log(Symbol.keyFor(s1)) // 'a'

Set,Map

ES6之前,JavaScript存储结构只要有数组和对象

Set

Set是ES6新增的数据结构,类似数组,特征是其元素不能重复

Set常见方法

  1. // 创建Set
  2. const set=new Set()
  3. // size:获取Set长度
  4. set.size
  5. // add:添加元素
  6. set.add(1)
  7. // delete:删除元素(需传入元素)
  8. set.delete(1)
  9. // has:判断包含某属性
  10. set.has(1)
  11. // clear:清除set所有元素
  12. set.clear()
  13. // Set数据结构转数组结构
  14. Array.from(set)
  15. /* Set结构遍历 */
  16. // 方式一
  17. set.forEach(item=>{
  18. console.log(item)
  19. })
  20. // 方式二
  21. for(const item of set){
  22. console.log(item);
  23. }

WeakSet

WeakSet也是Set的一种,同样内部元素不能重复

Set,WeakSet主要区别

  1. 相比Set可存放任意类型元素,WeakSet只能存放对象元素

  2. 相比Set是强引用,WeakSet存放的对象是一个弱引用(即不计入GC回收器的引用)

    强引用(strong reference)

弱引用(weak reference)

WeakSet常见方法

  1. // 创建WeackSet数据结构
  2. const wset=new WeakSet()
  3. // add:添加元素,返回WeakSet自身
  4. wset.add({})
  5. // delete:删除元素,返回boolean
  6. wset.delete({})
  7. // has:判断存在元素,返回boolean
  8. wset.has({})
  9. /*
  10. WeakSet无法进行遍历,因为其为弱引用,因此无法进行获取元素
  11. */

WeakSet应用场景

  1. // 强制不能通过非类的构造方法创建出来的对象调用类方法
  2. const personSet=new WeakSet()
  3. class Person{
  4. constructor(){
  5. ···
  6. personSet.add(this)
  7. }
  8. running(){
  9. if(!person.has(this)){
  10. throw new Error("不能通过非构造方法创建的对象调用该running方法")
  11. }
  12. ···
  13. }
  14. }
  15. /* 虽然此处用Set可以实现同样的效果,但Set的强引用会导致GC无法回收对象实例 */

Map

Map是ES6新增数据结构,用来存储映射关系,类似于对象

Map,对象的区别

  • 相比对象的key只能为字符串或Symbol,Map的key可以为任意值

Map常见方法

  1. // 创建Map
  2. const map=new Map()
  3. // 创建Map,传入Entitry
  4. const map2=new Map(
  5. [key1,value1],
  6. [key2,value2]
  7. )
  8. // size:获取长度
  9. map.size
  10. // set:新增映射关系
  11. map.set(key,value)
  12. // get:获取映射关系
  13. map.get(key)
  14. // has:判断key是否存在
  15. map.has(key)
  16. // delete:通过key删除映射关系,返回boolean
  17. map.delete(key)
  18. // clear:清空map
  19. map.clear()
  20. /* 遍历操作 */
  21. // 方式一:forEach
  22. map.forEach((value,key)=>{
  23. console.log(key,value)
  24. })
  25. // 方式二:for of
  26. for(const item of map){
  27. // 此处item为数组
  28. console.log(item[0],item[1])
  29. }
  30. for(const [key,value] of map){
  31. console.log(key,value)
  32. }

WeakMap

WeakMap和Map类型相似,同样是以键值对的形式存在

WeakMap,Map区别

  • 相比Map的key可为任意值,WeakMap的key只能为对象(类比Set,WeakSet)
  • WeakMap的key中对对象的引用为弱引用

WeakMap常见方法

  1. // 创建WeakMap
  2. const wmap=new WeakMap()
  3. // set:新增映射关系
  4. wmap.set(key,value)
  5. // get:获取映射关系
  6. wmap.get(key)
  7. // has:判断key是否存在
  8. wmap.has(key)
  9. // delete:通过key删除映射关系,返回boolean
  10. wmap.delete(key)
  11. /* 无法遍历 */

WeakMap应用场景

  1. /* Vue3响应式原理 */
  2. const obj={
  3. name:'rv',
  4. age:20
  5. }
  6. function objNameFun(){}
  7. function objAgeFun(){}
  8. // 收集结构
  9. const wmap=new WeakMap()
  10. const map=new Map()
  11. // 收集操作
  12. map.set('name',[objNameFun])
  13. map.set('age',[objAgeFun])
  14. wmap.set(obj,map)
  15. // 当obj.name发生改变,进行监听(Proxy),再通过obj获取weakMap,再通过name获取函数,重新调用,实现响应式

ES7 (ESMA2016)

Array.prototype.includes()

  • 查找数组中是否存在value元素,可选从index索引开始查找

  • 用于替换曾经的indexOf()方法,区别在于indexOf无法判断NaN元素,而includes可以

  1. Array.prototype.includes(value,?index)

指数运算符**

  1. const result1=Math.pow(3,3)
  2. const result2=3 ** 3
  3. console.log(result1===result2) // true

ES8 (ESMA2017)

Object.values()

类似Object.keys(),可以获取对象的所有value,同时可以将字符串转字符数组

  1. // 获取对象所有value
  2. Object.values({name:'rv',age:20}) // ['rv',20]
  3. // 获取数组本身
  4. Object.values(['rv',20]) // ['rv',20]
  5. // 将字符串转字符数组
  6. Object .values('abc') // ['a','b','c']

Object.entries()

获取entries数组

  1. Object.entries({name:'rv',age:20})
  2. // [['name','rv'],['age',20]]
  3. Object.entries(["abc","cba"])
  4. // [['0','abc'],['1',cba]]
  5. Object.entries("abc")
  6. // [['0','a'],['1','b'],['2','c']]

字符串填充(String Padding)

字符串新增两个方法padStart(),padEnd(),用于给字符串首尾进行填充

  1. const message='abc'
  2. /* 填充首部字符串至5长度 */
  3. const msg1=message.padStart(5)
  4. console.log(msg1) // ' abc'
  5. /* 规定填充符号 */
  6. const msg2=message.padEnd(5,'*')
  7. console.log(msg2) // 'abc**'

结尾逗号(Trailing Commas)

了解即可,ES8支持函数参数设置和调用时以逗号结尾

  1. function foo(m,n,){}
  2. 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()可按照一个指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

(实现降维的目的)

  1. const nums=[10,[20,[30,40],50]]
  2. /* 降维一次 */
  3. const nums1=nums.flat()
  4. console.log(nums1) // [10,20,[30,40],50]
  5. /* 多次降维 */
  6. const nums2=nums.flat(2)
  7. console.log(num2) // [10,20,30,40,50]

Array.prototype.flatMap()

Array.prototype.flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组

(类似map(),但会将结果降维成新数组)

  1. const nums=[10,20,30,40]
  2. const nums1=nums.flatMap(item=>{
  3. return item*2
  4. })
  5. console.log(nums1) // [20,40,60,80]
  6. const msg=['abc','a b c']
  7. const msg1=msg.flatMap(item=>{
  8. item.splice(' ')
  9. })
  10. console.log(msg1)// ['abc','a','b','c']

Object fromEntries

ES8中提出Object.entries(),可将对象转成entry数组

而此方法Object.fromEntries()为其逆向方法,可将entry数组转成对象

  1. /* 应用场景:解析URL参数 */
  2. const queryStr='name=rv&age=20'
  3. const queryParams=new URLSearchParams(queryStr)
  4. console.log(queryParams) // [['name','rv'],['age',20]]
  5. const paramObj=Object.fromEntries(queryParams)
  6. 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)

  1. const bigInt=1_0000_0000_0000_0000n
  2. console.log(bigInt) // 10000000000000000n

BigInt无法与其他类型进行计算

JavaScript之所以允许1+'a'等计算,是因为其存在隐式转换,而当前(2022年)还不支持bigint的隐式转换

  1. console.log(10n+10)
  2. // TypeError: Cannot mix BigInt and other types, use explicit conversions(显示转换)
  3. console.log(10n+BigInt(10)) // 20n

空值合并运算符??(Nullish Coalescing Operator)

  1. const foo=0
  2. /*
  3. ES11以前默认值赋值
  4. 使用或逻辑运算会将0,''等转换成布尔false值,无法正确判断空值
  5. */
  6. const bar1=foo||'default value'
  7. console.log(bar1) // 'default value'
  8. /*
  9. ES11的空值合并运算符 ??
  10. 可以解决这个问题
  11. */
  12. const bar2=foo??'defalut value'
  13. console.log(bar2) // 0

可选链?.(Optional Chaining)

主要作用是让代码在进行nullundefined判断时更加清晰简洁

  1. console.log(null.method) // 报错
  2. console.log(null?.method) // undefined

获取全局对象(Global This)

JavaScript在不同环境下获取全局对象方法是不同的

  • 浏览器环境:全局作用域的this,window对象
  • Node环境:global对象
  1. /* ES11以前兼容不同环境获取全局对象 */
  2. function golbalObject(){
  3. if(window!==undefined){
  4. return window
  5. }else{
  6. return global
  7. }
  8. }
  9. /* ES11新增globalThis */
  10. 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,可用于监听注册对象的回收操作

  1. const finalRegistry=new FinalizationRegistry((value)=>{
  2. /*
  3. 创建对象时可传入callback
  4. 当finalRegistry内注册的对象回收时,就会执行此回调函数
  5. */
  6. console.log(`${value}对象已被GC回收`)
  7. })
  8. let obj={name:'rv'}
  9. let foo={age:20}
  10. // 注册时可传入value,在回调函数中可获取区分回收的对象
  11. finalRegistry.register(obj,'obj')
  12. finalRegistry.register(foo,'foo')
  13. obj=null
  14. foo=null

弱引用(WeakRef)

新增类WeakRef,可用于创建弱引用

  1. let obj={name:'rv'}
  2. // 创建弱引用,可使用FinalizationRegistry验证
  3. let info=new WeakRef(obj)
  4. // 获取引用对象,无法直接获取
  5. info.name // undefined
  6. info.deref().name // 'rv'

逻辑赋值运算(logical assign operator)

  • ||=逻辑或赋值运算
  • &&=逻辑与赋值运算
  • ??=逻辑空赋值运算
  1. /* 理解类比 +和+= */
  2. let num=1
  3. n=n+1
  4. n+=1
  5. /* ||=逻辑或赋值运算 */
  6. let foo=undefined
  7. foo=foo||'default value'
  8. foo||='default value'
  9. /* &&=逻辑与赋值运算 */
  10. // 由于逻辑与运算没啥赋值的使用情况,因此使用频率极低,没啥应用场景
  11. /* ??=逻辑空赋值运算 */
  12. // 使用场景同空值合并运算符,可用于替换逻辑或赋值运算无法正确判断空值情况
  13. let bar=0
  14. bar=bar??'default value'
  15. bar??='default value'

数字分割符_(Numeric Separator)

  1. const num=1_00_000_0000

String.prototype.replaceAll