总结ES11—ES13新特性——提升开发效率

ECMAScriptJavaScript 的关系

ECMAScriptJavaScript 的标准与规范,JavaScriptECMAScript 标准的实现和扩展。

ES11,即 ECMAScript 2020

ES12,即 ECMAScript 2021

ES13,即 ECMAScript 2022
ECMAScript

ES11(ECMAScript 2020)新特性

1、BigInt

JavaScript 中,数值类型 Number 是 64 位浮点数,所以计算精度和表示范围都有一定限制。
ES11 新增了 BigInt 数据类型,它可以表示任意大的整数。其语法如下:

BigInt(value);

其中 value 是创建对象的数值,可以是字符串或者整数。

JavaScript 中,Number 基本类型可以精确表示的最大整数是
  2 53   \ 2^{53}\  253 

因此之前会有这样的问题:

let max = Number.MAX_SAFE_INTEGER;    // 最大安全整数 Math.pow(2, 53) - 1

let max1 = max + 1
let max2 = max + 2

max1 === max2   // true

有了 BigInt 之后,这个问题就不复存在了:

let max = BigInt(Number.MAX_SAFE_INTEGER);

let max1 = max + 1n
let max2 = max + 2n

max1 === max2   // false

1、如何判断变量是否为 BigInt 类型?

  • 通过 typeof 操作符(返回字符串"bigint")
typeof 1n === 'bigint'; // true 
typeof BigInt('1') === 'bigint'; // true 
  • 通过 Object.prototype.toString 方法(返回字符串"[object BigInt]"):
Object.prototype.toString.call(10n) === '[object BigInt]';    // true

2、BigIntNumber 的比较

10n === 10 // false 
10n == 10  // true 
// 比较
1n < 2;    // true 
2n > 1;    // true 
2 > 2;     // false 
2n > 2;    // false 
2n >= 2;   // true

2、空值合并运算符(??)

在写代码时,如果某个属性不为 nullundefined,那么就获取该属性,如果该属性为 nullundefined,则取一个默认值:

// 三元运算符
const name = title ? title : 'default'; 
// || 运算符
const name =  title || 'default'; 

但是 || 的写法存在一定的缺陷,当 title 为 0 或 false 的时候也会走到 default 的逻辑。所以 ES11 引入了 ?? 运算符。只有 ?? 左边为 nullundefined 时才返回右边的值:

const title = false; 
const name =  title ?? 'default';  // name = false;

3、可选链操作符(?.)

在开发过程中,经常需要获取深层次属性,例如 res.data.userInfo.name。但在获取 name 这个属性前需要一步步的判断前面的属性是否存在,否则并会报错:

const name = (res && res.data && res.data.userInfo && res.data.userInfo && res.data.userInfo.name) || 'default';

为了简化上述过程,ES11 引入了「链判断运算符」?.,可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。在引用为 nullundefined 的情况下不会引起错误,该表达式短路返回值是 undefined。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined

const name = res?.data?.userInfo?.name || 'default';

可选链有以下三种形式:

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()

4、Promise.allSettled

Promise.allSettled 的参数接受一个 Promise 的数组,返回一个新的 Promise。唯一的不同在于,执行完之后不会失败,j即当 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 方法不太一样的地方。

5、String.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

ES12(ECMAScript 2021)新特性

1、String.replaceAll()

replaceAll() 方法会返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉,替换规则可以是字符串或者正则表达式

let string = 'hello world, hello ES12'
string.replace(/hello/g,'hi')    // hi world, hi ES12
string.replaceAll('hello','hi')  // hi world, hi ES12

注意的是,replaceAll 在使用正则表达式的时候,如果非全局匹配(/g),会抛出异常:

let string = 'hello world, hello ES12'
string.replaceAll(/hello/,'hi') 
// Uncaught TypeError: String.prototype.replaceAll called with a non-global

2、数字分隔符

