ES6~ES13新特性

ES6(2015)

let 和 const

ES6常用语法
1
2
3
let name = '小豪'// 这是值引用类型
const arr = []; // 这是对象引用类型
arr[0] = '1'

模板字符串

1
2
const name = "小豪";
const str = `Your name is ${name}`;

解构赋值

1
2
3
let a = 1,
b = 2;
[a, b] = [b, a]; // a 2 b 1

类(class)

Class 和普通构造函数有何区别?
1
2
3
4
5
6
7
8
9
10
class Man {
constructor(name) {
this.name = "小豪";
}
console() {
console.log(this.name);
}
}
const man = new Man("小豪");
man.console(); // 小豪

模块化(ES Module)

1
2
3
4
5
// 模块 A 导出一个方法
export const sub = (a, b) => a + b;
// 模块 B 导入使用
import { sub } from "./A";
console.log(sub(1, 2)); // 3

Promise

Promise 对象
1
2
3
4
5
Promise.resolve().then(() => {
console.log(2);
});
console.log(1);
// 先打印 1 ,再打印 2

Rest/Spread 属性

扩展运算符/延展操作符

在 ES6 中只针对于数组

1
let a = [..."hello world"]; // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
1
2
3
4
5
const arr1 = [1, 2, 4];
const arr2 = [4, 5, 7];
const arr3 = [7, 8, 9];

const arr = [...arr1, ...arr2, ...arr3][(1, 2, 4, 4, 5, 7, 7, 8, 9)];

函数

箭头(Arrow)函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fn = () => {};

// 如果只有一个参数,可以省略括号
const fn = (name) => {};

// 如果函数体里只有一句return
const fn = (name) => {
return 2 * name;
};
// 可简写为
const fn = (name) => 2 * name;

// 如果返回的是对象
const fn = (name) => ({ name: name });

函数参数默认值

函数参数默认值只有在传入参数为 undefined 的时候有效

1
2
3
function foo(age = 25) {
// ...
}

剩余参数

大家可能遇到过这种问题,一个函数,传入参数的个数是不确定的,这就可以用 ES6 的剩余参数

1
2
3
4
5
6
function fn(name, ...params) {
console.log(name);
console.log(params);
}
fn("zs", 1, 2); // zs [ 1, 2 ]
fn("zs", 1, 2, 3, 4, 5); // zs [ 1, 2, 3, 4, 5 ]

Array 的扩展

Array 对象
  1. Array.prototype.forEach
  2. Array.prototype.map
  3. Array.prototype.filter
  4. Array.prototype.some
  5. Array.prototype.every
  6. Array.prototype.reduce
  7. find 和 findIndex

对象

属性名表达式

ES6 允许字面量定义对象时,用(表达式)作为对象的属性名,即把表达式放在方括号内。

1
2
3
4
5
6
let propKey = "foo";

let obj = {
[propKey]: true,
["a" + "bc"]: 123
};

表达式还可以用于定义方法名

1
2
3
4
5
6
7
let obj = {
["h" + "ello"]() {
return "hi";
}
};

obj.hello(); // hi

注意 属性名表达式与简洁表示法,不能同时使用,会报错

1
2
3
4
5
6
7
8
// 报错
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// 正确
const foo = 'bar';
const baz = { [foo]: 'abc'};

注意 属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]

1
2
3
4
5
6
7
8
9
const keyA = { a: 1 };
const keyB = { b: 2 };

const myObject = {
[keyA]: "valueA",
[keyB]: "valueB"
};

myObject; // Object {[object Object]: "valueB"}

