ES6(2015)~ES14(2023)语法新特性

一、ECMAScript相关介绍
ECMAScript 是由 Ecma 国际通过ECMA-262 标准化的脚本程序设计语言。平时经常编写的JavaScript,是ECMAScript 的一种实现,所以ES新特性其实指的就是JavaScript的新特性。

ES6 加入许多新的语法特性,使编程的实现更简单、高效

ES标准列表:

http://www.ecma-international.org/publications/standards/Standard.htm

ES兼容性:

ECMAScript 6 compatibility table
二、ES6新特性
(一)let关键字
1、同一变量不能重复声明,var可以。
//报错 ​ let a=1; ​ let a=2;

2、let遵循块级作用域(全局声明的let变量不会变成windows属性)。
 ES5有三种作用域:全局、函数、eval。ES6新增块级作用域。
3、不存在变量提升。
  不允许在声名之前使用,会报错。但var可以,在声明之前使用会有一个初始值undefined。
4、不影响作用域链。
let a=1;
function fn(){
    console.log(a);
}
fn();
块级作用域:除了对象 {},函数 {}(函数作用域)之外的一切 {} 都属于块级作用域。

作用域链:内部函数访问外部函数的变量,采取的是就近链式查找的方式来决定最终取那个值。内层作用域 ——> 外层作用域 ——> 全局作用域

(二)const关键字(定义常量、多用于数组和对象
1、一定要赋初始值。

2、一般常量名使用大写。

3、常量的值不能修改。

4、const也遵循块级作用域。

5、对于数组和对象的修改,不算做对常量的修改,不会报错。

const array1=['a','b','c']; ​ array1.push('c');

(三)变量解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,即为解构赋值(个人认为有点像二维数组)。适用于频繁使用的对象方法、数组元素。

数组解构赋值:

const SEA=['a','b','c','d']; let [aa,bb,cc,dd]=SEA; console.log(aa); console.log(bb); console.log(cc); console.log(dd);

对象解构赋值:

const object={ name:'Rose', age:'18', herWork:function(){ console.log('Hi'); }, herStory(){ console.log('Bye'); } } let {name,age,herWork}=object; let {herStory}=object;

(四)模板字符串
新的声明字符串的方式 ``:①在模板字符串里面可以直接使用enter换行,无需使用 <br>

//变量拼接

let one='小猪';

let two=${one}佩奇;

(五)简化对象写法
ES6允许在{ }里面直接写入变量和函数,作为对象的属性和方法。

let name='魔法学院'; let address='earth';

const school={ name, //name:name, address }

phone(){}//phone:function(){}

(六)箭头函数
ES6允许使用箭头定义函数,(参数)=>函数体。

箭头函数的作用:1、箭头函数比函数表达式更简洁。

                         2、解决 this 的指向问题。箭头函数不会创建自己的this,它只会从自己的作用域链向上一层继承this。
( call() 方法可以改变函数内部this的值)

let fn=function(){

}

let fn=()=>{

} 箭头函数特性:①this是静态的,this始终指向函数声明时所在作用域下的this的值。

                     ②不能作为构造函数实例化对象。

                     ③不能使用arguments变量。

                          (arguemnts是函数的内置对象,存储其调用函数时传入的所有实参)
//②报错 let Person=(name,age)=>{ this.name=name; this.age=age; } let me=new Person('Mei',20); console.log(me) 当箭头函数的形参有且仅有一个时可以省略();当函数体只有一句执行语句时,{ }可以省略,此时return也必须省略,而且语句的执行结果就是函数的返回值。

箭头函数适用于与this无关的回调,eg:定时器、数组的方法回调

箭头函数不适用于与this有关的回调,eg:事件回调、对象的方法

(七)函数参数的默认值
ES6允许给函数的形参赋初始值,且默认值可以与解构赋值结合。(具有默认值的参数,一般位置要靠后)。

function add(a,b,c=1){ return a+b+c; } let result=add(1,2); console.log(result);//result==4

(八)rest参数(剩余参数)
ES6引入rest参数,用于获取函数的实参,用来代替arguments。(...变量名)

rest参数必须要放到参数最后,只能是最后一个参数。

function data(...gain){ console.log(gain); } data('a','b','c'); 剩余语法(Rest syntax 也可以叫剩余参数)看起来和展开语法完全相同都是使用 ... 的语法糖,不同之处在于剩余参数用于解构数组和对象。从某种意义上说,剩余语法与展开语法是相反的:展开语法将数组展开为其中的各个元素,而剩余语法则是将多个元素收集起来成为一个整体。

剩余语法看起来和展开语法完全相同,不同点在于,剩余参数用于解构数组和对象。

剩余参数是为了能替代函数内部的 arguments 而引入的; 和展开语法相反,剩余参数是将多个单个元素聚集起来形成一个单独的个体的过程。

(九)扩展运算符
"..."能将 数组 转换为逗号分隔的 参数序列。

const singers=['周杰伦','五月天','周深'] function grand(){ console.log(arguments); } grand(...singers); 扩展运算符的应用: ①数组合并 const color=['pink','green','blue'] const fruits=['banana','apple','orange','watermelon'] const colorfulFruits=[...color,...fruits] ②数组克隆(浅拷贝) const mood=['hao','henhao','feichanghao'] const mood2=[...mood] ③将伪数组转为真正的数组

​​

(十)symbol
ES6引入一种新的原始数据类型Symbol,表示独一无二的值,是动态形成的。

1、symbol的特点:
①symbol值是唯一的,用来解决命名冲突的问题。
                           ②symbol的值不能与其他数据类型进行运算。

                           ③symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
2、Symbol创建:
//函数 let a=Symbol();

//对象 let b=Symbol.for();

3、向对象添加Symbol类型的属性和方法:
//方法一
//向game对象中添加Symbol类型的方法up down
let game={}
let methods={
    up:Symbol(),
    down:Symbol()
}
game[methods.up,methods.down]=function(){
      console.log()
}

//方法二
let study={
    name:"good",
    [Symbol()]:function(){
}
}
4、Symbol内置属性
方法 描述

Symbol.hasInstance 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法

Symbol.isConcatSpreadable 对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于Array.prototype.concat() 时,是否可以展开

Symbol.species 创建衍生对象时,会使用该属性

Symbol.match 当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。

Symbol.replace 当该对象被 str.replace(myObject) 方法调用时,会返回该方法的返回值。 Symbol.search 当该对象被 str.search(myObject) 方法调用时,会返回该方法的返回值。 Symbol.split 当该对象被 str.split(myObject) 方法调用时,会返回该方法的返回值。 Symbol.iterator 对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器 Symbol.toPrimitive 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

Symbol. toStringTag 在该对象上面调用 toString() 方法时,返回该方法的返回值 Symbol. unscopables 该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。

(十一)迭代器 Iterator
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完后遍历操作。

1、ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
const eat=['啤酒','炸鸡','我好饿'] //使用for...of遍历数组(for...in获取的是数组值的索引) for(let v of eat){ console.log(v); }

2、原生具备Iterator接口的数据类型:array、arguments、set、map、string、typedArray、nodeList。
3、迭代器工作原理:
a)创建一个指针对象,指向当前数据结构的起始位置;

b)第一次调用对象的next方法,指针自动指向数据结构的第一个成员;

