阅读 156

ES2020(ES11)几个好用的新特性

1. 空值合并 ??

只针对 nullundefined,对 ''0NaNfalse 无效,所以这同||是有区别的,写法是 ?? 连接

  • obj?.prop // 对象属性
  • obj?.[expr] // 同上
  • func?.(...args) // 函数或对象方法的调用
// 普通对象
const user = { age 0 }
let age = user.age ?? 50 // value is 0
let ageWrong = user.age || 50 // value is 50

// 函数
funtion register(a, b) {
  console.log(a, b);
}
this.register?.(1, 2);  // register函数存在,则执行此函数,并且可传参
复制代码

仅当 age 为 undefined 或者 null 时才使用 50 这个值

2. 可选链 ?.

在对象属前面使用 ?. 连接会检查该属性是否为 nullundefined,以避免代码崩溃。

const house = { owner: { name: 'Jim', pet: null }};
// 以前的写法
if(house && house.owner && house.owner.pet && house.owner.pet.type === 'dog'){
  console.log('owner has a dog');
}
// 新特性的写法
if (house?.owner?.pet?.type === 'dog') {
  console.log('owner has a dog');
}
复制代码

&&||运算符一起使用时,需要用括号来表明优先级,要不会报错。

a && b ?? c // 错误
(a && b) ?? c // 正确
复制代码

3. Promise.allSettled

以前处理多个异步请求一并返回使用的是 Promise.all 但如果其他一个请求失败了就会报出异常,但使用 Promise.allSettled 则不一样,不管请求成功或失败都会把结果返回过来

const promises = [Promise.resolve(1), Promise.reject(2)];
const [result1, result2] = await Promise.allSettled(promises);
复制代码

即使其他 promise 被拒绝了,我们仍然可以在这里使用 result1 的值。

  • Promise.all

都成功,才会成功,有一个失败,就返回失败。 返回数组,每一个成员都是实例对应的结果。

  • Promise.allSettled

一旦结束,状态总是fulfilled,不会变成rejected。 返回数组,数组的每一个成员都是对象,每个对象都有status属性,该属性的值只可能是字符串fulfilled或字符串rejectedfulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值.

  • Promise.race

其中一实例返回状态,p的状态就会发生改变,并且不会再变。 只要有返回值,立马停止其他请求。

4. matchAll

如果要用正则表达式查找所有的匹配项,可以用 match 来得到所有子字符串。但是,如果你既需要子字符串又需要索引,该怎么办?这时可以用 matchAll 并进行重复匹配。

const matches = 'Here are some numbers: 3 15 32'.matchAll(/\d+/g);
for (const match of matches) {
  console.log(match);
}

// 输出:
// ["3", index: 22, input: "Here are some numbers: 3 15 32", groups: undefined]
// ["15", index: 24, input: "Here are some numbers: 3 15 32", groups: undefined]
// ["32", index: 27, input: "Here are some numbers: 3 15 32", groups: undefined]
复制代码

ES2020之前

let regex = /t(e)(st(\d?))/g;
let string = 'test1test2test3';

let matches = [];
let match;
while (match = regex.exec(string)) {
  matches.push(match);
}
console.log(matches); // 返回正则表达式在当前字符串所有匹配(数组)
复制代码

可一次性取出所有匹配,但是返回的是一个遍历器(Iterator),而非数组。可以用for of循环取出。 相对于返回数组,返回遍历器的好处在于,如果遍历结果是一个很大的数组,那么遍历器比较节省资源。 遍历器转换成数组,也是比较方便,可以使用...运算符和Array.from()就可以了。

let regex = /t(e)(st(\d?))/g;
let string = 'test1test2test3';
[...string.matchAll(regex)];
// 或
Array.from(string.matchAll(regex));
复制代码

5. BigInt

在处理大于 9007199254740991 的数字时应该用 BigInt。 BigInt 在数字末尾用 n 表示。

9_007_199_254_740_991 + 2; // 9007199254740992

BigInt(9_007_199_254_740_991) + BigInt(2) // 9007199254740993n
复制代码

6. 动态导入

以前是先导入再使用,新特性可以动态导入使用了,因为 import() 会与模块一起返回一个 Promise

const utils = await import('utils');
复制代码

7. globalThis

如果你的代码需要在多个环境(例如浏览器和 Node 服务器)下运行,那么它们所使用全局对象名称并不一致。在浏览器中用的是 windowNode 则用的是 global,而 web worker 用的是 self 。现在,无论代码在哪种环境中运行,globalThis 都能够为你提供正确的全局对象。

if (typeof globalThis.alert === 'function'){
  globalThis.alert('hello');
}
复制代码

8. 管道运算符 |>

有时也会遇到这个场景,一个参数,不断经过函数处理,结果又被当作参数传给下个函数

const result = foo(bar(baz('hello world')))
复制代码

管道给人的感觉就是一条流水线,从上到下,参数输入,结果输出,很简洁~

const result = 'hello world' |> baz |> bar |>foo
复制代码

再举个栗子:

const double = (n) => n * 2;
const increment = (n) => n + 1;

double(increment(double(double(5)))); // 42

//使用管道操作符
5 |> double |> double |> increment |> double; // 42
复制代码

想在项目中使用同样需要babel7,并使用 @babel/plugin-proposal-pipeline-operator 插件

"plugin": [
  [
    "@babel/plugin-proposal-pipeline-operator",
    {
      "proposal": "minimal"
    }
  ]
]
复制代码

注意,在配置 .babelrc 时需要指定 proposal 参数,更多参数请了解 babeljs.io/docs/en/bab… 如果函数还需要别的参数,可以这么写:

const result = 'hello world' |> baz |> (str => baz(str, 'other args')) |> foo
复制代码

但是如此就看着不简洁了,所以有个备选方案:使用#代表上个结果值,即:

const result = 'hello world' |> baz |> baz(#, 'other args')) |> foo
复制代码

9. 私有变量

在变量前加 #,可以定义私有变量,仅能在类内部使用

class Message {
  #message = "Howdy"
  greet() { console.log(this.#message) }
}
const greeting = new Message()
greeting.greet()
console.log(greeting.#message) 
复制代码

如果本文对你有所帮助,感谢点一颗小心心,您的支持是我继续创作的动力!

文章分类
前端
文章标签