zl程序教程

您现在的位置是:首页 >  其他

当前栏目

一文吃透es6 ~ es13所有新特性

2023-02-25 18:27:45 时间

ECMAScript简介

ECMAScript是一个脚本语言规范,通常看作是js的标准规范,但是js其实是ES的扩展语言。

在ES钟,只是提供了最基本的语法,停留在语言层面。而js使用了ES的标准,并且在其基础之上实现了其他的功能。

在浏览器中,js = ES + webApis(BOM,DOM)

在node中,js = ES + nodeApis(fs,net,etc…)

ES6(es2015)

es6规范是老生常谈的知识点,所以这里只简单盘点一下。

  1. let和const声明变量
  2. 箭头函数
  3. 解构赋值
  4. 参数默认值
  5. 模板字符串
  6. 数组方法for of
  7. Module模块化
  8. 数组的(展开/剩余)运算符
  9. class类
  10. Promise
  11. Map和Set对象
  12. symbol (第6个基本类型)
  13. 生成器和迭代器

具体内容可以查看我写过的一篇 ES6新特性梳理汇总

ES7(es2016)

Array.prototype.includes()

检查一个基本类型的值,是否在数组里,返回true或者false.

对NaN的检测有不同之处,在js中 NaN === NaN 的结果是false。indexOf()也是这样处理的,但是includes()不是这样的。

1let demo = [1, NaN, 2, 3]
2
3demo.indexOf(NaN)        //-1
4demo.includes(NaN)       //true

点评:与indexOf比较类似,假如你只想知道某个值是否在数组中,并不关心它的索引位置,includes比indexOf更好用 实用程度:★★★★☆

幂运算符

基本用法:

13 ** 2  //9
2效果同
3Math.pow(3, 2) //9

由于是运算符,所以可以和 +=一样的用法

1var b = 3;
2b **= 2;
3console.log(b); //9

点评:幂运算写起来更简单 实用程度:★★★★☆

ES8(es2017)

async await

async await被称作异步的终极解决方案

await必须在async函数内使用,用来暂停代码来等待一个promise实例的成功,并将结果作为表达式值

可以通过try catch语法捕获错误

1const makeRequest = async () => {
2  try {
3    // this parse may fail
4    const data = JSON.parse(await getJSON());
5    console.log(data)
6  }
7  catch (err) {
8    console.log(err)
9  }
10}

点评:非常赞的一个功能,完全避免了回调地狱层层嵌套的可能 实用程度:★★★★★

Object.values()

返回一个包含该对象所有的可枚举值的数组。(不包括原型链)

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

点评:某些场景可能比较适用 实用程度:★★★☆☆

Object.entries()

将一个对象中可枚举属性的键名和键值按照二维数组的方式返回。若对象是数组,则会将数组的下标作为键值返回。(不包括原型链)

1Object.entries({ one: 1, two: 2 })    //[["one", 1], ["two", 2]]
2Object.entries([1, 2])                //[["0", 1], ["1", 2]]

点评:配合数组解构,遍历对象更方便 实用程度:★★★★☆

ES9(es2018)

对象的(展开/剩余)运算符

展开对象

1const obj1 = { a: 1, b: 2, c: 3 };
2const obj2 = { ...obj1, z: 26 };
3
4console.log(obj2) // { a: 1, b: 2, c: 3, z: 26 }

收集剩余参数

1var obj = {
2            a: 1,
3            b: 2,
4            c: 3
5        }
6const { a, ...param } = obj;
7console.log(a)     //1
8console.log(param) //{b: 2, c: 3}
9

点评:前端最常用之一没啥好说的,只是有的同学可能以为它是es6的东西 实用程度:★★★★★

Promise.finally()

一个Promise链要么成功进入最后一个then()要么失败触发catch()。而实际中,我们可能需要无论Promise无论成功还是失败,都运行相同的代码。

1new Promise((resolve) => {
2  setTimeout(() => {
3    resolve("success");
4  }, 3000);
5})
6  .then((res) => {
7    console.log(res);
8  })
9  .catch((err) => {
10    console.log(err);
11  })
12  .finally(() => {
13    console.log("finally");
14  });

点评:某些场景会有关键用途,例如清除,删除回话,关闭数据库连接等操作。 实用程度:★★★★☆

异步迭代for await

ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。

在async/await的某些时刻,你可能尝试在同步循环中调用异步函数。例如:

1async function process(array) {
2  for (let i of array) {
3    await doSomething(i);
4  }
5}

这段代码不会正常运行,循环本身依旧保持同步,并在在内部异步函数之前全部调用完成。

所以用for await改写

1async function process(array) {
2  for await (let i of array) {
3    doSomething(i);
4  }
5}