c)接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员;

d)每调用next方法返回一个包含value和done属性的对象。

迭代器适用于自定义遍历数据。

    // 声明一个对象
        const cartoon={
            name:'xiaoLiYu',
            mbrs:[
                'xialiyu',
                'laipishe',
                'xiaomei'
            ],
            [Symbol.iterator](){
                let index=0;
                let _this=this;
                return{
                    next:function(){
                        if(index<_this.mbrs.length){
                            const result={value:_this.mbrs[index],done:false}
                            index++;
                            return result;
                        }else{
                            return {value:undefined,done:true}
                        }
                    }
                }
            }
        }
    // 遍历对象
    for(let v of cartoon){
        console.log(v);
    }
(十二)生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。传统函数所实现异步编程的方式是回调函数。

1、生成器函数的特殊点:声明时要加 * ,调用时需要使用 next 方法,yield 语句可以看作是分隔符,每调用一次next,就执行一块yield。
function * fnc(){ yield '一只小猪'; yield '被烤了'; } let iterator=fnc();

iterator.next(); iterator.next();

2、生成器函数参数
在next()内可以传入实参,其结果会在yield中调用

// 模拟获取用户数据、订单数据、商品数据 function getUsers(){ setTimeout(()=>{ let data='用户数据' // 调用next方法,并且将数据传入 iterator.next(data) },1000) }

    function getOrders(){
        setTimeout(()=>{
            let data='订单数据'
        },1000)
    }
 
    function getGoods(){
        setTimeout(()=>{
            let data='商品数据'
        },1000)
    }
 
    function *gen(){
      let users= yield getUsers();
      console.log(users);
       yield getOrders();
       yield getGoods()
    }
 
    // 调用生成器函数
    let iterator=gen()
    iterator.next()
(十三)Promise
Promise是ES6引入的异步编程的新的解决方案(即解决层层嵌套的回调函数(回调地狱)的问题)。语法上promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

//Promise基本语法 // 实例化Promise对象 const p=new Promise(function(resolve,reject){ setTimeout(function(){ //成功 // let data='数据库中的用户数据' // // resolve成功函数 // resolve(data)

            // 失败
            let err='数据读取失败'
            reject(err)
        },1000)
    })
 
    // 调用Promise对象的then方法    value成功的实参  reason失败的实参
    p.then(function(value){
        console.log(value);//成功
    },function(reason){
        console.log(reason);//失败
    })
1、Promise的三种状态:Pending初始状态、resolved成功(fulfilled)、rejected失败。
2、Promise的then方法的链式调用:
p.then(value=>{},reason=>{}).then(value=>{},reason=>{})