[keyA]和[keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而 myObject 最后只有一个[object Object]属性

Object.keys

1
2
3
4
5
6
7
8
const obj = {
name: "zs",
age: 22,
gender: "男"
};

const keys = Object.keys(obj);
console.log(keys); // [ 'name', 'age', 'gender' ]

对象属性同名简写

1
2
3
4
5
6
7
8
9
10
const name = "zs";
const age = "22";

// 属性同名可简写
const obj = {
name,
age
};

console.log(obj); // { name: '林三心', age: '22' }

for of 和 for in

  • for in :遍历方法,可遍历对象和数组
  • for of :遍历方法,只能遍历数组,不能遍历非 iterable 对象

for of

1
2
3
4
for (let item of arr) {
console.log(item);
}
// 1 2 3 4 5

for in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = { name: "林三心", age: 22, gender: "男" };
const arr = [1, 2, 3, 4, 5];

for (let key in obj) {
console.log(key);
}
name;
age;
gender;

for (let index in arr) {
console.log(index);
}
// 0 1 2 3 4

Set 和 Map

Set 和 Map 数据结构

Set

  • 基本用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 可不传数组
const set1 = new Set();
set1.add(1);
set1.add(2);
console.log(set1); // Set(2) { 1, 2 }

// 也可传数组
const set2 = new Set([1, 2, 3]);
// 增加元素 使用 add
set2.add(4);
set2.add("林三心");
console.log(set2); // Set(5) { 1, 2, 3, 4, '林三心' }
// 是否含有某个元素 使用 has
console.log(set2.has(2)); // true
// 查看长度 使用 size
console.log(set2.size); // 5
// 删除元素 使用 delete
set2.delete(2);
console.log(set2); // Set(4) { 1, 3, 4, '林三心' }
  • 不重复性
1
2
3
4
5
6
7
8
// 增加一个已有元素,则增加无效,会被自动去重
const set1 = new Set([1]);
set1.add(1);
console.log(set1); // Set(1) { 1 }

// 传入的数组中有重复项,会自动去重
const set2 = new Set([1, 2, "林三心", 3, 3, "林三心"]);
console.log(set2); // Set(4) { 1, 2, '林三心', 3 }
  • Set 的不重复性中,要注意引用数据类型和 NaN
1
2
3
4
5
6
7
8
9
10
11
12
// 两个对象都是不同的指针,所以没法去重
const set1 = new Set([1, { name: "林三心" }, 2, { name: "林三心" }]);
console.log(set1); // Set(4) { 1, { name: '林三心' }, 2, { name: '林三心' } }

// 如果是两个对象是同一指针,则能去重
const obj = { name: "林三心" };
const set2 = new Set([1, obj, 2, obj]);
console.log(set2); // Set(3) { 1, { name: '林三心' }, 2 }

// NaN !== NaN,NaN是自身不等于自身的,但是在Set中他还是会被去重
const set = new Set([1, NaN, 1, NaN]);
console.log(set); // Set(2) { 1, NaN }
  • 利用 Set 的不重复性,可以实现数组去重
1
2
3
4
5
const arr = [1, 2, 3, 4, 4, 5, 5, 66, 9, 1];

// Set可利用扩展运算符转为数组哦
const quchongArr = [...new Set(arr)];
console.log(quchongArr); // [1, 2, 3, 4, 5, 66, 9]

Map

Map 对比 object 最大的好处就是,key 不受类型限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定义map
const map1 = new Map();
// 新增键值对 使用 set(key, value)
map1.set(true, 1);
map1.set(1, 2);
map1.set("哈哈", "嘻嘻嘻");
console.log(map1); // Map(3) { true => 1, 1 => 2, '哈哈' => '嘻嘻嘻' }
// 判断map是否含有某个key 使用 has(key)
console.log(map1.has("哈哈")); // true
// 获取map中某个key对应的value 使用 get(key)
console.log(map1.get(true)); // 2
// 删除map中某个键值对 使用 delete(key)
map1.delete("哈哈");
console.log(map1); // Map(2) { true => 1, 1 => 2 }

// 定义map,也可传入键值对数组集合
const map2 = new Map([
[true, 1],
[1, 2],
["哈哈", "嘻嘻嘻"]
]);
console.log(map2); // Map(3) { true => 1, 1 => 2, '哈哈' => '嘻嘻嘻' }

ES7(2016)

Array.prototype.includes()

传入元素,如果数组中能找到此元素,则返回 true,否则返回 false

1
[1].includes(1); // true

指数操作符/求幂运算符

1
2
3
4
5
const num = Math.pow(2, 10); // 1024

// ==>

2 ** 10; // 1024

ES8(2017)

async/await

以同步方式执行异步操作

1
2
3
4
async function getData() {
const res = await api.getTableData(); // await 异步任务
// do something
}

Object.values()

用来获取对象的 value 的集合

1
Object.values({ a: 1, b: 2, c: 3 }); // [1, 2, 3]

Object.entries()

用来获取对象的键值对集合

1
Object.entries({ a: 1, b: 2, c: 3 }); // [["a", 1], ["b", 2], ["c", 3]]

String padding

1
2
3
4
// padStart
"hello".padStart(10); // " hello"
// padEnd
"hello".padEnd(10); // "hello "

函数参数列表结尾允许逗号

Object.getOwnPropertyDescriptors()

获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

SharedArrayBuffer 对象

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区。

1
2
3
4
5
/**
* @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。
* @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
*/
new SharedArrayBuffer(10);

Atomics 对象

Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。

ES9(2018)

异步迭代 for await of

await 可以和 for…of 循环一起使用,以串行的方式运行异步操作

1
2
3
4
5
async function process(array) {
for await (let i of array) {
// doSomething(i);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function fn(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time}毫秒后我成功啦!!!`);
}, time);
});
}

