这些ES7-ES12的知识点你都掌握了嘛?

338 阅读9分钟

大家在工作中ES6语法都已经用的炉火纯青了,那ES7-ES12的新特性你现在都用上了嘛?很多的新特性在开发中还是很实用的,也解决了很多js存在的问题。了解真相,你才能获得真正的自由!

Array.prototype.includes()

includes()方法用来判断一个数组是否包含一个指定的值,包含则返回true,否则返回false arr.includes(value,formIndex) value:需要查找的元素值 formIndex可选属性,从formIndex 索引处开始查找value,如果为负值,则从末尾往前搜索,默认为0

const arr = ['es6','es7','es8']
arr.includes('es6')    // true
arr.includes('es7',1)  // true

注意:includes() 查找字符串是区分大小写的。

const arr = ['es6','es7','es8','A']
arr.includes("a")    // false

使用includes() 只能判断简单类型的数据,对于复杂类型的数据,比如对象类型的数组,二位数组,对象这些是无法判断的。

能识别NaN。indexOf是不能识别NaN的

const arr = ['es6','es7','es8',NaN]
arr.indexOf(NaN)    // -1
arr.includes(NaN)   // true

如果只想知道某个值是否在数组中存在,而并不关心它的索引位置,建议使用includes(),如果想获取一个值在数组中的位置,那么使用indexOf方法。

String.prototype.padStart

指定字符串填充到字符串头部,返回新的字符串

str.padStart(targetLength,[,padString]

targetLength 当前字符串需要填充到的目标长度,如果这个数值小于当前字符串的长度,则返回当前字符串本省。

padString 可选,填充字符串,如果字符串太长,是填充后的字符串长度超出目标长度,则只保留最左侧的部分,其他部分会被截断,次参数默认是空字符串。

示例
'abc'.padString(10)            // '       abc'
'abc'.padStart(10,'foo')      // 'foofoofabc'
'abc'.padStart(6,"123456")   // '123abc'
'abc'.padStart(1);          // "abc"

应用场景

  • 日期格式化:yyyy-mm-dd:
const now = new Date()\
const year = now.getFullYear()\
// 月份和日期 如果是一位前面给它填充一个0\
const month = (now.getMonth() + 1).toString().padStart(2'0')\
const day = (now.getDate()).toString().padStart(2'0')\
console.log(year, month, day)
console.log`${year}-${month}-${day}` ) //输入今天的日期 2022-02-20
  • 数字替换(手机号,银行卡号)
const tel = "18763985432"
const newTel = tel.slice(-4).padString(tel.length,"*")    // ********5432

String.prototype.padEnd

指定字符串填充到字符串的尾部返回新的字符串

'abc'.padEnd(10);          // "abc       "\
'abc'.padEnd(10"foo");   // "abcfoofoof"\
'abc'.padEnd(6"123456"); // "abc123"\
'abc'.padEnd(1);

async/await

我们都知道使用Promise 能很好的解决回调地狱函数的问题。但如果处理的流程比较复杂的话,那么整段代码将充斥着then,语义化不明显,代码不能很好的表示执行流程。那有没有比Promise更优雅的异步方式呢?那就是async/await

前面添加了async的函数在执行后都会自动返回一个Promise对象

async function foo(){
    return "jimmy"
}
console.log(foo())     // Promise

async 函数中使用await,那么await 这里的代码就变成了同步的,意思是说只有等await后面的Promise执行完成后得到结果才会继续下去,await 就是等待。

function timeout(){
    return new Promise(resolve=>{
        setTimeout(()=>{
            console.log(1)
            resolve()
        }
    }
}
// 不加async 和 await 是2,1   加了之后是1,2
async function foo(){
    await timeout()
    console.log(2)
}
foo();

使用场景

假如有这样一个场景:需要先请求a链接,等信息返回后,在请求b链接的另外一个资源。下面的代码展示的是用fetch来实现。fetch被定义在window对象中,它返回的是一个Promise对象。

fetch("https://blog.csdn.net/").then(response=>{
    console.log(response)
    return fetch('https://juejin.im/')
}.then(response=>{
     console.log(response)})
.catch(error=>{
    console.log(error)
})

虽然上述的代码可以实现这个需求,但语意化不明显,不能很好的表示执行流程。基于这个原因,Es8引入了async/await,这是js异步编程的一个重大改进。提供了在不阻塞主线程情况下使用同步代码实现了异步访问资源的能力,使得代码逻辑更加清晰。

async function foo(){
    try{
        let response1 = await fetch('https://blog.csdn.net/')
        console.log(response1)
        let response2 = await fetch('https://blog2.csdn.net/')
        console.log(response2)
    }catch(err){
        console.log(error)
    }
}
foo()

你会发现整个异步的处理逻辑都是使用同步代码的方式来实现的。而且还支持try catch来捕获异常,这就感觉在写同步代码。

注意⚠️

  • await 只能在async标记的函数内部使用。
  • await 后面需要跟异步操作,不然就没有意义。而且await后面的Promise对象不必写then,因为await的作用之一就是获取后面Promise对象成功状态下传递出来的参数。

缺陷:await关键字 会阻塞后面代码的执行,直到Promise完成,

for await of

异步迭代器,循环等待每个Promise对象变成resolved状态才进入下一步 我们都知道for...of 是同步运行的。

function TimeOut(time){
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(time)
        }, time)
    })
}
async function test() {
    let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
    for (let item of arr) {  
     console.log(Date.now(),item.then(console.log))
    }
}
test()