3、promise的catch方法:用于指定promise对象失败的回调。
const p=new Promise((resolve,reject)=>{ setTimeout(()=>{ // 仅设置对象的状态失败 reject("出错啦!") },1000) })

    // p.then(function(value){},function(reason){
    //     console.error(reason);
    // })
 
    p.catch(function(reason){
        console.error(reason);
    })
补:console.log:输出普通信息

   console.info输出提示性信息

   console.error输出错误信息

   console.warn输出警告信息

   console.debug输出调试信息 
(十四)集合
ES6提供了新的数据结构Set(集合)。类似于数组,但成员的值都是唯一的,集合实现了Iterator接口,所以可以使用扩展运算符和for...of...进行遍历。

1、集合set的属性和方法:
size 返回集合的元素个数

add 增加一个新元素,返回当前集合

delete 删除元素,返回boolean值

has 检测集合中是否包含某个元素,返回boolean值

//声明一个集合 let s=new Set(['a','b','c','b']) console.log(s)//去重

console.log(s.size)

(十五)Map(类似对象)
ES6提供了Map数据结构,类似于对象,也是键值对的集合,但是“键”的范围不再局限于字符串 ,各种类型的值(包括对象)都可以当作键。Map也实现了Iterator接口,可以使用扩展运算符和for...of...进行遍历。

1、Map的属性和方法:
size 返回Map的元素个数

set 增加一个新元素,返回当前Map

get 返回键名对象的键值

has 检测Map中是否包含某个元素,返回boolean值

clear 清空集合,返回undefined

//声明Map let m=new Map() //添加元素(键,值) m.set('status','好困') m.set('change',function(){ console.log("等会再困") }) m.set('city',['成都','天津','咸阳'])

(十六)class类
ES6提供了更接近传统语言的写法,引入了class这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作是一个语法糖,其绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

1、super()
既可以作为函数使用,又可以作为对象使用。作为函数调用时,代表父类的构造函数;作为对象使用时,在普通方法中,指向父类的原型对象,在静态方法中,指向父类。
2、constructor(),
prototype有一个constructor的属性,默认是指向prototype所在的构造函数。是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
3、getter()和setter()是对某一个对象属性进行方法的绑定。getter通常用于对动态属性的封装,setter用于添加更多的控制和判断。
(十七)数值扩展
1、Number.EPSION表示js的最小精度。
2、二进制0b 八进制0o 十六进制0x
 eg: let a=0b1010,即十进制的10
3、Number.isFinite用于检测一个数值是否为有限数,返回结果为布尔值
4、Number.isNaN检测一个数值是否为NaN
5、Number.parseInt Number.parseFloat将字符串转为整数(会截断)
6、Number.isInteger判断是否为整数
7、Math.trunc将数字的小数部分抹掉
8、Math.sign判断一个数到底为正数 负数 还是零,返回结果1 -1 0
(十八)对象方法扩展
1、Object.is判断两个值是否完全相等,返回结果为boolean,与===有一定区别
2、Object.assign对象的合并,对于重复的参数属性,后者会覆盖前者
3、Object.setPrototypeOf设置原型对象
(十九)模块化
1、初识module
模块(module):一个一个的局部作用域代码块。

模块化:是指将一个大的程序文件拆分成许多小的文件,然后将文件组合起来。

模块化优势:防止命名冲突、代码复用、高校维护

ES6 之前的模块化规范有:

CommonJS => NodeJS、Browserify AMD => requireJS CMD => seaJS

2、Module基本用法
普通的 HTML、JS 是本地文件环境,地址以 file 协议开头,服务器则以 http 或 https 开头。 Module 要生效,必须在服务器环境下才能执行。方法:VSCode 中使用 Live Server 拓展,WebStorm 默认就是服务器环境。 在使用 script 标签加载的时候,需要加上 type="module",否则就以普通 JS 文件的形式引入了,就不是模块了

3、模块化语法
模块功能主要由两个命令构成:import 和 export

import 命令用于导入

export 用于导出,规定模块的对外接口

<script type="module">
//引入模块
//1、通用导入
import * as xxx from "./xxx/xxx.js"
//2、解构赋值
import {person,school} from "./src/xxx.js"
import {person as lily,city} from "./src/xxx1.js"//别名
//3、简便形式(只针对默认暴露default)
import xxx from "./xxx/xxx.js"
xxx.js //1、分别暴露 export xxx //2、统一暴露 export {xxx1,xxx2} //3、默认暴露 export default{ ... }

三、ES7新特性
1、Array.prototype.includes
includes 方法用来检测数组中是否包含某个元素,返回结果为布尔类型。

includes() 方法用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回false。该方法不会改变原数组。其语法如下:

arr.includes(searchElement, fromIndex)
该方法有两个参数:

searchElement:必须,需要查找的元素值。

fromIndex:可选,从fromIndex 索引处开始查找目标值。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。

[1, 2, 3].includes(2);  //  true
[1, 2, 3].includes(4);  //  false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true
在 ES7 之前,通常使用 indexOf 来判断数组中是否包含某个指定值。但 indexOf 在语义上不够明确直观,同时 indexOf 内部使用 === 来判等,所以存在对 NaN 的误判,includes 则修复了这个问题:

[1, 2, NaN].indexOf(NaN);   // -1
[1, 2, NaN].includes(NaN);  //  true
注意:使用includes()比较字符串和字符时区分大小写。

2、指数操作符
在ES7中引入指数运算符 ** ,用来实现幂运算,功能与 Math.pow 相同

Math.pow(2, 10));  // 1024
2**10;           // 1024
四、ES8新特性
(一)async 和 await
async 和 await两种语法结合可以让异步代码像同步代码一样

(异步编程的解决方案:生成器函数、promise、async和await)

①async函数

该函数返回值为promise对象,该promise对象的结果由async函数执行的返回值(return)决定。

async function fn(){

} ②await表达式

await必须写在async函数中。 await右侧的表达式一般为promise对象。 await返回的结果是promise成功的值。 await的promise失败了,就会抛出异常,需要通过try...catch捕获处理。 //创建promise对象 const p=new Promise((resolve,reject)=>{ resolve("成功") //reject("失败“) })

//await函数 async function main(){ //try{ let result=await p console.log(result) //}catch(e){ //console.log(e) } })

//调用函数 main()

(二)对象方法扩展
1、Object.values()方法返回一个给定对象的所有可枚举属性值的数组
2、Object.entries()方法返回一个给定对象自身可遍历属性[key,value]的数组,对象转换为二维数组
3、Object.getOwnPropertyDescriptors()该方法返回指定对象所有自身属性的描述对象
(三)
1. padStart()和padEnd()
padStart()和padEnd()方法用于补齐字符串的长度。如果某个字符串不够指定长度,会在头部或尾部补全。

(1)padStart()
padStart()用于头部补全。该方法有两个参数,其中第一个参数是一个数字,表示字符串补齐之后的长度;第二个参数是用来补全的字符串。

如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串:

'x'.padStart(1, 'ab') // 'x'
如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串:

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
如果省略第二个参数,默认使用空格补全长度:

'x'.padStart(4, 'ab') // 'a   '
padStart()的常见用途是为数值补全指定位数,笔者最近做的一个需求就是将返回的页数补齐为三位,比如第1页就显示为001,就可以使用该方法来操作:

"1".padStart(3, '0')   // 输出结果: '001'
"15".padStart(3, '0')  // 输出结果: '015'
(2)padEnd()
padEnd()用于尾部补全。该方法也是接收两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串:

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
2. Object.values()和Object.entries()
在ES5中就引入了Object.keys方法,在ES8中引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用。它们都用来遍历对象,它会返回一个由给定对象的自身可枚举属性(不含继承的和Symbol属性)组成的数组,数组元素的排列顺序和正常循环遍历该对象时返回的顺序一致,这个三个元素返回的值分别如下:

Object.keys():返回包含对象键名的数组;

Object.values():返回包含对象键值的数组;

Object.entries():返回包含对象键名和键值的数组。

let obj = { id: 1, name: 'hello', age: 18 
};
console.log(Object.keys(obj));   // 输出结果: ['id', 'name', 'age']
console.log(Object.values(obj)); // 输出结果: [1, 'hello', 18]
console.log(Object.entries(obj));   // 输出结果: [['id', 1], ['name', 'hello'], ['age', 18]
注意

Object.keys()方法返回的数组中的值都是字符串,也就是说不是字符串的key值会转化为字符串。

结果数组中的属性值都是对象本身可枚举的属性,不包括继承来的属性。

3. 函数扩展
ES2017 规定函数的参数列表的结尾可以为逗号:

function person( name, age, sex, ) {}
该特性的主要作用是方便使用git进行多人协作开发时修改同一个函数减少不必要的行变更。

4. Object.values
之前可以通过 Object.keys 来获取一个对象所有的 key。在ES8中提供了 Object.values 来获取对象所有的 value 值:

const person = {name: "zhangsan",age: 18,height: 188,
};console.log(Object.values(person)); // ['zhangsan', 18, 188]
五、ES9新特性
// 1、rest参数与spread扩展运算符在ES6中已经引入,不过只针对数组,在ES9中为对象提供了像数组一样的rest参数和扩展运算符。
function fn({host,port,...user}){ .... }

fn({ host:'127.0.0.1', port:3306, username:'root', password:123 })

1.for await of
for of方法能够遍历具有Symbol.iterator接口的同步迭代器数据,但是不能遍历异步迭代器。ES9新增的for await of可以用来遍历具有Symbol.asyncIterator方法的数据结构,也就是异步迭代器,且会等待前一个成员的状态改变后才会遍历到下一个成员,相当于async函数内部的await。现在我们有三个异步任务,想要实现依次输出结果,该如何实现呢?

// for of遍历function Gen (time) {  return new Promise(function (resolve, reject) {    setTimeout(function () {      resolve(time)    }, time)  })}async function test () {  let arr = [Gen(2000), Gen(100), Gen(3000)]  for (let item of arr) {    console.log(Date.now(), item.then(console.log))  }}test()
得到如下结果:

上述代码证实了for of方法不能遍历异步迭代器,得到的结果并不是我们所期待的,于是for await of就粉墨登场啦!

function Gen (time) {  return new Promise(function (resolve, reject) {    setTimeout(function () {      resolve(time)    }, time)  })}async function test () {  let arr = [Gen(2000), Gen(100), Gen(3000)]  for await (let item of arr) {    console.log(Date.now(), item)  }}test()// 1575536194608 2000// 1575536194608 100// 1575536195608 3000
使用for await of遍历时,会等待前一个Promise对象的状态改变后,再遍历到下一个成员。

异步迭代器的支持情况:

2.Object Rest Spread
ES6中添加的最意思的特性之一是spread操作符。你不仅可以用它替换cancat()和slice()方法,使数组的操作(复制、合并)更加简单,还可以在数组必须以拆解的方式作为函数参数的情况下,spread操作符也很实用。

const arr1 = [10, 20, 30];const copy = [...arr1]; // 复制console.log(copy);    // [10, 20, 30]const arr2 = [40, 50];const merge = [...arr1, ...arr2]; // 合并console.log(merge);    // [10, 20, 30, 40, 50]console.log(Math.max(...arr));    // 30 拆解
ES9通过向对象文本添加扩展属性进一步扩展了这种语法。他可以将一个对象的属性拷贝到另一个对象上,参考以下情形:

const input = {  a: 1,  b: 2,  c: 1}const output = {  ...input,  c: 3}console.log(output) // {a: 1, b: 2, c: 3}
上面代码可以把 input 对象的数据都添加到 output 对象中,需要注意的是,如果存在相同的属性名,只有最后一个会生效。

const input = {  a: 1,  b: 2}const output = {  ...input,  c: 3}input.a=\'浪里行舟\'console.log(input,output) // {a: "浪里行舟", b: 2} {a: 1, b: 2, c: 3}
上面例子中,修改input对象中的值,output并没有改变,说明扩展运算符拷贝一个对象(类似这样obj2 = {...obj1}),实现只是一个对象的浅拷贝。值得注意的是,如果属性的值是一个对象的话,该对象的引用会被拷贝:

const obj = {x: {y: 10}};const copy1 = {...obj};    const copy2 = {...obj}; obj.x.y=\'浪里行舟\'console.log(copy1,copy2) // x: {y: "浪里行舟"} x: {y: "浪里行舟"}console.log(copy1.x === copy2.x);    // → true
copy1.x 和 copy2.x 指向同一个对象的引用,所以他们严格相等。

我们再来看下 Object rest 的示例:

const input = {  a: 1,  b: 2,  c: 3}let { a, ...rest } = inputconsole.log(a, rest) // 1 {b: 2, c: 3}
当对象 key-value 不确定的时候,把必选的 key 赋值给变量,用一个变量收敛其他可选的 key 数据,这在之前是做不到的。注意,rest属性必须始终出现在对象的末尾,否则将抛出错误。

Rest与Spread兼容性一致,下列以spread为例:

3.Promise.prototype.finally()
Promise.prototype.finally() 方法返回一个Promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then()和catch()后,都会执行finally指定的回调函数。

fetch(\'https://www.google.com\')  .then((response) => {    console.log(response.status);  })  .catch((error) => {     console.log(error);  })  .finally(() => {     document.querySelector(\'#spinner\').style.display = \'none\';  });
无论操作是否成功,当您需要在操作完成后进行一些清理时,finally()方法就派上用场了。这为指定执行完promise后,无论结果是fulfilled还是rejected都需要执行的代码提供了一种方式,避免同样的语句需要在then()和catch()中各写一次的情况。

Promise.prototype.finally()的支持情况:

4.新的正则表达式特性
ES9为正则表达式添加了四个新特性,进一步提高了JavaScript的字符串处理能力。这些特点如下:

s (dotAll) 标志

命名捕获组

Lookbehind 后行断言

Unicode属性转义

(1)s(dotAll)flag
正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符,如换行符(n)或回车符(r),这个可以通过ES9的s(dotAll)flag,在原正则表达式基础上添加s表示:

console.log(/foo.bar/.test(\'foo\nbar\')) // falseconsole.log(/foo.bar/s.test(\'foo\nbar\')) // true
那如何判断当前正则是否使用了 dotAll 模式呢?

const re = /foo.bar/s // Or, `const re = new RegExp(\'foo.bar\', \'s\');`.console.log(re.test(\'foo\nbar\')) // trueconsole.log(re.dotAll) // trueconsole.log(re.flags) // \'s\'
(2)命名捕获组
在一些正则表达式模式中,使用数字进行匹配可能会令人混淆。例如,使用正则表达式/(d{4})-(d{2})-(d{2})/来匹配日期。因为美式英语中的日期表示法和英式英语中的日期表示法不同,所以很难区分哪一组表示日期,哪一组表示月份:

const re = /(\d{4})-(\d{2})-(\d{2})/;const match= re.exec(\'2019-01-01\');console.log(match[0]);    // → 2019-01-01console.log(match[1]);    // → 2019console.log(match[2]);    // → 01console.log(match[3]);    // → 01
ES9引入了命名捕获组,允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;const match = re.exec(\'2019-01-01\');console.log(match.groups);          // → {year: "2019", month: "01", day: "01"}console.log(match.groups.year);     // → 2019console.log(match.groups.month);    // → 01console.log(match.groups.day);      // → 01
上面代码中,“命名捕获组”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?),然后就可以在exec方法返回结果的groups属性上引用该组名。

命名捕获组也可以使用在replace()方法中,例如将日期转换为美国的 MM-DD-YYYY 格式:

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/const usDate = \'2018-04-30\'.replace(re, \'$<month>-$<day>-$<year>\')console.log(usDate) // 04-30-2018
(3)Lookbehind 后行断言
JavaScript 语言的正则表达式,只支持先行断言,不支持后行断言,先行断言我们可以简单理解为"先遇到一个条件,再判断后面是否满足",如下面例子:

let test = \'hello world\'console.log(test.match(/hello(?=\sworld)/))// ["hello", index: 0, input: "hello world", groups: undefined]
但有时我们想判断前面是 world 的 hello,这个代码是实现不了的。在 ES9 就支持这个后行断言了:

let test = \'world hello\'console.log(test.match(/(?<=world\s)hello/))// ["hello", index: 6, input: "world hello", groups: undefined]
(?<…)是后行断言的符号,(?..)是先行断言的符号,然后结合 =(等于)、!(不等)、1(捕获匹配)。

(4)Unicode属性转义
ES2018 引入了一种新的类的写法p{...}和P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。比如你可以使用p{Number}来匹配所有的Unicode数字,例如,假设你想匹配的Unicode字符㉛字符串:

const str = \'㉛\';console.log(/\d/u.test(str));    // → falseconsole.log(/\p{Number}/u.test(str));     // → true
同样的,你可以使用p{Alphabetic}来匹配所有的Unicode单词字符:

const str = \'ض\';console.log(/\p{Alphabetic}/u.test(str));     // → true// the \w shorthand cannot match ضconsole.log(/\w/u.test(str));    // → false
同样有一个负向的Unicode属性转义模板 P{...}

console.log(/\P{Number}/u.test(\'㉛\'));    // → falseconsole.log(/\P{Number}/u.test(\'ض\'));    // → trueconsole.log(/\P{Alphabetic}/u.test(\'㉛\'));    // → trueconsole.log(/\P{Alphabetic}/u.test(\'ض\'));    // → false
除了字母和数字之外,Unicode属性转义中还可以使用其他一些属性。

以上这几个特性的支持情况:

六、ES10新特性
(一)对象扩展 - Object.fromEntries
Object.fromEntries用于创建对象,参数接收一个二维数组或Map。是ES8中的Object.entries()的逆运算。

//二维数组 const result=Object.fromEntries([ ['name','red'], ['xueke','java,web,SQL'] ])

//MAP const m=new Map() m.set('name','haokun') const result=Object.fromEntries(m)

console.log(result)

Object.fromEntries()方法可以把键值对列表转换为一个对象。该方法相当于 Object.entries() 方法的逆过程。Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,而Object.fromEntries() 方法把键值对列表转换为一个对象。

const object = { key1: 'value1', key2: 'value2' }const array = Object.entries(object)  // [ ["key1", "value1"], ["key2", "value2"] ]Object.fromEntries(array)             // { key1: 'value1', key2: 'value2' }
使用该方法主要有以下两个用途:

(1)将数组转成对象

const entries = [['foo', 'bar'],['baz', 42]
]
Object.fromEntries(entries)  //  { foo: "bar", baz: 42 }
(2)将 Map 转成对象

const entries = new Map([['foo', 'bar'],['baz', 42]
])
Object.fromEntries(entries)  //  { foo: "bar", baz: 42 }
(二)字符串扩展 - trimStart() 和 trimEnd()
trimStart和trimEnd用于指定清除某个字符串左侧或右侧的空白字符。

let str=' xxx '

console.log(str.trimStart());

在ES10之前,JavaScript提供了trim()方法,用于移除字符串首尾空白符。在ES9中提出了trimStart()和trimEnd() 方法用于移除字符串首尾的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等。

(1)trimStart()
trimStart() 方法的的行为与trim()一致,不过会返回一个从原始字符串的开头删除了空白的新字符串,不会修改原始字符串:

const s = '  abc  ';s.trimStart()   // "abc  "
(2)trimStart()
trimEnd() 方法的的行为与trim()一致,不过会返回一个从原始字符串的结尾删除了空白的新字符串,不会修改原始字符串:

const s = '  abc  ';s.trimEnd()   // "  abc"
注意,这两个方法都不适用于null、undefined、Number类型。

(三)数组扩展 - flat() &flatMap()
flat 可以数组降维, num 表示降维级数

const arr=[1,2,3,[4,5]] console.log(arr.flat(num)) //>[1,2,3,4,5] flatMap falt和Map方法的结合,Map返回结果如果是多维数组,可以降维

const arr=[1,2,3,4] const result=arr.flatMap(item=>[item*10])

console.log(result)

(1)flat()
在ES2019中,flat()方法用于创建并返回一个新数组,这个新数组包含与它调用flat()的数组相同的元素,只不过其中任何本身也是数组的元素会被打平填充到返回的数组中:

[1, [2, 3]].flat()        // [1, 2, 3]
[1, [2, [3, 4]]].flat()   // [1, 2, [3, 4]]
在不传参数时,flat()默认只会打平一级嵌套,如果想要打平更多的层级,就需要传给flat()一个数值参数,这个参数表示要打平的层级数:

[1, [2, [3, 4]]].flat(2)  // [1, 2, 3, 4]
如果数组中存在空项,会直接跳过:

[1, [2, , 3]].flat());    //  [1, 2, 3]
如果传入的参数小于等于0,就会返回原数组:

[1, [2, [3, [4, 5]]]].flat(0);    //  [1, [2, [3, [4, 5]]]]
[1, [2, [3, [4, 5]]]].flat(-10);  //  [1, [2, [3, [4, 5]]]]
(2)flatMap()
flatMap()方法使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。该方法会返回一个新的数组,其中每个元素都是回调函数的结果,并且结构深度 depth 值为1。

[1, 2, 3, 4].flatMap(x => x * 2);      //  [2, 4, 6, 8]
[1, 2, 3, 4].flatMap(x => [x * 2]);    //  [2, 4, 6, 8][1, 2, 3, 4].flatMap(x => [[x * 2]]);  //  [[2], [4], [6], [8]]
[1, 2, 3, 4].map(x => [x * 2]);        //  [[2], [4], [6], [8]]
(四)Symbol扩展
JS的7种数据类型:undefined、string、symbol、object、null、number、boolean

Symbol.prototype.description 获取symbol的字符串描述

//创建Symbol let s=Symbol('haokun') console.log(s.description)

(五) 特殊说明 - ES10新特性
1.Array.prototype.flat()
多维数组是一种常见的数据格式,特别是在进行数据检索的时候。将多维数组打平是个常见的需求。通常我们能够实现,但是不够优雅。

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

newArray = arr.flat(depth) // depth是指定要提取嵌套数组的结构深度,默认值为 1
接下来我们看两个例子:

const numbers1 = [1, 2, [3, 4, [5, 6]]]console.log(numbers1.flat())// [1, 2, 3, 4, [5, 6]]const numbers2 = [1, 2, [3, 4, [5, 6]]]console.log(numbers2.flat(2))// [1, 2, 3, 4, 5, 6]
上面两个例子说明flat 的参数没有设置,取默认值 1,也就是说只扁平化第一级;当 flat 的参数大于等于 2,返回值就是 [1, 2, 3, 4, 5, 6] 了。

Array.prototype.flat的支持情况:

2.Array.prototype.flatMap()
有了flat方法,那自然而然就有Array.prototype.flatMap方法,flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。从方法的名字上也可以看出来它包含两部分功能一个是 map,一个是 flat(深度为1)。

let arr = [1, 2, 3]console.log(arr.map(item => [item * 2]).flat()) // [2, 4, 6]console.log(arr.flatMap(item => [item * 2])) // [2, 4, 6]
实际上flatMap是综合了map和flat的操作,所以它也只能打平一层。

Array.prototype.flatmap的支持情况:

3.Object.fromEntries()
Object.fromEntries 这个新的API实现了与 Object.entries 相反的操作。这使得根据对象的 entries 很容易得到 object。

const object = { x: 23, y:24 };const entries = Object.entries(object); // [[\'x\', 23], [\'y\', 24]]const result = Object.fromEntries(entries); // { x: 23, y: 24 }
ES2017引入了Object.entries, 这个方法可以将对象转换为数组,这样对象就可以使用数组原型中的众多内置方法,比如map, filter、reduce,举个例子,我们想提取下列对象obj中所有value大于21的键值对,如何操作呢?

// ES10之前const obj = {  a: 21,  b: 22,  c: 23}console.log(Object.entries(obj)) // [[\'a\',21],["b", 22],["c", 23]]let arr = Object.entries(obj).filter(([a, b]) => b > 21) // [["b", 22],["c", 23]]let obj1 = {}for (let [name, age] of arr) {  obj1[name] = age}console.log(obj1) // {b: 22, c: 23}
上例中得到了数组arr,想再次转化为对象,就需要手动写一些代码来处理,但是有了Object.fromEntries()就很容易实现

// 用Object.fromEntries()来实现const obj = {  a: 21,  b: 22,  c: 23}let res = Object.fromEntries(Object.entries(obj).filter(([a, b]) => b > 21))console.log(111, res) // {b: 22, c: 23}
Object.fromEntries()的支持情况:

4.String.trimStart 和 String.trimEnd
移除开头和结尾的空格,之前我们用正则表达式来实现,现在ES10新增了两个新特性,让这变得更简单!

trimStart() 方法从字符串的开头删除空格,trimLeft()是此方法的别名。

let str = \' 前端工匠 \'console.log(str.length) // 6str = str.trimStart()console.log(str.length) // 5let str1 = str.trim() // 清除前后的空格console.log(str1.length) // 4str.replace(/^\s+/g, \'\') // 也可以用正则实现开头删除空格
trimEnd() 方法从一个字符串的右端移除空白字符,trimRight 是 trimEnd 的别名。

let str = \' 浪里行舟 \'console.log(str.length) // 6str = str.trimEnd()console.log(str.length) // 5let str1 = str.trim() //清除前后的空格console.log(str1.length) // 4str.replace(/\s+$/g, \'\') // 也可以用正则实现右端移除空白字符
String.trimStart和String.trimEnd 两者兼容性一致,下图以trimStart为例:

5.String.prototype.matchAll
如果一个正则表达式在字符串里面有多个匹配,现在一般使用g修饰符或y修饰符,在循环里面逐一取出。

function collectGroup1 (regExp, str) {  const matches = []  while (true) {    const match = regExp.exec(str)    if (match === null) break    matches.push(match[1])  }  return matches}console.log(collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`))// [ \'foo\', \'bar\', \'baz\' ]
值得注意的是,如果没有修饰符 /g, .exec() 只返回第一个匹配。现在通过ES9的String.prototype.matchAll方法,可以一次性取出所有匹配。

function collectGroup1 (regExp, str) {  let results = []  for (const match of str.matchAll(regExp)) {    results.push(match[1])  }  return results}console.log(collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`))// ["foo", "bar", "baz"]
上面代码中,由于string.matchAll(regex)返回的是遍历器,所以可以用for...of循环取出。

String.prototype.matchAll的支持情况:

6.try…catch
在ES10中,try-catch语句中的参数变为了一个可选项。以前我们写catch语句时,必须传递一个异常参数。这就意味着,即便我们在catch里面根本不需要用到这个异常参数也必须将其传递进去

// ES10之前try {  // tryCode} catch (err) {  // catchCode}
这里 err 是必须的参数,在 ES10 可以省略这个参数:

// ES10try {  console.log(\'Foobar\')} catch {  console.error(\'Bar\')}
try…catch的支持情况:

7.BigInt
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。

// 超过 53 个二进制位的数值,无法保持精度Math.pow(2, 53) === Math.pow(2, 53) + 1 // true// 超过 2 的 1024 次方的数值,无法表示Math.pow(2, 1024) // Infinity
现在ES10引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

创建 BigInt 类型的值也非常简单,只需要在数字后面加上 n 即可。例如,123 变为 123n。也可以使用全局方法 BigInt(value) 转化,入参 value 为数字或数字字符串。

const aNumber = 111;const aBigInt = BigInt(aNumber);aBigInt === 111n // truetypeof aBigInt === \'bigint\' // truetypeof 111 // "number"typeof 111n // "bigint"
※: 如果算上 BigInt,JavaScript 中原始类型就从 6 个变为了 7 个。(??? 加上Object 8个?)
Boolean

Null

Undefined

Number

String

Symbol (new in ECMAScript 2015)

BigInt (new in ECMAScript 2019) BigInt的支持情况:

8.Symbol.prototype.description
我们知道,Symbol 的描述只被存储在内部的 [[Description]],没有直接对外暴露,我们只有调用 Symbol 的 toString() 时才可以读取这个属性:

Symbol(\'desc\').description;  // "desc"Symbol(\'\').description;      // ""Symbol().description;        // undefined
Symbol.prototype.description的支持情况:

9.Function.prototype.toString()
ES2019中,Function.toString()发生了变化。之前执行这个方法时,得到的字符串是去空白符号的。而现在,得到的字符串呈现出原本源码的样子:

function sum(a, b) {  return a + b;}console.log(sum.toString());// function sum(a, b) {//  return a + b;// }
Function.prototype.toString()的支持情况:

七、ES11新特性
(一)私有属性
对属性的封装,防止外部直接操作

<script> class Dog{ //公有属性 name; //私有属性 #age; #sex; //构造方法 constructor(name,age,sex){ this.name=name; this.#age=age; this.#sex=sex; }

// intro(){ console.log(this.#age)//可以读取 }

}

//实例化 const little=new Dog('wang',4,'boy')

console.log(little.#age)//无法输出,在类的内部才可以读取 // little.intro() </script>

(二)promise.allSettled()方法
Promise.all() 方法只适合所有异步操作都成功的情况,如果有一个操作失败,就无法满足要求。

Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。

二者都用于批量异步任务。

Promise.allSettled 的参数接受一个 Promise 的数组,返回一个新的 Promise。唯一的不同在于,执行完之后不会失败,也就是说当 Promise.allSettled 全部处理完成后,我们可以拿到每个 Promise 的状态,而不管其是否处理成功。

下面使用 allSettled 实现的一段代码:

const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {console.log(results);
});
// 返回结果:
// [
//    { status: 'fulfilled', value: 2 },
//    { status: 'rejected', reason: -1 }
// ]
可以看到,Promise.allSettled 最后返回的是一个数组,记录传进来的参数中每个 Promise 的返回值,这就是和 all 方法不太一样的地方。你也可以根据 all 方法提供的业务场景的代码进行改造,其实也能知道多个请求发出去之后,Promise 最后返回的是每个参数的最终状态。

(三)str.matchAll()
该方法返回一个包含所有匹配正则表达式的结果及其分组捕获组的迭代器。适于数据的批量提取。

matchAll() 是新增的字符串方法,它返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。因为返回的是遍历器,所以通常使用for...of循环取出。

for (const match of 'abcabc'.matchAll(/a/g)) {console.log(match)
}
//["a", index: 0, input: "abcabc", groups: undefined]
//["a", index: 3, input: "abcabc", groups: undefined]
需要注意,该方法的第一个参数是一个正则表达式对象,如果传的参数不是一个正则表达式对象,则会隐式地使用 new RegExp(obj) 将其转换为一个 RegExp 。另外,RegExp必须是设置了全局模式g的形式,否则会抛出异常 TypeError。

(四)可选链操作符 ?.
?. 允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。

?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空(nullish ) (null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined。

xxx?.zzz,可选链操作符会判断xxx是否存在,存在才会读取zzz。

function main(config){ //过去方式 //const dbHost=config && config.db && config.db.host

//可选链操作符?. const dbHost=config?.db?.host

(五)动态import
可实现按需加载(懒加载)

xxx.js const btn=document.getElementById('btn')

btn.οnclick=function(){ import('./server.js).then(modules=>{ //返回结果是一个promise对象 module.hello() }) }

(六)BigInt(新数据类型),BigInt()函数
用于进行更大的数值运算。表现方式,在普通整数数值后面加一个n。

注意:BigInt类型的数值不能直接与普通数值类型做运算。

//表现形式 let a=65n;

//BigInt函数 let max=Number.MAX_SAFE_INTEGER;//最大安全整数 console.log(BigInt(max)+BigInt(7));

(七)globalThis变量
作用:始终指向全局变量

(八) 空值合并运算符(??)
在编写代码时,如果某个属性不为 null 和 undefined,那么就获取该属性,如果该属性为 null 或 undefined,则取一个默认值:

const name = dogName ? dogName : 'default'; 
可以通过 || 来简化:

const name =  dogName || 'default'; 
但是 || 的写法存在一定的缺陷,当 dogName 为 0 或 false 的时候也会走到 default 的逻辑。所以 ES2020 引入了 ?? 运算符。只有 ?? 左边为 null 或 undefined时才返回右边的值:

const dogName = false; 
const name =  dogName ?? 'default';  // name = false;
八、ES13 新特性(2022)
1. Object.hasOwn()
在ES2022之前,可以使用 Object.prototype.hasOwnProperty() 来检查一个属性是否属于对象。

Object.hasOwn 特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法:

const example = {property: '123'
};console.log(Object.prototype.hasOwnProperty.call(example, 'property'));
console.log(Object.hasOwn(example, 'property'));
2. Array.at()
at() 是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索:

const array = [0,1,2,3,4,5];console.log(array[array.length-1]);  // 5
console.log(array.at(-1));  // 5console.log(array[array.lenght-2]);  // 4
console.log(array.at(-2));  // 4
除了数组,字符串也可以使用at()方法进行索引:

const str = "hello world";console.log(str[str.length - 1]);  // d
console.log(str.at(-1));  // d
3. error.cause
在 ECMAScript 2022 规范中,new Error() 中可以指定导致它的原因:

function readFiles(filePaths) {return filePaths.map((filePath) => {try {// ···} catch (error) {throw new Error(`While processing ${filePath}`,{cause: error});}});
}
4. Top-level Await
在ES2017中,引入了 async 函数和 await 关键字,以简化 Promise 的使用,但是 await 关键字只能在 async 函数内部使用。尝试在异步函数之外使用 await 就会报错:SyntaxError - SyntaxError: await is only valid in async function。

顶层 await 允许我们在 async 函数外面使用 await 关键字。它允许模块充当大型异步函数,通过顶层 await,这些 ECMAScript 模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。

由于 await 仅在 async 函数中可用,因此模块可以通过将代码包装在 async 函数中来在代码中包含 await:

  // a.jsimport fetch  from "node-fetch";let users;export const fetchUsers = async () => {const resp = await fetch('https://jsonplaceholder.typicode.com/users');users =  resp.json();}fetchUsers();export { users };// usingAwait.jsimport {users} from './a.js';console.log('users: ', users);console.log('usingAwait module');
我们还可以立即调用顶层async函数(IIAFE):

import fetch  from "node-fetch";(async () => {const resp = await fetch('https://jsonplaceholder.typicode.com/users');users = resp.json();})();export { users };
这样会有一个缺点,直接导入的 users 是 undefined,需要在异步执行完成之后才能访问它:

// usingAwait.js
import {users} from './a.js';console.log('users:', users); // undefinedsetTimeout(() => {console.log('users:', users);
}, 100);console.log('usingAwait module');
当然,这种方法并不安全,因为如果异步函数执行花费的时间超过100毫秒, 它就不会起作用了,users 仍然是 undefined。

另一个方法是导出一个 promise,让导入模块知道数据已经准备好了:

//a.js
import fetch  from "node-fetch";
export default (async () => {const resp = await fetch('https://jsonplaceholder.typicode.com/users');users = resp.json();
})();
export { users };//usingAwait.js
import promise, {users} from './a.js';
promise.then(() => { console.log('usingAwait module');setTimeout(() => console.log('users:', users), 100); 
});
虽然这种方法似乎是给出了预期的结果,但是有一定的局限性:导入模块必须了解这种模式才能正确使用它。

而顶层await就可以解决这些问题:

  // a.jsconst resp = await fetch('https://jsonplaceholder.typicode.com/users');const users = resp.json();export { users};// usingAwait.jsimport {users} from './a.mjs';console.log(users);console.log('usingAwait module');
类 Class
5. 类的私有字段
通过 # 关键字,可以创建私有字段或者方法,不可以在外部被访问到,内部可以访问

javascript复制代码class Breeze {
  constructor() {
    this.age = 18;
  }
  name = 'zhang';
  #girl = 'bao';
  #eat() {
    console.log('eating');
  }
  miss() {
    console.log(this.#girl);
  }
  meet() {
    this.#eat();
  }
}

const boy = new Breeze();
console.log(boy.#girl); // Uncaught SyntaxError: Private field '#girl' must be declared in an enclosing class
console.log(boy.miss()); // bao
console.log(boy.meet()); // eating
如果给 私有属性或者方法加上 static 关键字

加上 static 只能通过类名来调用,不能被实例对象调用(和之前的概念没有差别)

javascript复制代码class Breeze {
  constructor() {
    this.age = 18;
  }
  static name = 'zhang';
  static #girl = 'bao';
  static #eat() {
    console.log('eating');
    console.log(this.#girl); // this 指向构造函数
    this.#miss(); // 同样带有 static 才可以访问
  }
  static #miss() {
    console.log(this.#girl);
  }
  meet() {
    this.constructor.#eat(); // 没加 static 关键字,需要加上 constructor,这边的 this 指向实例
  }
}

const boy = new Breeze();
console.log(boy.meet());
6. 使用 in 判断是否是对象的私有属性
接着上面的代码

Javascript复制代码class Breeze {
  constructor() {
    this.age = 18;
  }
  name = 'zhang';
  #girl = 'bao';
  #eat() {
    console.log('eating');
  }
  testEat() {
    return #eat in this;
  }
}
const boy = new Breeze();
console.log(boy.testEat()) // true
console.log('name' in boy) // true
7. 类内的静态块(static blocks inside classes)
类内的静态块,用于执行每个类的评估初始化

可以在类中包含静态块,可以做初始化处理,比如在外部如何获取到内部的私有字段

可以声明多个静态块

javascript复制代码let getPrivateField;
class Breeze {
  constructor() {
    this.age = 18;
  }
  name = 'zhang';
  #girl = 'bao';
  static {
    console.log(1);
  }
  static {
    console.log(2);
    getPrivateField = (el) => el.#girl; // 内部可以访问到私有属性
  }
}
const boy = new Breeze();
console.log(getPrivateField(boy)); // bao
8. /d 正则
正则表达式通过/d 标志匹配索引,该标志为匹配的子字符串提供开始和结束索引

用人话讲就是提供了一个字段 indices,值为数组,分别标志了匹配到的字符串的开始和结束位置,下面来看一看

javascript复制代码const str = 'Hello world!';
//查找"Hello"
const patt = /Hello/;
const res = patt.exec(str);
console.log(res); // ['Hello', index: 0, input: 'Hello world!', groups: undefined]

// 加上 d
const patt = /Hello/d;
const res = patt.exec(str);
console.log(res); // ['Hello', index: 0, input: 'Hello world!', groups: undefined, indices: [[0, 5], groups: undefined]]
console.log(res.indices); // [[0, 5], groups: undefined]
※: 顶级 await 在以下场景中将非常有用:
动态加载模块:

 const strings = await import(`/i18n/${navigator.language}`);
资源初始化:

const connection = await dbConnector();
依赖回退:

let translations;
try {translations = await import('https://app.fr.json');
} catch {translations = await import('https://fallback.en.json');
}
九、ES14 (2023将会完成的提案)
1. 数组被修改时返回副本
Array 和 TypedArray 有很多方法(比如 sort/splice 等)会改变数组自身,比如:

TypeScript复制代码const array = [3, 2, 1];
const sortedArray = array.sort();
// [1, 2, 3]
console.log(sortedArray);
// 原数组也变成了 [1, 2, 3]
console.log(array);
如果不希望改变数组自身,可以这样做:

TypeScript复制代码const array = [3, 2, 1];
const sortedArray = array.toSorted();
// [1, 2, 3]
console.log(sortedArray);
// 原数组不变 [3, 2, 1]
console.log(array);
类似的方法还有这些:

TypeScript复制代码T.prototype.toReversed() -> T
T.prototype.toSorted(compareFn) -> t
T.prototype.toSpliced(start, deleteCount, ...items) -> T
T.prototype.with(index, value) -> T
reverse/sort/splice 还能看懂,with 是干什么的?看个例子就明白了:

TypeScript复制代码const array = [1, 2, 3];
const newArray = array.with(1, false);

// [1, false, 3]
console.log(newArray);
// 原数组不变 [1, 2, 3]
console.log(array);
2. WeakMap 支持 Symbol 作为 key
WeakMap 原本只支持 object 类型的 key,现在支持了 Symbol 类型作为 key。

TypeScript复制代码const weak = new WeakMap();
weak.set(Symbol('symbol1'), {});
3. Hashbang 语法
Hashbang 也叫 Shebang,是一个由井号和叹号构成的字符序列 #!,用来指定使用哪种解释器执行此文件:

TypeScript复制代码
// hashbang.js

#!/usr/bin/env node
console.log('hashbang');


// nohashbang.js

console.log('no hashbang')
在终端执行,没有 Hashbang 时,需要使用 node 指令才能执行:

4. 从尾部查找
涉及到两个函数 findLast / findLastIndex

TypeScript复制代码const array = [1, 2, 3]
array.findLast(n => n.value % 2 === 1); 
array.findLastIndex(n => n.value % 2 === 1); 

————————————————
版权声明:本文为CSDN博主「芳华324」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_72626212/article/details/132564324

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值