ES7

arr.includes( )

在ES7之前,如果我们想判断一个数组中是否包含某个元素,需要通过 indexOf 获取结果,并且判断是否为 -1。
在ES7中,我们可以通过includes来判断一个数组中是否包含一个指定的元素,根据情况,如果包含则返回 true,
否则返回false。

  • arr.includes(valueToFind, fromIndex) ```javascript const names = [“abc”, “cba”, “nba”, “mba”, NaN]

if (names.indexOf(“cba”) !== -1) { // 存在就会返回索引值 console.log(“包含abc元素”) }

// ES7 ES2016 if (names.includes(“cba”, 2)) { // 从索引2开始找 console.log(“包含abc元素”) }

// indexOF 无法判断 NaN,因为返回值就是 -1 if (names.indexOf(NaN) !== -1) { console.log(“包含NaN”) } console.log(names.indexOf(NaN)); // -1

// includes 能正常判断 NaN if (names.includes(NaN)) { console.log(“包含NaN”) }

  1. <a name="Au7VA"></a>
  2. ## 指数(乘方) exponentiation运算符
  3. 在ES7之前,计算数字的乘方需要通过 Math.pow 方法来完成。<br />在ES7中,增加了`**` 运算符,可以对数字来计算乘方。
  4. ```javascript
  5. const result1 = Math.pow(3, 3)
  6. // ES7: **
  7. const result2 = 3 ** 3
  8. console.log(result1, result2)

ES8

Object.values( )

之前我们可以通过Object.keys()获取一个对象所有的 key
在 ES8 中提供了Object.values()来获取所有的 value 值

  1. const obj = {
  2. name: "why",
  3. age: 18
  4. }
  5. // 以数组的形式返回
  6. console.log(Object.keys(obj)) // [ 'name', 'age' ]
  7. console.log(Object.values(obj)) // [ 'why', 18 ]
  8. // 也可以接收参数,用的非常少
  9. // 传数组就返回数组,传字符串就会按字符展开返回新数组
  10. console.log(Object.values(["abc", "cba", "nba"])) // [ 'abc', 'cba', 'nba' ]
  11. console.log(Object.values('abc')) // [ 'a', 'b', 'c' ]

Object.entries( )

通过Object.entries()可以获取到一个数组,数组中会存放可枚举属性的键值对数组。就是格式化成套娃数组。
这个数组和初始化 Map 时传入的对象的格式是一样的,或许数组包数组的方式是对象的一种通用格式?

  • 这种可枚举属性的键值对数组称为entries ```javascript const obj = { name: “why”, age: 18 }

console.log(Object.entries(obj)) // [ [ ‘name’, ‘why’ ], [ ‘age’, 18 ] ]

// 这种键值对数组可以被 forEach 遍历 const objEntries = Object.entries(obj) objEntries.forEach(item => { console.log(item[0], item[1]) // name why , age 18 })

// 也可以传入参数,都会返回格式化成键值对数组的形式 console.log(Object.entries([“abc”, “cba”, “nba”])) // [ [ ‘0’, ‘abc’ ], [ ‘1’, ‘cba’ ], [ ‘2’, ‘nba’ ] ] console.log(Object.entries(“abc”)) // [ [ ‘0’, ‘a’ ], [ ‘1’, ‘b’ ], [ ‘2’, ‘c’ ] ]

  1. <a name="T6wjB"></a>
  2. ## 字符串前后填充 String Padding
  3. 某些字符串我们需要对其进行前后的填充,来实现某种格式化效果,ES8中增加了`padStart()` 和 `padEnd()` 方法,分别是对字符串的首尾进行填充的。
  4. ```javascript
  5. const message = 'Hello World'
  6. // 参数一:要填充到的长度,也就是填充完后字符串的长度,如果小于现有长度,则返回字符串本身
  7. // 参数二:可选,填充的内容,默认为空格
  8. const newMessage = message.padStart(15, '*').padEnd(20, '-')
  9. console.log(newMessage) // ****Hello World-----
  10. // 案例:隐藏身份证号的前几位
  11. const cardNumber = '123456200020206789'
  12. // 截取后4位
  13. const lastFourCard = cardNumber.slice(-4)
  14. // 用*填充截取的4位到身份证号长度
  15. const finalCard = lastFourCard.padStart(cardNumber.length - 4, '*')
  16. console.log(finalCard) // **********6789