点评:某些场景会有关键用途 实用程度:★★★★☆

ES10(es2019)

Array.prototype.flat()

es10为数组扁平化提供了两个api,通过参数控制扁平深度(默认深度为 1),话不多说直接上代码

1const arr1 = [1, 2, [3, 4]]
2arr1.flat() // [1, 2, 3, 4]
3
4const arr2 = [1, 2, [3, 4, [5, 6]]]
5arr2.flat() // [1, 2, 3, 4, [5, 6]]
6
7const arr3 = [1, 2, [3, 4, [5, 6]]]
8arr3.flat(2) // [1, 2, 3, 4, 5, 6]
9
10const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]
11arr4.flat(Infinity) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

点评:有了它,就不用再自己封装扁平化方法了,减负功能,必须赞 实用程度:★★★★★

Array.prototype.flatMap()

扁平顺便map一下

1const arr = [1, 2, 3, 4]
2
3arr.flatMap(x => [x * 2]) // [2, 4, 6, 8]
4
5// 只有一层是扁平的
6arr.flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]
7

点评:扁平化的扩展功能,不错 实用程度:★★★★☆

Object.fromEntries()

它接收一个键值对列表(Map实例),并返回一个真实对象,其属性由条目给出。

它的功能与 Object.entries() 正相反。

1const entries = new Map([
2  ["apple", "origin"],
3  ["grapes", "peach"]
4])
5
6console.log(Object.fromEntries(entries)) // { apple: "origin", grapes: "peach" }

使用场景1:交换属性和值

1function foo(obj) {
2  return Object.fromEntries(Object.entries(obj)
3    .map(([key, value]) => [value, key])
4  )
5}
6
7console.table({ name: "oli", age: "12" })
8console.table(foo({ name: "oli", age: "12" }))

使用场景2:将 url上的参数提取为一个对象

1const params = Object.fromEntries(new URLSearchParams("foo=bar&baz=qux"));
2
3console.log(params); // {foo: "bar", baz: "qux"}

点评:被低估的一个api,其实可以帮我们简化处理一些场景 实用程度:★★★★☆

String.prototype.trimStart() 和 String.prototype.trimEnd()

修整字符串前后的空格

1let message = "     Hello      ";
2message = message.trimEnd().trimStart();
3console.log(message); // "Hello"

点评:搜索时,可以检测用户是不是只搜索了空格 实用程度:★★★★☆

try catch升级

catch的参数有时候是多余的,现在可以省略

1try {
2  const data = JSON.parse(obj)
3  return true
4} catch {
5  return false
6}

点评:很好,又为我们码农减了一点点负 实用程度:★★★★☆

ES11(es2020)

动态 import

通过import声明引用的所有模块(包括初始化暂时用不到的模块)都会在初始化阶段前置加载,影响首屏性能.

import()能够在函数、分支等非顶层作用域使用,按需加载、懒加载都不是问题。

例子:路由懒加载

1import { lazy } from "react";
2
3const route = [
4  {
5    title: "博客",
6    path: "/home",
7    component: lazy(() => import("@/views/Home")),
8  },
9  {
10    title: "文章",
11    path: "/article",
12    search:"?category=all",
13    component: lazy(() => import("@/views/Article")),
14  },
15  {
16    path: "/article/:id",
17    component: lazy(() => import("@/views/Article/ArticleDetail")),
18  }
19]

点评:性能优化新的突破,鼓掌 实用程度:★★★★☆

可选链

平时我们访问对象第n级的时候,可能某一级的节点并不存在,导致js程序崩溃。除非提前用if或者&&预判,写起来有些啰嗦。而可选链它来了

?.用户检测不确定的中间节点,如果不存在中间节点则返回undefined。

1//一个对象
2const user = {
3  info:{
4    age: 11
5  }
6}
7
8//要取值:
9console.log(user.info.age) // 11
10
11//当无法判断user对象中是否有info的属性,info属性下面是否有age属性我们会:
12console.log(user&&user.info&&user.info.age)
13
14//而有了?. 可选链运算符之后 可以如此
15console.log(user?.info?.age)

点评:减负神技,必须满分 实用程度:★★★★★

Promise.allSettled

常规的promise.all并发多个实例只要有一个失败,就会停止执行并跳入catch方法。

而这个Promise.allSettled不管多个实例成功还是失败,一定会执行完,并将结果收集到一个数组中。

