JavaScript高级深入浅出:ES8-ES12 知识点解析

535 阅读7分钟

介绍

本文是 JavaScript 高级深入浅出的第 13 篇,本文将会介绍 ES8-ES12 中的新语法与知识点解析

正文

1. ES8 新增知识

1.1 Object values

Object.values() 获取某个对象的所有值

const obj = {
  name: 'alex',
  age: 18,
}

// 我们通过 Object.keys() 获取某个对象的所有键
console.log(Object.keys(obj)) // [ 'name', 'age' ]

// ES8 允许我们通过 Object.values() 获取某个对象所有的值
console.log(Object.values(obj)) // [ 'alex', 18 ]

Object.values() 也接受其他的数据类型:

console.log(Object.values([1, 2, 3])) // [1, 2, 3]

console.log(Object.values('abc')) // ['a', 'b', 'c']

1.2 Object entries

通过Object.entries()可以获取到一个数组,数组中会存放可枚举属性的键值对数组

const obj = {
  name: 'alex',
  age: 18,
}

console.log(Object.entries(obj)) // [ [ 'name', 'alex' ], [ 'age', 18 ] ]

使用这种方式,也可以对对象进行遍历:

const obj = {
  name: 'alex',
  age: 18,
}

for (const [key, value] of Object.entries(obj)) {
  console.log(`key is ${key}, value is ${value}`)
}

Object.entries() 也接受其他类型的参数:

// [ [ '0', 1 ], [ '1', 2 ], [ '2', 3 ] ]
console.log(Object.entries([1, 2, 3]))

// [ [ '0', 'a' ], [ '1', 'b' ], [ '2', 'c' ] ]
console.log(Object.entries('abc'))

1.3 String Padding

1. padStart

padStart() 方法用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充。

  • 第一个参数是给定的长度
  • 第二个参数是如果长度不够,那么就从字符串开头补什么字符
let message = '5'

// 000005  填充0,让字符串的长度到达 6
console.log(message.padStart(6, 0))

2. padEnd

padEndpadStart的用法相同,只不过padEnd是填充在字符串的尾部的

let message = '5'

// 500000  填充0,让字符串的长度到达 6
console.log(message.padEnd(6, 0))

3. 案例

需求:隐藏身份证号、手机号之类的,前面用*,只保留后几位:

const phoneNumber = '13111111125'
const finalPhoneNumber = phoneNumber.slice(-4).padStart(11, '*')

console.log(finalPhoneNumber) // *******1125

1.4 Trailing Commas

ES8 允许函数的最后一位参数有尾逗号(Trailing Commas),此前,任何函数参数或调用函数存在尾逗号是会解析错误的,这个并不是一种规范写法,只是有人习惯这样写,而 JS ES8 则是支持了而已。

// 参数声明存在尾逗号,在 ES8 之前会报错
// 但是 ES8 就不会报错了
function foo(n1, n2,) { }

foo(1, 2,)

1.5 Object Descriptors

  • Object.getOwnPropertyDescriptors,这个在之前的篇章已经讲述过,获取某个对象的属性描述符
  • Async Function:async,await,异步函数描述符,这个会在后面的篇章中讲到

2. ES9 新增知识

  • Async interators:异步迭代器,这个后续篇章会讲到
  • Object spread operators:对象的展开运算
  • Promise finally:后续 Promise 篇讲到

3. ES10 新增知识

3.1 flat flatMap

flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

// 多维数组遍历,ES10 之前
// 需要一层一层的递归遍历,直到元素不再是数组
const numbers = [10, [20, 30], 40, [[[[[[['456']]]]]]]]
// 而 ES10 新增了 flat 函数
// flat 接收一个参数,该参数是遍历的深度,即最深到几维
// 注意:传入 2 ,那么就是 3 维,不传参数,默认就是遍历深度为 1
const flatNumbers = numbers.flat(7)
// [ 10, 20, 30, 40, '456' ]
console.log(flatNumbers)

flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩为一个新数组:

  • flatMap:先进行 map 操作,再做 flat 操作
  • flatMap 中的 flat 深度为 1
const numbers = [1, 2, 3]

const newNumbers = numbers.flatMap(item => item * 2)
// [2, 4, 6]
console.log(newNumbers)

flatMap的应用场景:

// 需求:获取所有通过空格分割的单词
const messages = ['hello world', 'good morning', 'have a good day']

const words = messages.flatMap(item => item.split(' '))

// ['hello', 'world', 'good', 'morning', 'have', 'a', 'good', 'day']
// 首先 map,然后再 flat(深度只有 1)
console.log(words)

如果用map需要这样

let words = messages.map(item => item.split(' '))
words = words.flat()

3.2 Object fromEntries

反向转换entries

const obj = {
  username: 'alex',
  birth: '20021030',
}
// 通过 Object.entries 获取到 entries
// [ [ 'username', 'alex' ], [ 'birth', '20021030' ] ]
const entries = Object.entries(obj)

// 而 fromEntries 就是让 entries 转为 Object 的
const newObj = Object.fromEntries(entries)
// { username: 'alex', birth: '20021030' }
console.log(newObj)

fromEntries的应用场景:

const url = `https://baidu.com/api/?user=101&q=nihao`

const queryString = new URL(url).search.slice(1)

const queryParams = new URLSearchParams(queryString)
// URLSearchParams { 'user' => '101', 'q' => 'nihao' }
console.log(queryParams)

for (const param of queryParams) {
  // [ 'user', '101' ]
  // [ 'q', 'nihao' ]
  console.log(param)
}

// queryParams 就是 entries,想要逆向为 Object,就可以使用
// Object.fromEntries()

const queryParamsObj = Object.fromEntries(queryParams)
// { user: '101', q: 'nihao' }
console.log(queryParamsObj)

3.3 trimStart trimEnd

trim()用于去除两端空格,

trimStart()trimEnd()就是用于去除头部和尾部的空格了

const str = '     hello world     '

const trimStr = str.trim()
const trimStartStr = str.trimStart()
const trimEndStr = str.trimEnd()

// hello world 11
console.log(trimStr, trimStr.length)
// hello world      16
console.log(trimStartStr, trimStartStr.length)
//      hello world 16
console.log(trimEndStr, trimEndStr.length)

3.4 其他

  • Symbol description:之前提到过了new Symbol("描述符")
  • Optional catch binding:后面 try...catch 时讲

4. ES11 新增知识

4.1 BigInt

在早期 JS 中,不能正确的表示过大的数字

  • 大于MAX_SAGE_INTEGER的数值,表示的可能是不正确的
// ES11 之前,最大的 int 类型
const maxInt = Number.MAX_SAFE_INTEGER
// 9007199254740991
console.log(maxInt)
// 9007199254740992  加 1 和加 2 的结果时一样的
console.log(maxInt + 1)
// 9007199254740992  那就说明过大的数字就存在问题
console.log(maxInt + 2)
// ES11 后,有 bigInt
// 最后面的 n 是不能省略的
const bigInt = 90071992547409910000000000n
// 90071992547409910000000000n
console.log(bigInt)
// 由于 int 与 bigInt 类型不兼容,因此无法使用 + - * /
// console.log(bigInt + 1)  // 报错
// 只能让参与运算的数字也是 bigInt
console.log(bigInt + 10n) // 没问题
  • int => bigInt使用BigInt()
const bigInt = 90071992547409910000000000n

const number = 100

console.log(bigInt + BigInt(number)) //没问题

4.2 Nullish Coalescing Operator

Nullish Coalescing Operator:空值合并运算 ??

let foo = false
// 如果 foo 有值,bar = foo,反之 bar = 1
const bar = foo ?? 1
// ES11 之前
const baz = foo !== undefined ? foo : 1
// false, false
console.log(bar, baz)