Trailing Commas

在ES8中,我们允许在函数定义和调用时多加一个逗号,只是为了照顾部分转语言的开发者使用习惯。

  1. function foo(m, n,) { console.log(m + n) }
  2. foo(1, 2,) // 3

Object Descriptors

ES8中增加了另一个对对象的操作是Object.getOwnPropertyDescriptors(),详情戳 7. 深入 JS 面向对象 属性描述符部分。

ES9

Async iterators 迭代器

待定

Object spread operators:对象展开运算符

详情戳 12. 模板字符串、展开语法、Symbol… 展开运算符

Promise finally

待定

ES10

flat flatMap 降维

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

  1. // 数组套数组套了三层
  2. const nums = [10, 20, [2, 9], [[30, 40], [10, 45]], 78, [55, 88]]
  3. const newNums = nums.flat() // 默认向下递归 1 层
  4. console.log(newNums) // [ 10, 20, 2, 9, [ 30, 40 ], [ 10, 45 ], 78, 55, 88 ]
  5. const newNums2 = nums.flat(2) // 递归遍历 2 层
  6. console.log(newNums2)
  7. // [ 10, 20, 2, 9, 30, 40, 10, 45, 78, 55, 88 ]

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
注意一:flatMap 是先进行 map 操作,再做 flat 的操作;
注意二:flatMap 中的 flat 相当于深度为 1;

  1. const nums2 = [10, 20, 30]
  2. // flatMap()
  3. const newNums3 = nums2.flatMap(item => {
  4. return item * 2 // 返回值会放入新数组中,这个回调函数是map的操作
  5. })
  6. // map()
  7. const newNums4 = nums2.map(item => {
  8. return item * 2
  9. })
  10. // 两个结果是一样的
  11. console.log(newNums3) // [ 20, 40, 60 ]
  12. console.log(newNums4) // [ 20, 40, 60 ]

flatMap()有啥应用场景:比如对数组元素内容进行分割。

  1. // 希望将数组元素内容以空格分隔
  2. const messages = ['Hello World', 'hello lyh', 'my name is zs']
  3. // 以前做法,两次循环很不方便
  4. const word = new Array()
  5. for (let i = 0; i < messages.length; i++) {
  6. const tempArr = messages[i].split(' ')
  7. for (let i = 0; i < tempArr.length; i++) {
  8. word.push(tempArr[i])
  9. }
  10. }
  11. // flatMap
  12. const word2 = messages.flatMap(item => {
  13. // map 会让数组变成 [ ['Hello','World'], [], [] ] 这样的套娃结构,然后 flat 刚好降维了
  14. return item.split(' ')
  15. })
  16. console.log(word) // ['Hello', 'World','hello','lyh','my','name','is','zs']
  17. console.log(word2) // ['Hello', 'World','hello','lyh','my','name','is','zs']

Object.fromEntries( )

ES8 我们可以通过 Object.entries 将一个对象转换成 entries,那么如果我们有一个entries了,就可以利用 Object.fromEntries()将它转换成对象,相当于一个Object.entries()的逆运算。

  1. const obj = {
  2. name: "why",
  3. age: 18
  4. }
  5. console.log(Object.entries(obj)) // [ [ 'name', 'why' ], [ 'age', 18 ] ]
  6. console.log(Object.fromEntries(Object.entries(obj))); // { name: 'why', age: 18 }
  1. const queryString = 'name=why&age=18&height=1.88' // 网络请求中的查询字符串
  2. const queryParams = new URLSearchParams(queryString) // API
  3. console.log(queryParams) // URLSearchParams { 'name' => 'why', 'age' => '18', 'height' => '1.88' }
  4. // 返回的对象可以遍历,并且是数组形式
  5. for (const param of queryParams) {
  6. console.log(param) // [ 'name', 'why' ] [ 'age', '18' ] [ 'height', '1.88' ]
  7. }
  8. // fromEntries() 虽然接收 entries,但是可迭代对象也可以接收,所以将查询字符串传入,可直接得到普通对象
  9. const paramObj = Object.fromEntries(queryParams)
  10. console.log(paramObj) // { name: 'why', age: '18', height: '1.88' }

trimStart trimEnd