1Promise.all([
2    new Promise.reject("a1"),
3    new Promise.resolve("a2")
4]).then((ret) => {
5    // 不会执行
6    console.log(ret)
7}).catch((error) => {
8    // 因为有一个promise返回为reject。所以程序只会走到这里
9    // 输出:a1
10    console.log(error) 
11})
12
13// 使用es11的Promise.allSettled
14Promise.allSettled([
15    new Promise.reject("a1"),
16    new Promise.resolve("a2")
17]).then((ret) => {
18    // 输出 
19    // 0: {status: "fulfilled", value: "a1"},
20    // 1: {status: "rejected", value: "a2"}
21    console.log(ret)
22    
23    // 这样可以过滤掉rejected,避免整段程序运行错乱
24    handleFun(ret.filter(el => el.status !== "rejected"))
25})

点评:在需要执行多个的异步操作并收集所有结果时非常有效,即使某些异步操作可能失败。 实用程度:★★★★☆

BigInt(第7个基本类型)

BigInt是一种特殊的数字类型,它支持任意长度的整数。

先说说它诞生的意义:

JS 中的Number类型只能安全地表示-9007199254740991 (-(2^53-1)) 和9007199254740991(2^53-1)之间的整数,任何超出此范围的整数值都可能失去精度。

1// 注意最后一位的数字
29007199254740992 === 9007199254740993; //true

要创建一个 bigint,可以在一个整数的末尾添加字符n,或者调用函数 BigInt()。BigInt 函数使用字符串、数字等来创建一个BigInt。

19007199254740992n === 9007199254740993n; //false

点评:如果你需要用到这么大的数字,那它就派上用场了 实用程度:★★★★☆

全局this

全局this。在浏览器中它是 window, 在 Node.js 中它是global。

点评:好像除了在全局作用域快速拿到window,就没啥用了 实用程度:★★★☆☆

String.protype.matchAll()

原有的 match() 方法仅返回完整的匹配结果,却不会返回特定正则表达式组。而 matchAll()返回的迭代器不仅包括精确的匹配结果,还有全部的正则模式捕获结果

1var str = "From 2019.01.29 to 2019.01.30";
2var allMatchs = str.matchAll(/(?<year>d{4}).(?<month>d{2}).(?<day>d{2})/g);
3
4for (const match of allMatchs) {
5  console.log(match);
6}
7// [
8//   [
9//     "2019.01.29",
10//     "2019",
11//     "01",
12//     "29",
13//     index: 5,
14//     input: "From 2019.01.29 to 2019.01.30",
15//     groups: [Object: null prototype] { year: "2019", month: "01", day: "29" }
16//   ],
17//   [
18//     "2019.01.30",
19//     "2019",
20//     "01",
21//     "30",
22//     index: 19,
23//     input: "From 2019.01.29 to 2019.01.30",
24//     groups: [Object: null prototype] { year: "2019", month: "01", day: "30" }
25//   ]
26// ]

点评:正则家族又强大了,某些场景可能会很有用 实用程度:★★★★☆

ES12(es2021)

replaceAll

字符串新的原型方法(String.prototype.replaceAll)

有了它,以后【替换所有】就不用写正则了

1
2const str = "today is work will be finished today";
3
4//old
5//const newStr = str.replace(/today/g, "tomorrow");
6
7//new
8const newStr = str.replaceAll("today", "tomorrow");
9

Promise.any()

返回第一个 fullfilled 的 promise ,若全部 reject,则返回一个带有失败原因的 AggregateError

数字分隔符

数字分隔符是数字之间添加的下划线;当代码解析时,下划线会被自动去除;

1let n1 = 1_000_000_000;
2console.log(n1); // This will print: 1000000000

点评:写很大的数字可以更容易读,好功能 实用程度:★★★★☆

三个逻辑赋值

+||= 逻辑或赋值,等同于:a || (a = b)

&&= 逻辑与赋值,等同于:a && (a = b)

??= 逻辑合并赋值,等同于:a ?? (a = b)

举栗:

1//当左边为false时赋值
2let myPlaylist = {songsCount: 0, songs:[]};
3myPlaylist.songsCount ||= 100;
4console.log(myPlaylist); // This will print: {songsCount: 100, songs: Array(0)}
5
6//当左边为true时赋值
7let myFiles = {filesCount: 100, files:[]};
8myFiles.filesCount &&= 5;
9console.log(myFiles); // This will print: {filesCount: 5, files: Array(0)}
10
11//当左边为null或者undefined时赋值
12let userDetails = {firstname: "Katina", age: 24}
13userDetails.lastname ??= "Dawson";
14console.log(userDetails); // This will print: {firstname: "Katina", age: 24, lastname: "Dawson"}
15

点评:运用这哥仨,我们的代码逼格又高了 实用程度:★★★★☆

ES13(es2022)

at()

根据传入的数字获取字符串、数组的元素。不同于[]的是它还可以获取负数,也就是倒数

