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”) }
<a name="Au7VA"></a>
## 指数(乘方) exponentiation运算符
在ES7之前,计算数字的乘方需要通过 Math.pow 方法来完成。<br />在ES7中,增加了`**` 运算符,可以对数字来计算乘方。
```javascript
const result1 = Math.pow(3, 3)
// ES7: **
const result2 = 3 ** 3
console.log(result1, result2)
ES8
Object.values( )
之前我们可以通过Object.keys()
获取一个对象所有的 key
在 ES8 中提供了Object.values()
来获取所有的 value 值
const obj = {
name: "why",
age: 18
}
// 以数组的形式返回
console.log(Object.keys(obj)) // [ 'name', 'age' ]
console.log(Object.values(obj)) // [ 'why', 18 ]
// 也可以接收参数,用的非常少
// 传数组就返回数组,传字符串就会按字符展开返回新数组
console.log(Object.values(["abc", "cba", "nba"])) // [ 'abc', 'cba', 'nba' ]
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’ ] ]
<a name="T6wjB"></a>
## 字符串前后填充 String Padding
某些字符串我们需要对其进行前后的填充,来实现某种格式化效果,ES8中增加了`padStart()` 和 `padEnd()` 方法,分别是对字符串的首尾进行填充的。
```javascript
const message = 'Hello World'
// 参数一:要填充到的长度,也就是填充完后字符串的长度,如果小于现有长度,则返回字符串本身
// 参数二:可选,填充的内容,默认为空格
const newMessage = message.padStart(15, '*').padEnd(20, '-')
console.log(newMessage) // ****Hello World-----
// 案例:隐藏身份证号的前几位
const cardNumber = '123456200020206789'
// 截取后4位
const lastFourCard = cardNumber.slice(-4)
// 用*填充截取的4位到身份证号长度
const finalCard = lastFourCard.padStart(cardNumber.length - 4, '*')
console.log(finalCard) // **********6789
Trailing Commas
在ES8中,我们允许在函数定义和调用时多加一个逗号,只是为了照顾部分转语言的开发者使用习惯。
function foo(m, n,) { console.log(m + n) }
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()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
// 数组套数组套了三层
const nums = [10, 20, [2, 9], [[30, 40], [10, 45]], 78, [55, 88]]
const newNums = nums.flat() // 默认向下递归 1 层
console.log(newNums) // [ 10, 20, 2, 9, [ 30, 40 ], [ 10, 45 ], 78, 55, 88 ]
const newNums2 = nums.flat(2) // 递归遍历 2 层
console.log(newNums2)
// [ 10, 20, 2, 9, 30, 40, 10, 45, 78, 55, 88 ]
flatMap()
方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
注意一:flatMap 是先进行 map 操作,再做 flat 的操作;
注意二:flatMap 中的 flat 相当于深度为 1;
const nums2 = [10, 20, 30]
// flatMap()
const newNums3 = nums2.flatMap(item => {
return item * 2 // 返回值会放入新数组中,这个回调函数是map的操作
})
// map()
const newNums4 = nums2.map(item => {
return item * 2
})
// 两个结果是一样的
console.log(newNums3) // [ 20, 40, 60 ]
console.log(newNums4) // [ 20, 40, 60 ]
那flatMap()
有啥应用场景:比如对数组元素内容进行分割。
// 希望将数组元素内容以空格分隔
const messages = ['Hello World', 'hello lyh', 'my name is zs']
// 以前做法,两次循环很不方便
const word = new Array()
for (let i = 0; i < messages.length; i++) {
const tempArr = messages[i].split(' ')
for (let i = 0; i < tempArr.length; i++) {
word.push(tempArr[i])
}
}
// flatMap
const word2 = messages.flatMap(item => {
// map 会让数组变成 [ ['Hello','World'], [], [] ] 这样的套娃结构,然后 flat 刚好降维了
return item.split(' ')
})
console.log(word) // ['Hello', 'World','hello','lyh','my','name','is','zs']
console.log(word2) // ['Hello', 'World','hello','lyh','my','name','is','zs']
Object.fromEntries( )
ES8 我们可以通过 Object.entries
将一个对象转换成 entries,那么如果我们有一个entries了,就可以利用 Object.fromEntries()
将它转换成对象,相当于一个Object.entries()
的逆运算。
const obj = {
name: "why",
age: 18
}
console.log(Object.entries(obj)) // [ [ 'name', 'why' ], [ 'age', 18 ] ]
console.log(Object.fromEntries(Object.entries(obj))); // { name: 'why', age: 18 }
const queryString = 'name=why&age=18&height=1.88' // 网络请求中的查询字符串
const queryParams = new URLSearchParams(queryString) // API
console.log(queryParams) // URLSearchParams { 'name' => 'why', 'age' => '18', 'height' => '1.88' }
// 返回的对象可以遍历,并且是数组形式
for (const param of queryParams) {
console.log(param) // [ 'name', 'why' ] [ 'age', '18' ] [ 'height', '1.88' ]
}
// fromEntries() 虽然接收 entries,但是可迭代对象也可以接收,所以将查询字符串传入,可直接得到普通对象
const paramObj = Object.fromEntries(queryParams)
console.log(paramObj) // { name: 'why', age: '18', height: '1.88' }
trimStart trimEnd
去除一个字符串首尾的空格,我们可以通过trim()
方法,单独去除前后则可用trimStart()``trimEnd()
const message = " Hello World "
console.log(message.trim())
console.log(message.trimStart())
console.log(message.trimEnd())
//Hello World
//Hello World
// Hello World
Symbol description
Optional catch binding(可选的捕获绑定)
待定
ES11
BigInt 大数
在早期的JavaScript中,我们不能正确的表示过大的数字。大于MAX_SAFE_INTEGER
的数值,表示的可能是不正确的。
那么ES11中,引入了新的数据类型BigInt
,用于表示大的整数:BitInt
的表示方法是在数值的后面加上n
// ES11之前 max_safe_integer 表示 js 支持的最大安全数字
const maxInt = Number.MAX_SAFE_INTEGER
console.log(maxInt) // 9007199254740991
// 最大值加1加2,都是一样的,可见超过最大值计算开始不安全
console.log(maxInt + 1) // 9007199254740992
console.log(maxInt + 2) // 9007199254740992
// ES11之后新的数据类型:BigInt
const bigInt = 900719925474099100n
// 报错,因为 10 是Number类型,不同类型的数据间不能直接计算
// 为什么 10+1.2 整型和浮点型计算可以?因为它们进行了类型隐式转换,而 BigInt 类型不会自动转换
// console.log(bigInt + 10)
// 手动类型转换,成功计算
console.log(bigInt + 10n)
// Number 和 BigInt 互相转换
const num = 100
console.log(bigInt + BigInt(num))
const smallNum = Number(bigInt)
console.log(smallNum)
Nullish Coalescing Operator 空值合并操作符
之前变量默认值,我们会使用||
或操作。但是或操作存在一个隐患,它为认为 空、0、null、undefined 都为false,当传入的值刚好为空和 0 的时候,或运算符会刚好舍去空和 0。
ES11,Nullish Coalescing Operator 增加了空值合并操作符:??
,它只会识别 null、undefined 为 false。
// || 或运算存在隐患
let foo = ''
let test = foo || '默认值'
console.log(test) // 默认值
let foo1 = 0
let test1 = foo1 || '默认值'
console.log(test1) // 默认值
// ?? 空值合并运算符
let fn = ''
let t1 = fn ?? 'defalut value'
console.log(t1) //"空"
let fn1 = 0
let t2 = fn1 ?? 'defalut value'
console.log(t2) // 0
Optional Chaining 可选链
可选链?.
,主要作用是让我们的代码在进行 null 和 undefined 判断时更加清晰和简洁:
// info 是一个隐藏的对象
const info = {
name: "why",
// friend: {
// girlFriend: {
// name: "hmm"
// }
// }
}
// 我们预期 info 对象有 friend 属性,但是某种原因没了
// 那就会立即因 undefined.girlFriend 导致报错,终止后面代码的执行
console.log(info.friend.girlFriend.name)
// 之前会用 &&与运算 和 if 先判断属性是否存在,但是这样太烦了
if (info && info.friend && info.friend.girlFriend) {
console.log(info.friend.girlFriend.name)
}
// ES11 提供了可选链(Optional Chainling)
// 以 ?. 的方式调用,若中间某个属性不存在,则会立即停止调用,并返回 undefined,不影响后续代码
console.log(info.friend?.girlFriend?.name) // undefined
console.log('其他的代码逻辑')
Global This
在之前我们希望获取 JavaScript 环境的全局对象,不同的环境获取的方式是不一样的:
- 比如在浏览器中可以通过
this
、window
来获取; - 比如在Node中我们需要通过
global
来获取;
我们写的代码不知道会在什么环境执行,所以会进行兼容处理
// 伪代码兼容性处理
let myGO = undefined
// 对当前执行环境判断,获取全局对象
if (window !== undefined) {
// 浏览器环境
myGO = window
} else {
// node 环境
myGO = global
}
那么在ES11中对获取全局对象进行了统一的规范:globalThis
无论什么环境下都指向当前环境的全局对象
console.log(globalThis);
for…in
在ES11之前,虽然很多浏览器支持for…in来遍历对象类型,但是并没有被ECMA标准化,有些浏览器的for…in 是遍历对象的 value,有些是遍历 key。
在ES11中,对其进行了标准化,for…in 用来遍历对象的 key 。
const obj = {
name: 'zs',
age: 19
}
for (const item in obj) {
console.log(item); // name, age
}
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
// 一会后就会执行回调函数
<a name="dwtAB"></a>
## WeakRefs 弱引用
如果我们默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用,如果我们希望是一个弱引用的话,之前需要使用 WeakSet、WeakMap,现在可以使用`WeakRef`类;
WeakRef 创建的弱引用,需要`deref()`方法才能访问指向的内存地址。
```javascript
// ES12: WeakRef类
// WeakRef.prototype.deref:
// > 如果原对象没有销毁, 那么可以获取到原对象
// > 如果原对象已经销毁, 那么获取到的是undefined
const finalRegistry = new FinalizationRegistry((value) => {
console.log("注册在finalRegistry的对象, 某一个被销毁", value)
})
let obj = { name: "why" }
// 建立弱引用 info 指向 {name: 'why'}
let info = new WeakRef(obj)
finalRegistry.register(obj, "obj")
obj = null
// 测试弱引用会被 GC 忽略,直接回收
setTimeout(() => {
console.log(info.deref()?.name) // 回收对象后通过 info 访问属性,可选链方式
console.log(info.deref() && info.deref().name) // 传统逻辑与方式
}, 10000)
logical assignment operators 逻辑赋值运算符
// 1.||= 逻辑或赋值运算
let message = "hello world"
message = message || "default value"
message ||= "default value"
console.log(message)
// 2.&&= 逻辑与赋值运算
// &&
const obj = {
name: "why",
foo: function() {
console.log("foo函数被调用")
}
}
// 一般情况下会这样使用逻辑与
obj.foo && obj.foo()
// &&=
let info = {
name: "why"
}
// 1.判断info
// 2.有值的情况下, 取出info.name
info = info && info.name
info &&= info.name
console.log(info)
// 3.??= 逻辑空赋值运算,和 ||= 的区别就是识别 0 和 空
let message = 0
message ??= "default value"
console.log(message)
Numeric Separator
String.replaceAll:字符串替换
replace()
方法的加强版,replaceAll()
可以替换匹配到的所有字符串。
replace()
方法返回一个由替换值(replacement)替换一些或所有匹配的模式(pattern)后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。
原字符串不会改变
const p = 'dog, dog, dog, hhh';
console.log(p.replace('dog', 'monkey')); // monkey, dog, dog, hhh
console.log(p); // dog, dog, dog, hhh
console.log(p.replaceAll('dog', 123)); // 123, 123, 123, hhh
console.log(p); // dog, dog, dog, hhh