async function asyncFn() {
const arr = [fn(3000), fn(1000), fn(1000), fn(2000), fn(500)];
for await (let x of arr) {
console.log(x);
}
}

asyncFn();
// 3000毫秒后我成功啦!!!
// 1000毫秒后我成功啦!!!
// 1000毫秒后我成功啦!!!
// 2000毫秒后我成功啦!!!
// 500毫秒后我成功啦!!!

Promise.finally()

1
2
3
4
Promise.resolve()
.then()
.catch((e) => e)
.finally();

Rest/Spread 属性

rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中,为对象提供了像数组一样的 rest 参数和扩展运算符

1
2
const values = [1, 2, 3, 5, 6];
console.log(Math.max(...values)); // 6

正则表达式命名捕获组

1
2
const reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
const match = reg.exec("2021-02-23");

正则表达式反向断言

1
2
/(?=p)/,/(?<=p)/  // p 前面(位置)、p 后面(位置)
/(?!p)/, /(?<!p>)/ // 除了 p 前面(位置)、除了 p 后面(位置)

正则表达式 dotAll 模式

正则表达式中点.匹配除回车外的任何单字符,标记 s 改变这种行为,允许行终止符的出现

1
/hello.world/.test("hello\nworld"); // false

ES10(2019)

BigInt

BigInt 是 ES10 新加的一种 JavaScript 数据类型,用来表示表示大于 2^53 - 1 的整数,2^53 - 1 是 ES10 之前,JavaScript 所能表示最大的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
const theBiggestInt = 9007199254740991n;

const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n

const hugeString = BigInt("9007199254740991");
// 9007199254740991n

const hugeHex = BigInt("0x1fffffffffffff");
// 9007199254740991n

const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
// 9007199254740991n
  • typeof
1
2
const bigNum = BigInt(1728371927189372189739217);
console.log(typeof bigNum); // bigint

Array.flat()和 Array.flatMap()

1
2
3
4
// flat()
[(1, 2, [3, 4])].flat(Infinity); // [1, 2, 3, 4]
// flatMap()
[(1, 2, 3, 4)].flatMap((a) => [a ** 2]); // [1, 4, 9, 16]

String.trimStart()和 String.trimEnd()

去除字符串首尾空白字符

String.prototype.matchAll

matchAll()为所有匹配的匹配对象返回一个迭代器

1
2
const raw_arr = "test1  test2  test3".matchAll(/t(e)(st(\d?))/g);
const arr = [...raw_arr];

Symbol.prototype.description

只读属性,回 Symbol 对象的可选描述的字符串。

1
Symbol("description").description; // 'description'

Object.fromEntries()

返回一个给定对象自身可枚举属性的键值对数组

1
2
3
4
5
6
// 通过 Object.fromEntries, 可以将 Map 转化为 Object:
const map = new Map([
["foo", "bar"],
["baz", 42]
]);
console.log(Object.fromEntries(map)); // { foo: "bar", baz: 42 }

可选 Catch

ES11(2020)

Nullish coalescing Operator(空值处理)

表达式在 ?? 的左侧 运算符求值为 undefined 或 null,返回其右侧。