1// 1.数组
2const arr = [10, 20, 30, 40];
3console.log(arr.at(0)); // 10
4// at方法也支持负值
5console.log(arr.at(-1)); // 40
6
7// 2.字符串
8const message = "Hello";
9console.log(message.at(0)); // H
10console.log(message.at(-1)); // o
11

全局await

现在在全局作用域下,可以直接使用await

1function setTimeoutAsync(timeout) {
2   return new Promise((resolve) => {
3   setTimeout(() => {
4   resolve();
5  }, timeout);
6 });
7}
8// Waits for timeout - no error thrown
9await setTimeoutAsync(3000);
10

点评:不建议这么用,可能会阻塞js进程导致变慢 实用程度:★★★☆☆

类允许在constructor外部声明字段

1class Car {
2  color = "blue";
3  age = 2;
4}
5  const car = new Car();
6  console.log(car.color); // blue
7  console.log(car.age); // 2

等同于

1class Car {
2  constructor() {
3  this.color = "blue";
4  this.age = 2;
5 }
6}
7  const car = new Car();
8  console.log(car.color); // blue
9  console.log(car.age); // 2

点评:在类不需要接收参数,或者不需要调用super关键字时,就可以免写constructor了,且少写一些this 实用程度:★★★★☆

类的私有方法和字段

现在可以将私有字段和成员添加到类中,方法是在其前面加上井号 (#),试图从类外部访问它们会导致错误

1class Person {
2  #firstName = "Joseph";
3  #lastName = "Stevens";
4 get name() {
5  return `${this.#firstName} ${this.#lastName}`;
6  }
7}
8const person = new Person();
9console.log(person.name);//Joseph Stevens
10console.log(person.#firstName);
11console.log(person.#lastName);
12// SyntaxError: Private field "#firstName" must be
13// declared in an enclosing class

点评:如果你需要私藏类的变量,那就派上用场了 实用程度:★★★★☆

类静态块

ES13 允许在创建类时定义只执行一次的静态块,这类似于其他支持面向对象编程的语言(如 C# 和 Java)中的静态构造函数。

一个类的类主体中可以有任意数量的静态 {} 初始化块,它们将与任何交错的静态字段初始值设定项一起按照声明的顺序执行,我们可以在静态块中使用超属性来访问超类的属性。

1class Vehicle {
2static defaultColor = "blue";
3}
4class Car extends Vehicle {
5static colors = [];
6static {
7this.colors.push(super.defaultColor, "red");
8}
9static {
10this.colors.push("green");
11}
12}
13console.log(Car.colors); // [ "blue", "red", "green" ]
14

in 运算符

来检查一个对象中是否有一个特定的私有字段,使用 in 运算符

1class Car {
2#color;
3hasColor() {
4return #color in this;
5}
6}
7class House {
8#color;
9hasColor() {
10return #color in this;
11}
12}
13const car = new Car();
14const house = new House();
15console.log(car.hasColor()); // true;
16console.log(car.hasColor.call(house)); // false
17console.log(house.hasColor()); // true
18console.log(house.hasColor.call(car)); // false

Object.hasOwn()

用来检测对象中是否含有指定属性,它接受对象和属性作为参数,返回 true/false。

1const obj = { name: "hyl", color: "red" };
2
3console.log(Object.hasOwn(obj, "color")); // true
4console.log(Object.hasOwn(obj, "name")); // false
5
1function userAction() {
2try {
3apiCallThatCanThrow();
4} catch (err) {
5throw new Error("New error message", { cause: err });
6}
7}
8try {
9userAction();
10} catch (err) {
11console.log(err);
12console.log(`Cause by: ${err.cause}`);
13}
14
15

高级捕获错误

错误对象现在有一个 cause 属性,用于指定导致即将抛出的错误的原始错误。这有助于为错误添加额外的上下文信息并帮助诊断意外行为,我们可以通过在作为第二个参数传递给 Error() 构造函数的对象上设置 cause 属性来指定错误的原因。

1function userAction() {
2  try {
3    apiCallThatCanThrow();
4  } catch (err) {
5    throw new Error("New error message", { cause: err });
6  }
7}
8try {
9  userAction();
10} catch (err) {
11  console.log(`Cause by: ${err.cause}`);
12}
13
14//Cause by: ReferenceError: apiCallThatCanThrow is not defined

Array.prototype.findLast()和Array.prototype.findLastIndex()

快速查找到符合条件的最后一项或者下标。与find正相反

1const nums = [7, 14, 3, 8, 10, 9];
2const lastEven = nums.findLast((num) => num % 2 === 0);
3const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);
4console.log(lastEven); // 10
5console.log(lastEvenIndex); // 4