上面的打印结果如下

上面代码证实了for of方法不能遍历异步迭代器,得到的结果并不是我们所期待的,于是for await of就粉墨登场了。

function TimeOut(time) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(time)
        }, time)
    })
}
async function test() {
    let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
    for await (let item of arr) {
        console.log(Date.now(), item)
    }
}
test()
// 1560092345730 2000
// 1560092345730 1000
// 1560092346336 3000

for await of 等待每个Promise对象变成resolved状态才进入下一步。

Promise.prototype.finally()

返回一个promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then和catch后都会执行finally指定的会调函数。避免了同样的语句在then和catch中各写一次。

Object.formEntries()

把键值对列表转换成一个对象,它和Object.entries()相对的。

Object.formEntries([
    ['foo',2],
    ['bar',1]
])
// {foo:2,bar:1}
  • case 1
    const obj = {name:'jimmy',age:18}
    const entries = Object.entries(obj)
    console.log(entries)  // [['name', 'jimmy'],['age', 18]]
    
    // Es10
    const formEntries = Object.formEntries(entries)
    // {name:'jimmy',age:18}
  • case 2 Map转Object
const map = new Map()
map.set('name''jimmy')
map.set('age'18)
console.log(map) // {'name' => 'jimmy', 'age' => 18}

const obj = Object.fromEntries(map)
console.log(obj)
// {name: "jimmy", age: 18}
  • case 3 过滤
const course = {
    math80,
    english85,
    chinese90
}
const res = Object.entries(course).filter(([key, val]) => val > 80)
console.log(res) // [ [ 'english', 85 ], [ 'chinese', 90 ] ]
console.log(Object.fromEntries(res)) // { english: 85, chinese: 90 }
  • case 4 url的search参数转换
const queryString = "?name=jimmy&age=18&height=1.88";
const queryParams = new URLSearchParams(queryString);
const paramObj = Object.fromEntries(queryParams);
console.log(paramObj); // { name: 'jimmy', age: '18', height: '1.88' }

Array.prototype.flat()

arr.flat(depth) depth 可选,指定要提取嵌套数组的结构深度,默认值为1。 flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素和遍历到的子数组中的元素合并成一个新的数组返回。

//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [12, [34, [56, [78, [910]]]]];
arr4.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// `flat()` 方法会移除数组中的空项:
var arr5 = [12, , 45];
arr5.flat(); // [1, 2, 4, 5]

String.prototype.trimStart()

从字符串的开头删除空格

String.prototype.trimEnd()

从字符串的结尾删除空格

修订 Function.prototype.toString()

以前函数的toString() 方法来自于 Object.prototype.toString(),现在返回一个表示当前函数源代码的字符串,以前只会返回这个函数,不包含注释,空格等信息。

function foo(){
    // 哈哈哈,我是注释
    console.log("我来了")
}
console.log(foo.toString())
// function foo() {
   // 哈哈哈,我是注释
  // console.log("我来了");
//}

空值合并运算符

??是一个逻辑操作符,当左侧的操作数为null或者undefined时,返回其右侧的值,否则返回左侧的值。他与使用|| 逻辑或不同,|| 为某些变量设置默认值。遇到假值(例如:"",false,0,NaN)会发生意料之外的事。

const foo = undefined ?? "foo"    // foo
const foo = null ?? "foo"    // foo
const foo = "" ?? "default"  // ""
const foo = "" || "default"  // default

const foo = 0 ?? "default"  // 0
const foo = 0 || "default"  // default

const foo = NaN ?? "default"  // NaN
const foo = NaN || "default"  // default

注意: 将 ?? 直接与 && 和 || 组合使用时是不可取的。必须要加上括号