1
2
3
4
5
6
7
8
9
10
11
let user = {
u1: 0,
u2: false,
u3: null,
u4: undefined,
u5: ""
};
let u2 = user.u2 ?? "用户2"; // false
let u3 = user.u3 ?? "用户3"; // 用户3
let u4 = user.u4 ?? "用户4"; // 用户4
let u5 = user.u5 ?? "用户5"; // ''

??||最大的区别是,在??这,只有 undefined 和 null 才算假值

1
2
3
4
5
const a = 0 ?? "zs"; // 0
const b = "" ?? "zs"; // ''
const c = false ?? "zs"; // false
const d = undefined ?? "zs"; // zs
const e = null ?? "zs"; // zs

Optional chaining(可选链)

?.用户检测不确定的中间节点

1
2
3
let user = {};
let u1 = user.childer.name; // TypeError: Cannot read property 'name' of undefined
let u1 = user.childer?.name; // undefined

Promise.allSettled

返回一个在所有给定的 promise 已被决议或被拒绝后决议的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果

1
2
3
4
5
6
7
8
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => reject("我是失败的Promise_1"));
const promise4 = new Promise((resolve, reject) => reject("我是失败的Promise_2"));
const promiseList = [promise1, promise2, promise3, promise4];
Promise.allSettled(promiseList).then((values) => {
console.log(values);
});

import()

import语句、import()、require() 用法和区别

按需导入

  • 动态 import()提供基于 Promise 的 API
  • import()遵循 ES 模块规则:单例、匹配符、CORS 等
  • import()可以在传统的脚本中使用也可以在模块中使用
  • 在代码中使用的 import()的顺序与它们被解析的顺序没有任何关联

语法

1
import("./specifier.js"); // returns a Promise

这是一个从静态到动态导入转换的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 静态
import "./a.js";

import b from "./b.js";
b();

import { c } from "./c.js";
c();

// 动态
import("./a.js").then(() => {
console.log("a.js is loaded dynamically");
});

import("./b.js").then((module) => {
const b = module.default;
b("isDynamic");
});

import("./c.js").then(({ c }) => {
c("isDynamic");
});

新基本数据类型 BigInt

任意精度的整数

globalThis

浏览器:window
worker:self
node:global

ES12(2021)

replaceAll

返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉

1
2
const str = "hello world";
str.replaceAll("l", ""); // "heo word"

Promise.any

E12 新增的 Promise 的方法

  • 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
  • 如果有一个 Promise 成功,则返回这个成功结果
  • 如果所有 Promise 都失败,则报错
1
2
3
4
5
6
7
8
9
10
const promise1 = new Promise((resolve, reject) => reject("我是失败的Promise_1"));
const promise2 = new Promise((resolve, reject) => reject("我是失败的Promise_2"));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then((values) => {
console.log(values);
})
.catch((e) => {
console.log(e);
});

WeakRefs

使用 WeakRefs 的 Class 类创建对对象的弱引用(对对象的弱引用是指当该对象应该被 GC 回收时不会阻止 GC 的回收行为)

逻辑运算符和赋值表达式

逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而 JavaScript 已存在的 复合赋值运算符有:

  • 或等于(||=)   a ||= b 等同于 a || (a = b);
  • 且等于(&&=)   a &&= b 等同于 a && (a = b);
1
2
3
4
5
6
7
8
9
10
11
a ||= b;
//等价于
a = a || (a = b);

a &&= b;
//等价于
a = a && (a = b);

a ??= b;
//等价于
a = a ?? (a = b);

数字分隔符

数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性

1
2
3
4
5
const money = 1_000_000_000;
//等价于
const money = 1000000000;

1_000_000_000 === 1000000000; // true

ES13(2022)

class 新特性

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Person {
// 初始值可以直接定义
state = {
name: "haha",
age: 18
};
sex = "男";

// 实例私有属性
#play = "打游戏";
playGame() {
return this.#play;
}

// 静态私有属性配合静态公开方法
static #watch = "看电视";
static watchTv() {
return this.#watch;
}

// 静态代码块(可以写多个)
static obj = new Map();
static {
this.obj.set("name", "无量天尊");
this.obj.set("age", 8000);
}
static {
console.log(this.obj); // Map(2) {'name' => '无量天尊', 'age' => 8000}
}

// 判断某个属性是不是该类的私有属性
hasObj() {
return #play in this;
}
}
let person = new Person();
person.playGame(); // 打游戏
Person.watchTv(); // 看电视
person.hasObj(); // true