4.3 Optional Chaining

可选链是 ES11 新增的特性,主要是对于 null 和 undefined 进行更加优雅的判断 ?.

可选链的主要作用是如果遇到 undefined 不会抛出错误,导致下面的代码无法运行

const users = {
  user1: {
    username: 'Alex',
    friend: {
      name: 'John',
      girlFriend: {
        name: 'Alice',
      },
    },
  },
  user2: {},
}

// ES11 之前
// 获取 girlFriend name
const name1 =
  users.user1 &&
  users.user1.friend &&
  users.user1.friend.girlFriend &&
  users.user1.friend.girlFriend.name
    ? users.user1.friend.girlFriend.name
    : undefined

// 如果使用常规方法,会报错的
// TypeError: Cannot read property 'girlFriend' of undefined
// const name3 = users.user2.friend.girlFriend.name.getName()
// ES11 之后,使用可选链后 Alice
const name2 = users.user1.friend?.girlFriend?.name
// Alice, Alice
console.log(name1, name2)

4.4 Global This

在之前,在不同环境下获取全局对象的方式是不一样的:

  • 在浏览器中使用window
  • 在 Node 中使用global

在 ES11 中,提供了一个全局变量globalThis,该变量会自动会当前所处环境进行检测,并返回当前环境下的全局变量。

// Node -> gloabl     browser  -> window
console.log(globalThis)

4.5 其他

  • Dynamic Import:后续 ES Module 篇章
  • Promise.allSettled:后续 Promise 篇章
  • import meta:后续 ES Module 篇章

5. ES12 新增知识

5.1 FinalizationRegistry

FinalizationRegistry 实例让你可以在对象被垃圾回收时触发一个回调

  • 该类提供了一个方法,当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调(清理回调有时被称为finalizer
  • 你可以调用register方法,注册任何你想要清理回调的对象,传入该对象和值(值会作为到回调参数)
  • FinalizationRegistry对于注册在其中的对象是弱引用关系
const finalRegistry = new FinalizationRegistry(name => {
  console.log(`${name} 被销毁`)
})

let obj = { name: 'alex' }
// 这里的第二个参数,会被传递到回调的参数中
finalRegistry.register(obj, 'obj')
// 该行会触发回收的回调(建议在浏览器环境下测试)
// 打印:obj 被销毁
obj = null

5.2 WeakRef

WeakRef类允许您保留对另一个对象的弱引用,而不会阻止被弱引用对象被GC回收

  • 使用new WeakRef(obj)来创建一个 obj 的弱引用
  • 使用 weakRef 实例的 deref() 方法来获取该弱引用对应的数据
const finalRegistry = new FinalizationRegistry(name => {
  console.log(`${name} 被销毁`)
  if (name === 'obj') {
    console.log(wrObj && wrObj.deref())
  }
})

let obj = { name: 'alex' }
finalRegistry.register(obj, 'obj')
let wrObj = new WeakRef(obj)
// obj 被销毁,由于 WeakRef 是弱引用,也没有对应的值了
obj = null

注意:由于不同浏览器的 JS 引擎对于 GC 的实现不同,因此 WeakRef 慎用

5.3 logical assignment operators

逻辑赋值运算

||= 逻辑或赋值运算符

let message = ''
message = message || 'default value'
message ||= 'default value'

&&= 逻辑与赋值运算

let info = {
  name: 'alex',
}

let username = info && info.name

username &&= info.name
// alex
console.log(username)

??= 逻辑空赋值运算

let message = 123

message = message ? message : 'default value'
message ??= 'default value'
// 123
console.log(message)

5.4 其他

  • Number Separator:大数字分隔符,前面说到了
  • String.replaceAll:字符串替换

总结

本文是对于 ES8-ES12 的新增知识点的总结,下个篇章将会重点介绍 Proxy、Reflect

特别鸣谢

  • 催学社(崔大,cuixiaorui)