去除一个字符串首尾的空格,我们可以通过trim()方法,单独去除前后则可用trimStart()``trimEnd()

  1. const message = " Hello World "
  2. console.log(message.trim())
  3. console.log(message.trimStart())
  4. console.log(message.trimEnd())
  5. //Hello World
  6. //Hello World
  7. // Hello World

Symbol description

详情戳
12. 模板字符串、展开语法、Symbol…

Optional catch binding(可选的捕获绑定)

待定

ES11

BigInt 大数

在早期的JavaScript中,我们不能正确的表示过大的数字。大于MAX_SAFE_INTEGER的数值,表示的可能是不正确的。
那么ES11中,引入了新的数据类型BigInt,用于表示大的整数:BitInt的表示方法是在数值的后面加上n

  1. // ES11之前 max_safe_integer 表示 js 支持的最大安全数字
  2. const maxInt = Number.MAX_SAFE_INTEGER
  3. console.log(maxInt) // 9007199254740991
  4. // 最大值加1加2,都是一样的,可见超过最大值计算开始不安全
  5. console.log(maxInt + 1) // 9007199254740992
  6. console.log(maxInt + 2) // 9007199254740992
  7. // ES11之后新的数据类型:BigInt
  8. const bigInt = 900719925474099100n
  9. // 报错,因为 10 是Number类型,不同类型的数据间不能直接计算
  10. // 为什么 10+1.2 整型和浮点型计算可以?因为它们进行了类型隐式转换,而 BigInt 类型不会自动转换
  11. // console.log(bigInt + 10)
  12. // 手动类型转换,成功计算
  13. console.log(bigInt + 10n)
  14. // Number 和 BigInt 互相转换
  15. const num = 100
  16. console.log(bigInt + BigInt(num))
  17. const smallNum = Number(bigInt)
  18. console.log(smallNum)

Nullish Coalescing Operator 空值合并操作符

之前变量默认值,我们会使用||或操作。但是或操作存在一个隐患,它为认为 空、0、null、undefined 都为false,当传入的值刚好为空和 0 的时候,或运算符会刚好舍去空和 0。
ES11,Nullish Coalescing Operator 增加了空值合并操作符:??,它只会识别 null、undefined 为 false。

  1. // || 或运算存在隐患
  2. let foo = ''
  3. let test = foo || '默认值'
  4. console.log(test) // 默认值
  5. let foo1 = 0
  6. let test1 = foo1 || '默认值'
  7. console.log(test1) // 默认值
  8. // ?? 空值合并运算符
  9. let fn = ''
  10. let t1 = fn ?? 'defalut value'
  11. console.log(t1) //"空"
  12. let fn1 = 0
  13. let t2 = fn1 ?? 'defalut value'
  14. console.log(t2) // 0

Optional Chaining 可选链

可选链?.,主要作用是让我们的代码在进行 null 和 undefined 判断时更加清晰和简洁:

  1. // info 是一个隐藏的对象
  2. const info = {
  3. name: "why",
  4. // friend: {
  5. // girlFriend: {
  6. // name: "hmm"
  7. // }
  8. // }
  9. }
  10. // 我们预期 info 对象有 friend 属性,但是某种原因没了
  11. // 那就会立即因 undefined.girlFriend 导致报错,终止后面代码的执行
  12. console.log(info.friend.girlFriend.name)
  13. // 之前会用 &&与运算 和 if 先判断属性是否存在,但是这样太烦了
  14. if (info && info.friend && info.friend.girlFriend) {
  15. console.log(info.friend.girlFriend.name)
  16. }
  17. // ES11 提供了可选链(Optional Chainling)
  18. // 以 ?. 的方式调用,若中间某个属性不存在,则会立即停止调用,并返回 undefined,不影响后续代码
  19. console.log(info.friend?.girlFriend?.name) // undefined
  20. console.log('其他的代码逻辑')

Global This

在之前我们希望获取 JavaScript 环境的全局对象,不同的环境获取的方式是不一样的:

  • 比如在浏览器中可以通过thiswindow来获取;
  • 比如在Node中我们需要通过global来获取;

我们写的代码不知道会在什么环境执行,所以会进行兼容处理

  1. // 伪代码兼容性处理
  2. let myGO = undefined
  3. // 对当前执行环境判断,获取全局对象
  4. if (window !== undefined) {
  5. // 浏览器环境
  6. myGO = window
  7. } else {
  8. // node 环境
  9. myGO = global
  10. }

