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中,增加了`**` 运算符,可以对数字来计算乘方。```javascriptconst result1 = Math.pow(3, 3)// ES7: **const result2 = 3 ** 3console.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()` 方法,分别是对字符串的首尾进行填充的。```javascriptconst 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])}}// flatMapconst 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) // APIconsole.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_INTEGERconsole.log(maxInt) // 9007199254740991// 最大值加1加2,都是一样的,可见超过最大值计算开始不安全console.log(maxInt + 1) // 9007199254740992console.log(maxInt + 2) // 9007199254740992// ES11之后新的数据类型:BigIntconst bigInt = 900719925474099100n// 报错,因为 10 是Number类型,不同类型的数据间不能直接计算// 为什么 10+1.2 整型和浮点型计算可以?因为它们进行了类型隐式转换,而 BigInt 类型不会自动转换// console.log(bigInt + 10)// 手动类型转换,成功计算console.log(bigInt + 10n)// Number 和 BigInt 互相转换const num = 100console.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 = 0let test1 = foo1 || '默认值'console.log(test1) // 默认值// ?? 空值合并运算符let fn = ''let t1 = fn ?? 'defalut value'console.log(t1) //"空"let fn1 = 0let 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) // undefinedconsole.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:// > 如果原对象没有销毁, 那么可以获取到原对象// > 如果原对象已经销毁, 那么获取到的是undefinedconst 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.nameinfo = info && info.nameinfo &&= info.nameconsole.log(info)// 3.??= 逻辑空赋值运算,和 ||= 的区别就是识别 0 和 空let message = 0message ??= "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, hhhconsole.log(p); // dog, dog, dog, hhhconsole.log(p.replaceAll('dog', 123)); // 123, 123, 123, hhhconsole.log(p); // dog, dog, dog, hhh