数字分隔符可以在数字之间创建可视化分隔符,通过 _ 下划线来分割数字,使数字更具可读性,可以放在数字内的任何地方:

const money = 1_000_000_000
//等价于
const money = 1000000000

该新特性同样支持在八进制数中使用:

const number = 0o123_456
//等价于
const number = 0o123456

3、Promise.any

Promise.anyES12 新增的特性,它接收一个 Promise 可迭代对象(例如数组)。

  • 只要其中的一个 promise 成功,就返回那个已经成功的 promise
  • 如果可迭代对象中没有一个 promise 成功,就返回一个失败的 promiseAggregateError 类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。
const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.resolve('result'),
]

Promise.any(promises).then((value) => {
  console.log('value: ', value)
}).catch((err) => {
  console.log('err: ', err)
})

// 输出结果:value:  result

如果所有传入的 promises 都失败:

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.reject('ERROR C'),
]

Promise.any(promises).then((value) => {
  console.log('value:', value)
}).catch((err) => {
  console.log('err: ', err)
  console.log(err.message)
  console.log(err.name)
  console.log(err.errors)
})

输出结果:

err: AggregateError: All promises were rejected
All promises were rejected
AggregateError
["ERROR A", "ERROR B", "ERROR C"]

4、逻辑赋值操作符

ES12 中新增了几个逻辑赋值操作符,可以用来简化一些表达式:

// 等同于 a = a || b
a ||= b;
// 等同于 c = c && d
c &&= d;
// 等同于 e = e ?? f
e ??= f;

ES13(ECMAScript 2022)新特性

1、Object.hasOwn()

ES13 之前,可以使用 Object.prototype.hasOwnProperty() 来检查一个属性是否属于对象。

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

const userInfo = {
  name: 'admin'
};

console.log(Object.prototype.hasOwnProperty.call(userInfo, 'name'));
console.log(Object.hasOwn(userInfo, 'name'));

2、Array.at()

at() 是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索。

const array = [0, 1, 2, 3, 4, 5];

console.log(array[array.length-1]);  // 5
console.log(array.at(-1));  // 5

console.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

ES13 规范中,new Error() 中可以指定导致它的原因:

function readFiles(filePaths) {
  return filePaths.map(
    (filePath) => {
      try {
        // ···
      } catch (error) {
        throw new Error(
          `While processing ${filePath}`,
          {cause: error}
        );
      }
    });
}

4、Top-level Await

ES8 中,引入了 async 函数和 await 关键字,以简化 Promise 的使用,但是 await 关键字只能在 async 函数内部使用。如果在异步函数之外使用 await 就会报错:SyntaxError - SyntaxError: await is only valid in async function

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

ES13 之前:

  // user.js
  let users;

  export const fetchUsers = async () => {
    const resp = await fetch('https://xxx/api/user');
    users =  resp.json();
  }
  fetchUsers();

  export { users };

  // mine.js
  import { users } from './user.js';
  console.log('users: ', users);
  console.log('mine module');

我们还可以立即调用顶层 async 函数(IIAFE):

  (async () => {
    const resp = await fetch('https://xxx/api/user');
    users = resp.json();
  })();
  export { users };

这样会有一个缺点,直接导入的 usersundefined,需要在异步执行完成之后才能访问它:

// mine.js
import { users } from './user.js';

console.log('users:', users); // undefined

setTimeout(() => {
  console.log('users:', users);
}, 100);

console.log('mine module');

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

  // user.js
  const resp = await fetch('https://xxx/api/user');
  const users = resp.json();
  export { users};

  // mine.js
  import {users} from './user.js';

  console.log(users);
  console.log('mine module');

顶级 await 在以下场景中将非常有用:

  • 动态加载模块:
 const lang = await import(`/i18n/${language}`);
  • 资源初始化:
const connection = await getConnectParams();
  • 依赖回退:
let citydata;
try {
  citydata = await import('https://xxx.json');
} catch {
  citydata = await import('https://xxx.xxx.json');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值