那么在ES11中对获取全局对象进行了统一的规范:globalThis无论什么环境下都指向当前环境的全局对象

  1. console.log(globalThis);

for…in

在ES11之前,虽然很多浏览器支持for…in来遍历对象类型,但是并没有被ECMA标准化,有些浏览器的for…in 是遍历对象的 value,有些是遍历 key。
在ES11中,对其进行了标准化,for…in 用来遍历对象的 key 。

  1. const obj = {
  2. name: 'zs',
  3. age: 19
  4. }
  5. for (const item in obj) {
  6. console.log(item); // name, age
  7. }

Dynamic Import

Promise.allSettled

import meta

ES12

FinalizationRegistry

FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调。

  • FinalizationRegistry 可以让已注册的对象在被 GC 回收时调用一个回调函数。
  • 通过调用register()方法,注册任何你想要清理回调的对象,传入该对象和所含的值,这个值可以用来区分当前回收的对象是哪个 ```javascript // ES12: FinalizationRegistry类 // 当对象回收就会调用这个回调函数,value 为注册时传入的值,可以通过value区分当前回收的对象 const finalRegistry = new FinalizationRegistry((value) => { console.log(“注册在finalRegistry的对象, 某一个被销毁”, value) })

let obj = { name: “why” } let info = { age: 18 }

// 将对象注册到 finalRegistry 对象中 finalRegistry.register(obj, “obj”) finalRegistry.register(info, “info”) // 手动断开引用 obj = null info = null

// 一会后就会执行回调函数

  1. <a name="dwtAB"></a>
  2. ## WeakRefs 弱引用
  3. 如果我们默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用,如果我们希望是一个弱引用的话,之前需要使用 WeakSet、WeakMap,现在可以使用`WeakRef`类;
  4. WeakRef 创建的弱引用,需要`deref()`方法才能访问指向的内存地址。
  5. ```javascript
  6. // ES12: WeakRef类
  7. // WeakRef.prototype.deref:
  8. // > 如果原对象没有销毁, 那么可以获取到原对象
  9. // > 如果原对象已经销毁, 那么获取到的是undefined
  10. const finalRegistry = new FinalizationRegistry((value) => {
  11. console.log("注册在finalRegistry的对象, 某一个被销毁", value)
  12. })
  13. let obj = { name: "why" }
  14. // 建立弱引用 info 指向 {name: 'why'}
  15. let info = new WeakRef(obj)
  16. finalRegistry.register(obj, "obj")
  17. obj = null
  18. // 测试弱引用会被 GC 忽略,直接回收
  19. setTimeout(() => {
  20. console.log(info.deref()?.name) // 回收对象后通过 info 访问属性,可选链方式
  21. console.log(info.deref() && info.deref().name) // 传统逻辑与方式
  22. }, 10000)

logical assignment operators 逻辑赋值运算符

  1. // 1.||= 逻辑或赋值运算
  2. let message = "hello world"
  3. message = message || "default value"
  4. message ||= "default value"
  5. console.log(message)
  6. // 2.&&= 逻辑与赋值运算
  7. // &&
  8. const obj = {
  9. name: "why",
  10. foo: function() {
  11. console.log("foo函数被调用")
  12. }
  13. }
  14. // 一般情况下会这样使用逻辑与
  15. obj.foo && obj.foo()
  16. // &&=
  17. let info = {
  18. name: "why"
  19. }
  20. // 1.判断info
  21. // 2.有值的情况下, 取出info.name
  22. info = info && info.name
  23. info &&= info.name
  24. console.log(info)
  25. // 3.??= 逻辑空赋值运算,和 ||= 的区别就是识别 0 和 空
  26. let message = 0
  27. message ??= "default value"
  28. console.log(message)

Numeric Separator

大数字可以用_隔开,不影响识别

String.replaceAll:字符串替换

replace()方法的加强版,replaceAll()可以替换匹配到的所有字符串。

replace() 方法返回一个由替换值(replacement)替换一些或所有匹配的模式(pattern)后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。

原字符串不会改变

  1. const p = 'dog, dog, dog, hhh';
  2. console.log(p.replace('dog', 'monkey')); // monkey, dog, dog, hhh
  3. console.log(p); // dog, dog, dog, hhh
  4. console.log(p.replaceAll('dog', 123)); // 123, 123, 123, hhh
  5. console.log(p); // dog, dog, dog, hhh