一篇文章消化 ES7、ES8、ES9 主要新特性

7,679 阅读6分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

这里收集了某段时间前准备面试用的材料,方便更好的理解, 并整理出了一些 ES7至ES9 的常用新特性,在这做下分享,巩固记忆。

ES7 新特性

includes()-判断数组是否含有特定的值

如果需要判断数组中是否含有某个特定的值,以往可能更多的是使用indexOf的方式,有则返回查找值的下标,没有则返回-1,这在判断值是否存在,并需要获取值下标的场景比较友好。

const arr = [1, 2, 3]
arr.indexOf(2) // 1

includes则直接返回 Boolean 值,不返还值的下标,更纯粹的用于判断值的数组包含与否。

const arr = [1, 2, 3]
arr.includes(2) // true
arr.includes(4) // false

**-指数操作符

如果需要进行指数的计算,在以往需要怎么实现? 例如,输出2的10次方,除了自定义递归方法去实现之外,可以用Math.pow函数实现。

Math.pow(2, 10) // 1024

在 ES7 中,新增了指数的操作运算符**,可以更方便地去进行指数运算。

2**10 // 1024

ES8 新特性

async/await-异步操作关键字

为了解决回调地狱的问题,Promise 是个不错的解决方案,但在处理复杂流程的场景,Promise 欠缺了一定的语义化,各个流程通过 .then 连接,读起代码来较为恶心。

设想一个实际场景:用户登录的操作,在用户请求完登录接口并验证通过后,再请求用户信息接口获取用户信息,Promise 实现方式如下:

userLogin(data)
	.then(res=>{
		return getUserInfo(res.token)
	})
	.then(res=>{
		console.log(res)
	})
	.catch(err=>{
		console.error(err)
	})

如果流程更复杂的话,then 连接会更多,直到最后的 catch,这对于代码维护增加了不少阅读成本,不能够很好的表达执行流程。

于是便有了 async/await, 既可以用同步代码的方式去实现,又可以异步处理的关键字,还支持 try catch 捕获异常,两者不能脱离开来单独使用,await 后面一定得是 Promise 对象,否则,它将会自动的包装成一个 Promise 对象。

使用 async/await 的实现如下:

try {
    let loginRes = await userLogin(data)
    let res = await getUserInfo(loginRes.token)
    console.log(res)
  } catch (err) {
    console.error(err)
  }

values()/entries()-遍历对象新方式

ES7 新增了两个对象遍历方法,Object.values()Object.entries()Object.values()返回一个数组,包含对象自身的所有可遍历的属性值,示例:

const user = {name: "Sam", age: "25"}
Object.values(user) // ["Sam", "25"]

const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj) // ["b", "c", "a"]

需要注意的是,如果属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是 b、c、a。 Object.entries() 同样返回一个数组,包含对象自身的所有可比案例的键值和属性的成组,示例:

const user = {name: "Sam", age: "25"}
Object.entries(user) // [["name", "Sam"], ["age", "25"]]

const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.entries(obj) // [["2", "b"], ["7", "c"], ["100", "a"]]

String padding-字符串填充

ES8 中对 String 新增了两个实例函数,分别是padStartpadEnd,意味将字符创添加到原始字符串的开头或结尾。 padStartpadEnd都允许接受两个参数:

  • targetLength:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
  • padString:(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为 " "。 示例:
let str = 'Sam'
str.padStart(6,'its')// 'itsSam'
str.padEnd(6,'its')// 'Samits'

getOwnPropertyDescriptors-返回元素自身属性描述

如果想获取某个元素的自身属性描述,可以使用Object.getOwnPropertyDescriptors()如果没有任何属性,则会返回空对象。

使用其可以解决Object.assign()无法正确拷贝 get 和 set 的问题。 示例:

const obj = {name: "Sam"}
Object.getOwnPropertyDescriptors(obj)

// name:
// configurable: true
// enumerable: true
// value: "Sam"
// writable: true
// __proto__: Object
// __proto__: Object

ES9 新特性

for await of-异步迭代器

如果在 async/await中使用循环中去调用异步函数,则不会正常执行,例如:

async function demo(arr) {
  for (let i of arr) {
    await handleDo(i);
  }
}

该示例中,循环本身仍然保持着同步操作,并在内部异步函数之前,全部调用完成结束。

为了解决该问题,ES9 中引入了异步迭代器,允许在循环中去调用异步函数,示例:

async function demo(arr) {
  for await (let i of arr) {
    handleDo(i);
  }
}

Promise.finally()-Promise 结束触发

当我们调用 Promise 时,下一个结果要么是.then(),要么是失败触发.catch()。这样导致有些代码需要在这两处重复写两遍,导致代码冗余,如果希望 Promise 不管是成功还是失败,都执行同样的代码,可以使用.finally(),它允许你在执行结束后触发。 示例:

function login() {
  	userLogin()
  	.then(res=>{
  	  console.log(res);
	})
  	.catch(err => {
  	  console.log(err);
 	 })
	.finally(() => {
  	  console.log('finally');
	});
}

Rest/Spread 属性

Rest参数语法允许我们将一个不定数量的参数表示为一个数组。示例:

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}

ES9为对象解构提供了和数组一样的Rest参数和展开操作符,示例:

const Obj = {a: 1, b: 2, c: 3};
const { a, ...x } = Obj;// a = 1,x = { b: 2, c: 3 }

正则表达式新特性

s (dotAll) 标志

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

console.log(/foo.bar/.test('foo\nbar')) // false
console.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')) // true
console.log(re.dotAll) // true
console.log(re.flags) // 's'
命名捕获组

在一些正则表达式模式中,使用数字进行匹配可能会令人混淆。因为美式英语中的日期表示法和英式英语中的日期表示法不同,所以很难区分哪一组表示日期,哪一组表示月份:

const re = /(\d{4})-(\d{2})-(\d{2})/;
const match= re.exec('2019-01-01');
console.log(match[0]); // → 2019-01-01
console.log(match[1]); // → 2019
console.log(match[2]); // → 01
console.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); // → 2019
console.log(match.groups.month); // → 01
console.log(match.groups.day); // → 01

写在最后

写作不易,希望可以获得你的一个「赞」。如果文章对你有用,可以选择「收藏」。 如有文章有错误或建议,欢迎评论指正,谢谢你。❤️

欢迎阅读其它文章