13 ES7-ES12 知识点解析
在 ES7 之前,如果我们想判断一个数组中是否包含某个元素,需要通过 indexOf 获取结果,并且判断是否为 -1。在 ES7 中,我们可以通过 includes 来判断一个数组中是否包含一个指定的元素,根据情况,如果包含则返回 true, 否则返回 false。
ES7 - Array Includes
const names = ['abc', 'cba', 'nba', 'mba', NaN]
if (names.indexOf('cba') !== -1) {
console.log('包含abc元素')
}
// ES7 ES2016
// 指定查找的起始位置
if (names.includes('cba', 2)) {
// false
console.log('包含abc元素')
}
// indexOf 会忽略 NaN
if (names.indexOf(NaN) !== -1) {
// false
console.log('包含NaN')
}
// includes 可以检测到 NaN
if (names.includes(NaN)) {
console.log('包含NaN')
}
ES7 –指数(乘方) exponentiation 运算符
在 ES7 之前,计算数字的乘方需要通过 Math.pow 方法来完成。在 ES7 中,增加了 **
运算符,可以对数字来计算乘方。
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))
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']
ES8 Object entries
通过 Object.entries 可以获取到一个数组,数组中会存放可枚举属性的键值对数组。
const obj = {
name: 'why',
age: 18,
}
console.log(Object.entries(obj)) // [["name","why"],["age",18]]
const objEntries = Object.entries(obj)
objEntries.forEach((item) => {
console.log(item[0], item[1])
})
console.log(Object.entries(['abc', 'cba', 'nba'])) // [["0","abc"],["1","cba"],["2","nba"]]
console.log(Object.entries('abc')) // [["0","a"],["1","b"],["2","c"]]
ES8 - String Padding
某些字符串我们需要对其进行前后的填充,来实现某种格式化效果,ES8 中增加了 padStart 和 padEnd 方法,分 别是对字符串的首尾进行填充的。
第一个参数是,填充后,字符串的长度,如果该参数小于等于字符串的长度,则填充不起作用
const message = 'Hello World'
const newMessage = message.padStart(15, '*').padEnd(20, '-')
console.log(newMessage)
我们简单具一个应用场景:比如需要对身份证、银行卡的前面位数进行隐藏:
// 案例
const cardNumber = '321324234242342342341312'
const lastFourCard = cardNumber.slice(-4)
const finalCard = lastFourCard.padStart(cardNumber.length, '*')
console.log(finalCard) // ********************1312
ES8 - Trailing Commas
在 ES8 中,我们允许在函数定义和调用时多加一个逗号:
function foo(m, n,) {};
foo(20, 30,);
ES8 - Object Descriptors
ES8 中增加了另一个对对象的操作是 Object.getOwnPropertyDescriptors ,这个在之前已经讲过了,这里不再重 复
ES9 新增知识点
- Async iterators:后续迭代器讲解
- Object spread operators:前面讲过了
- Promise finally:后续讲 Promise 讲解
ES10 - flat flatMap
- flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返 回。
const nums = [
10,
20,
[2, 9],
[
[30, 40],
[10, 45],
],
78,
[55, 88],
]
const newNums = nums.flat()
console.log(newNums) // [10,20,2,9,[30,40],[10,45],78,55,88]
const newNums2 = nums.flat(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]
const newNums3 = nums2.flatMap((item) => {
return item * 2
})
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 coderwhy']
const words = messages.flatMap((item) => {
return item.split(' ')
})
console.log(words) // ['Hello', 'World', 'hello', 'lyh', 'my', 'name', 'is', 'coderwhy']
ES10 - Object fromEntries
在前面,我们可以通过 Object.entries 将一个对象转换成 entries,那么如果我们有一个 entries 了,如何将其转换成对象呢?
const obj = {
name: 'why',
age: 18,
height: 1.88,
}
const entries = Object.entries(obj)
console.log(entries)
const newObj = {}
for (const entry of entries) {
newObj[entry[0]] = entry[1]
}
ES10 提供了 Object.formEntries 来完成转换:
const newObj = Object.fromEntries(entries)
console.log(newObj)
那么这个方法有什么应用场景呢?
const queryString = 'name=why&age=18&height=1.88'
const queryParams = new URLSearchParams(queryString)
for (const param of queryParams) {
console.log(param)
}
const paramObj = Object.fromEntries(queryParams)
console.log(paramObj) // {name: 'why', age: '18', height: '1.88'}
ES10 - trimStart trimEnd
去除一个字符串首尾的空格,我们可以通过 trim 方法,如果单独去除前面或者后面呢? ES10 中给我们提供了 trimStart 和 trimEnd;
const message = ' Hello World '
console.log(message.trim())
console.log(message.trimStart())
console.log(message.trimEnd())
ES10 其他知识点
- Symbol description:已经讲过了
- Optional catch binding:后面讲解 try cach 讲解
ES11 - BigInt
在早期的 JavaScript 中,我们不能正确的表示过大的数字:大于 MAX_SAFE_INTEGER 的数值,表示的可能是不正确的。
// ES11之前 max_safe_integer
const maxInt = Number.MAX_SAFE_INTEGER
console.log(maxInt) // 9007199254740991
console.log(maxInt + 1) // 9007199254740992
console.log(maxInt + 2) // 9007199254740992
那么 ES11 中,引入了新的数据类型 BigInt,用于表示大的整数:BitInt 的表示方法是在数值的后面加上 n
const bigInt = 900719925474099100n
console.log(bigInt + 10n) // 900719925474099110n
// Number 转 BigInt
const num = 100
console.log(bigInt + BigInt(num)) // 900719925474099200n
// BigInt 转 Number
const smallNum = Number(bigInt)
console.log(smallNum) // 900719925474099100
ES11 - Nullish Coalescing Operator
ES11,Nullish Coalescing Operator 增加了空值合并操作符:
const foo = undefined
// const bar = foo || "default value";
const bar = foo ?? 'defualt value'
console.log(bar) // default value
ES11 - Optional Chaining
可选链也是 ES11 中新增一个特性,主要作用是让我们的代码在进行 null 和 undefined 判断时更加清晰和简洁:
const info = {
name: 'why',
// friend: {
// girlFriend: {
// name: "hmm"
// }
// }
}
if (info && info.friend && info.friend.girlFriend) {
console.log(info.friend.girlFriend.name)
}
// ES11提供了可选链(Optional Chainling)
console.log(info.friend?.girlFriend?.name) // undefined
ES11 - Global This
在之前我们希望获取 JavaScript 环境的全局对象,不同的环境获取的方式是不一样的; 比如在浏览器中可以通过 this、window 来获取;比如在 Node 中我们需要通过 global 来获取;
那么在 ES11 中对获取全局对象进行了统一的规范:globalThis
// 获取某一个环境下的全局对象(Global Object)
// 在浏览器下
// console.log(window)
// console.log(this)
// 在node下
// console.log(global)
// ES11
console.log(globalThis)
ES11 - for..in 标准化
在 ES11 之前,虽然很多浏览器支持 for...in 来遍历对象类型,但是并没有被 ECMA 标准化。在 ES11 中,对其进行了标准化,for...in 是用于遍历对象的 key 的:
const obj = {
name: 'why',
age: 18,
}
for (const item in obj) {
console.log(item)
}
ES11 其他知识点
- Dynamic Import:后续 ES Module 模块化中讲解。
- Promise.allSettled:后续讲 Promise 的时候讲解。
- import meta:后续 ES Module 模块化中讲解。
ES12 - FinalizationRegistry
FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调。FinalizationRegistry 提供了这样的一种方法:当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调。(清理回调有时被称为 finalizer ); 你可以通过调用 register 方法,注册任何你想要清理回调的对象,传入该对象和所含的值;
const finalRegistry = new FinalizationRegistry((value) => {
console.log('注册在finalRegistry的对象, 某一个被销毁', value)
})
let obj = { name: 'why' }
let info = { age: 18 }
finalRegistry.register(obj, 'obj')
finalRegistry.register(info, 'value')
obj = null
info = null
// 垃圾回收时,打印如下
// 注册在finalRegistry的对象, 某一个被销毁 value
// VM1565:2 注册在finalRegistry的对象, 某一个被销毁 obj
ES12 - WeakRefs
如果我们默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用:如果我们希望是一个弱引用的话,可以使用 WeakRef;
// ES12: WeakRef类
// WeakRef.prototype.deref:
// > 如果原对象没有销毁, 那么可以获取到原对象
// > 如果原对象已经销毁, 那么获取到的是undefined
const finalRegistry = new FinalizationRegistry((value) => {
console.log('注册在finalRegistry的对象, 某一个被销毁', value)
})
let obj = { name: 'why' }
let info = new WeakRef(obj)
finalRegistry.register(obj, 'obj')
obj = null
setTimeout(() => {
console.log(info.deref()?.name)
console.log(info.deref() && info.deref().name)
}, 10000)
// 打印如下
// 注册在finalRegistry的对象, 某一个被销毁 obj
// undefined
// undefined
ES12 - logical assignment operators
// 1.||= 逻辑或赋值运算
let message = ''
// message = message || "default value";
message ||= 'default value'
console.log(message) // default value
// 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) // why
// 3.??= 逻辑空赋值运算
let message = 0
message ??= 'default value'
console.log(message) // 0
let foo = null // undefined 或者 null 时,会取默认值,其它情况取原来的值
foo ??= 'default value'
console.log(foo) // default value
ES12 其他知识点
- Numeric Separator:讲过了;
- String.replaceAll:字符串替换;
replaceAll() 方法返回一个新字符串,新字符串所有满足 pattern 的部分都已被 replacement 替换。pattern 可以是一个字符串或一个 RegExp, replacement 可以是一个字符串或一个在每次匹配被调用的函数。
const p = 'dog dog'
console.log(p.replaceAll('dog', 'monkey')) // monkey monkey
console.log(p.replace(/dog/g, 'monkey')) // monkey monkey