null || undefined ?? "foo";  // 抛出 SyntaxError
(null || undefined) ?? "foo";   // foo

promise 方法和应用

  1. Promise.all() 将多个Promise实例,包装成一个新的Promise实例。它的参数是一个包含多个promise实例的数组。 返回值要分为两种情况。
  • 数组内的promise实例全部成功,就是状态都变为resolve,这样它的值也是一个数组,这个数组里面的值对应原来promise实例的resolve状态的值。可以调用then方法获取。
  • 数组内的所有内部的promise实例没有成功,这样她的状态就是reject。
let p1 = new Promise(function(resolve, reject) {  
    setTimeout(function() {        
        resolve(1)  
    }, 1000) }) 
let p2 = new Promise(function(resolve, reject) {    
    setTimeout(function() {       
        resolve(2)   
    }, 2000) }) 
let p3 = new Promise(function(resolve, reject) {    
    setTimeout(function() {        
        resolve(3)   
    }, 3000) }) 
Promise.all([p3, p1, p2]).then(res => {   
    console.log(res) // [3, 1, 2] 
})
  1. Promise.all如果是不全成功,它会返回第一个reject状态的Promise实例

下面我们看下promise.all 对应的源码

 function myPromiseall(promise){
    return new Promise(function(resolve,reject){
      if(!Array.isArray(promise)){
        throw new TypeError("argument must be a array")
      }
      let count = 0;
      let length = promise.length;
      let result = [];
      for(let i = 0;i<length;i++){
        Promise.resolve(promise[i].then(ret=>{
          count +=1;
          result[i]=ret;
          if(count === length){
            resolve(result)
          }
        },err=>{
          return reject(err)
        }))
      }
    })
  } 
  1. Promise.race() race就是竞赛赛跑的意思,竞赛肯定是最受关注的第一名那个,其他都无所谓了。
const p = Promise.race([p1, p2, p3]);

p1,p2,p3 之中有一个实例率先完成,P的状态就跟着改变。那个率先改变的promise实例的返回值。就传递给p的回调函数。 对应的源码

Promise.race = function(args) {   
    return new Promise((resolve, reject) => {        
       for (let i = 0, len = args.length; i < len; i++) { 
     args[i].then(resolve, reject)       
     }  
   }) 
}
  1. Promise.allSettled()
  • promise.all 适用于一组异步操作全部成功后调用,或者这组操作中的一个失败后调用。
  • promise.race 适用于一组异步操作第一个成功或者失败后调用。 但如果我们想等这组异步操作全部结束后在调用,上面的方法就不适用了。于是引出了一个新的方法来实现我们的需求。就是promise.allSettled()
  1. promise.any() 只要参数实例中有一个变成fulfilled 状态,包装的实例就会变成fulfilled状态,如果所有的参数实例都变成rejected状态,包装实例就会变成rejected状态。

promise.any 不会因为某个promise变成rejected状态而结束,必须等到所有参数promise变成rejectd状态才会结束。

  • 只要其中一个promise成功,就返回那个已经成功的promise
  • 如果对象中没有一个promise成功,及所有的promise都失败,就返回一个失败的promise和AggregateError类型的实例。它是Error的一个子类,用于把单一的错误集合在一起。 源码:
myPromiseall.any = function(promises){
    return new Promise((resolve,reject)=>{
      promise = Array.isArray(promises) ? promises : [];
      let len = promises.length;
      // 用于收集所有的error
      let errs = [];
      // 如果传入一个空数组,那么直接返回AggregateError
      if(len===0) return reject(new AggregateError("all promise were reject"))
      promises.forEach(promise => {
        promise.then(value=>{
          resolve(value)
        },err=>{
          len--
          errs.push(err);
          if(len===0){
            reject(new AggregateError(errs))
          }
        })
      });
    })
  }

总结:

  • promise.all 全成功我成功,失败一个我失败
  • promise.race 谁第一个改变状态就是谁的,无论成功或是失败
  • promise.allSettled 管你成功或是失败,全部都得运行完
  • promise.any 一个成功我成功,全部失败我失败。

状态成功返回值:

  • promise.all 返回状态成功时的数组
  • promise.race 返回最先成功的
  • prmise.allSettled 无所谓失败成功,返回值都是一个包含状态对象的数组
  • promise.any 返回第一个成功的

失败后返回值

  • promise.all 返回第一个失败的
  • promise.race 返回第一个失败的
  • prmise.allSettled 无所谓失败或成功,返回值都是一个包含状态对象的数组
  • promise.any AggregateError: All promises were rejected