公共实例字段

在 ES13 之前,在定义类的属性时,需要在构造函数中定义了实例字段和绑定方法

1
2
3
4
5
6
7
8
9
class myClass {
constructor() {
this.count = 1;
this.increment = this.increment.bind(this);
}
increment() {
this.count += 1;
}
}

ES 13 可以使用公共实例字段,这样就简化了类的定义,使代码更加简洁、可读

1
2
3
4
5
6
class myClass {
count = 1;
increment = () => {
this.count += 1;
};
}

私有实例字段

默认情况下,class 中所有属性都是公共的,可以在 class 之外进行修改,例如

1
2
3
4
5
6
7
8
9
class myClass {
count = 1;
setCount = () => {
this.count += 1;
};
}
const es13 = new myClass();
es13.count = 5;
// myClass {count: 5, setCount: ƒ} count: 5setCount: () => { this.count += 1 }[[Prototype]]: Object

通过上面的例子可以看到,当我们直接设置 count 属性的时候,是直接跳过 setCount 进行设置的,有时候我们并不想这样,所以可以使用私有实例字段,用法很简单,只需要在私有字段添加 # 就可以实现,当然了,在调用的时候我们也应该加上 # 进行调用,如下:

1
2
3
4
5
6
7
8
9
10
11
class myClass {
#count = 1;
setCount = () => {
this.#count += 1;
};
}
const es13 = new myClass();
es13.setCount(); // 正常修改,每执行执行一次 setCount 方法后 #count的值每一次都加1
// 直接修改私有属性
es13.#count = 5;
// 报错:Uncaught SyntaxError: Private field '#count' must be declared in an enclosing class

可以看到,当我们直接修改私有属性之后,浏览器直接抛出错误:Uncaught SyntaxError: Private field '#count' must be declared in an enclosing class

私有方法

都有私有属性了,怎么能少了私有方法呢,方法和属性一下只有加上 # 即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class myClass {
#count = 1;
#setCount = () => {
this.#count += 1;
};
newSetCount = () => {
this.#setCount();
};
}
const es13 = new myClass();
es13.#setCount();
// 直接调用私有方法报错:Uncaught SyntaxError: Private field '#setCount' must be declared in an enclosing class
//通过公共方法 newSetCount 调用
es13.newSetCount();
//成功,#count + 1

私有 in 操作符

我们知道 in 操作符可以帮我们判断实例中是否存在属性,当新增了私有化类元素后,我们也可以和下面例子一样,在类定义内使用 in 操作符判断私有化类元素存在与否。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ClassA {
// 私有属性
#privateProperty;

// 利用 in 操作符判断私有属性是否存在
static hasPrivateProperty(instance) {
return #privateProperty in instance;
}

constructor(privateProperty) {
this.#privateProperty = privateProperty;
}
}

const instance = new ClassA("private-property");
ClassA.hasPrivateProperty(instance);

// 输出 true

静态公共字段、静态私有字段、静态私有方法

与私有实例字段和方法一样,静态私有字段和方法也使用哈希#前缀来定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class myClass {
//静态公共字段
static color = "blue";
// 静态私有字段
static #count = 1;
// 静态私有方法
static #setCount = () => {
this.#count += 1;
};
newSetCount = () => {
this.#setCount();
};
}
const es13 = new myClass();
// 实例 es13 上面只有 newSetCount() 方法
es13.newSetCount();
// 报错:Uncaught SyntaxError: Private field '#setCount' must be declared in an enclosing class

私有静态字段有一个限制:只有定义私有静态字段的类才能访问该字段。这可能在使用 this 时导致出乎意料的情况, 所有我们需要改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class myClass {
// 静态私有字段
static #count = 1;
// 静态私有方法
static #setCount = () => {
// 实例化之后,this 不再指向 myClass,所有需要改成 myClass 类调用
myClass.#count += 1;
};
newSetCount = () => {
// 实例化之后,this 不再指向 myClass,所有需要改成 myClass 类调用
myClass.#setCount();
};
}
const es13 = new myClass();
es13.newSetCount();
// 成功

类静态块

在以前,如果我们希望在初始化期间像 try…catch 一样进行异常处理,就不得不在类之外编写此逻辑。该规范就提供了一种在类声明/定义期间评估静态初始化代码块的优雅方法,可以访问类的私有字段。
ES13 之前

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
static EEEOR = "error"
static SUCCESS_TYPE = "success_type";
constructor() {
// ...
}
try {
// ...
} catch {
// ...
}
}

上面代码直接报错:Uncaught SyntaxError: Unexpected token '{'
ES13 : 直接将 try...catch 使用 static 包裹起来即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
static EEEOR = "error";
static SUCCESS_TYPE = "success_type";
constructor() {
// ...
}
static {
try {
// ...
} catch {
// ...
}
}
}

顶级模块中的 await

以往我们总是在异步函数体内使用 await 表达式,自 ES13 后,我们也可以在一个模块的顶层使用它了。

在模块顶层使用 await 表达式

1
2
3
4
5
6
// 异步加载资源
import { fetchPerson } from "@/services/person";
const person = await fetchPerson();

// 异步加载组件
await import("your-component-path");

在异步函数中使用 await 表达式

1
2
3
4
5
6
import { get } from "@/utils/request";

export async function getPerson() {
const person = await get("/person");
return person;
}

错误用法

在以下几种语句内的 Formal Parameters 中使用 Await 表达式,会引发静态句法的语义错误。

  1. 异步函数声明 (AsyncFunctionDeclaration)
  2. 异步函数表达式 (AsyncFunctionExpression)
  3. 异步生成器函数表达式 (AsyncGeneratorExpression)
  4. Await 表达式内部 (AwaitExpression)

特殊情况

在早期,await 没有出现在关键字中,为了兼容旧代码,在一些特殊的 await 作为参数出现的情况下,它会被解释为标识符。如下所述:

  1. 当 await 出现在 AsyncFunctionBodyAsyncFunctionDeclaration、AsyncFunctionExpression、AsyncGeneratorDeclaration、AsyncGeneratorExpression 的 FormalParameter 外部时,await 会被解析为标识符。
  2. 当 await 出现在 FunctionExpression, GeneratorExpression, AsyncGeneratorExpressionBindingIdentifier 中时,await 会被解析为标识符。

error.cause

在以往 Error 对象只能传递消息信息,现在 ES13 为 Error 增添了 cause 属性,它可以是任意数据类型,方便我们获取更多地错误信息。

1
2
3
4
5
6
7
8
9
10
try {
// 抛出带 cause 属性的 Error实例
throw new Error("faied message", { cause: "cause" });
} catch (error) {
// 捕获 error 并输出 cause
console.log(error.cause);
}

// 输出
// cause

String/Array/TypedArrays 增加 .at()

我们利用下标访问数组元素时,下标会被转换为字符串,负数下标也对应着一个独立的数组元素,所以 Js 的数组是不支持关联访问的。ES13 新增了 at 方法,使得我们可以利用负数索引进行关联访问

例如以下示例,我们可以利用 at 方法和负数索引 -2 来访问倒数第二个元素。

1
2
3
const array = [5, 12, 8, 130, 44];

array.at(-2); // 130

正则表达式匹配索引

我们常用正则表达式来处理字符串,正则表达式有很多标志,它决定了正则状态机的行为和输出结果。

ES13 新增的 d 标志对应正则实例的 hasIndices 属性,当设置 d 标志时,hasIndices 为 true 。

使用 d 标志后,正则表达式的匹配结果将包含每个捕获组捕获子字符串的开始和结束游标。

1
2
3
4
5
6
7
8
9
10
// 字符串
const str = "today is saturday";

// 正则表达式
const reg = /saturday/d;

// 匹配结果输出游标
const [start, end] = reg.exec(str).indices[0];

console.log([start, end]); // [9, 17]

Object.hasOwn(obj, propKey)

ES13 新增了 Object.hasOwn 方法判断这个实例上是否有属性,以代替之前的 Object.prototype.hasOwnProperty 方法。

一种安全的方式来检查对象是否具有自己的非继承的属性

1
2
3
4
5
6
7
const object = {
count: 1
};

Object.hasOwn(object, "count"); // true
Object.hasOwn(object, "toString"); // false
Object.hasOwn(object, "property"); // false