ES2016~ES2021

116 阅读7分钟

ES2016~ES2021新特性

ES2016(ES7)

Array.prototype.includes()

includes方法用来判断一个数组是否包含一个指定的值,如果包含则返回true,否则返回false

语法

/**
* valueToFind 需要查找的元素值
* fromIndex 可选 从fromIndex 索引处开始查找 valueToFind。如果为负值(从末尾开始向前跳 fromIndex的绝对值个索引,然后往后搜索),默认值为0
* */
arr.includes([valueToFind, fromIndex])

示例

const arr = ['es6', 'es7', 'es8']
console.log(arr.includes('es7'))  //true
console.log(arr.includes('es7', 1)) //true
console.log(arr.iincludes('es7', 2)) //false
console.log(arr.includes('es7', -1)) //false
console.log(arr.includes('es7', -2)) // true

注意点

使用includes()查找字符串区分大小写

const array = ["es6", 'es7', 'es8', 'a'];
console.log(array.includes('A')) // false

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

const arr = ['es6', ['es7', 'es8'], 'es9', {name: 'shiyzhang'}]

console.log(arr.includes(['es7', 'es8'])) //false
console.log(arr.includes({name: 'shiyzhang'}))//false

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

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

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

幂运算符 **

比如我们想秋2的10次方

自己写函数实现

function pow(x, y) {
    let result = 1
    for (let init = 0; i < y; i++) {
        result *= x
    }
    return result
}

console.log(pow(2, 10)) //1024

Math.pow()

console.log(Math.pow(2, 10)) //1024

幂运算符**

console.log(2 ** 10)

基本求幂

2 ** 3 //8
3 ** 2 //9
3 ** 2.5 // 15.588457268119896
10 ** -1 //0.1
NaN ** 2 //NaN

注意 幂运算符的两个*号之间不能出现空格,否则语法会报错

ES2017(ES8)

object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值

const obj = {
    name: "shiyzhang",
    age: 18,
    height: 175
}
console.log(Object.values(obj)) //['shiyzhang',18,175]

Object.getOwnPropertyDescriptors()

Object,getOwnPropertyDescriptors()方法用来获取一个对象的所有自身属性的描述符

const obj = {
    name: 'shiyzhang',
    age: 18
}
const desc = Object.getOwnPropertyDescriptors(obj)

console.log(desc) //打印结果
// {
//     name:{
//         value:'shiyzhang',
//         writable:true,
//         wnumerable:true,
//         configurable:true
//     },
//     age:{
//         value:18,
//         writable:true,
//         wnumerable:true,
//         configurable:true
//     }
// }

上面打印结果中

  • value表示当前对象的默认值
  • writable表示对象属性是否可修改
  • enumerable表示当前这个属性是否可以出现在对象的枚举属性中
  • configurable表示当前对象的属性能否用delete删除

那这些对象的属性我们怎么设置和修改他们呢,我们可以使用es5的Object.defineProperty()

const obj = {}
Object.defineProperty(obj, 'shiyzhang', {
    value: 'shiyzhang',
    writable: true,
    configurable: true,
    enumerable: true
});
Object.defineProperty(obj, 'age', {
    value: 27,
    writable: true,
    configurable: true,
    enumerable: true,
})
console.log(obj) //{name:'shiyzhang,age,27}

接下来我们演示一下,一些属性设置为false的情况

const obj = {}
Object.defineProperty(obj, 'name', {
    value: 'shiyzhang',
    dwritable: false,
    cinfigurable: false,
    enumerable: true
})
console.log(obj) //{name:'shiyzhang'}
obj.name = 'chinmy';
console.log(obj)// {name:'shiyzhang'}
delete obj.name
console.log(obj)//{name:'shiyzhang}

可以看到设置 writable:false和configurable:false,为false时,对象的name对象的值不能改变和不能被删除,打印出来的还是原来的对象

设置enumerable为false时

const obj = {}

Object.defineProperty(obj, 'name', {
    value: 'shiyzhang',
    writable: true,
    configable: true,
    enumerable: false
})
console.log(obj)//{}
for (let key in obj) {
    console.log(key)//''
}

当设置enumerable:false时,表示对象的属性不可被枚举,这时打印对象为空,遍历对象的键也为空。

String.prototype.padStart

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

语法

str.padStart(targetLength,padString)

  • targetLength

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

  • padString 可选

填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数默认值为 ""

示列

'abc'.padStart(10) //'       abc'
'abc'.padStart(10, 'foo') //'foofoofabc'
'abc'.padStart(6, '12345')//'123abc'
'abc'.padStart(8, '0')//'00000abc'
'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}`)

数字替换(手机号,银行卡号等)


const tel = '18338636666'
const newTel = tel.slice(-4).padStart(tel.length, '*')
console.log(newTel) // *******6666

String.prototype.padEnd

把指定字符串填充到字符串尾部,返回新字符串。

语法同上

示例

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

应用场景

在js前端我们处理时间戳的时候单位是ms毫秒,但是,后端同学返回的时间戳则不一样是毫秒,可能只有10位,以S秒为单位。所以我们在前端处理这个时间戳的时候,保险起见,要先做一个13位的补全,保证单位是毫秒。

console.log(new Date().getTime()) //13位的时间戳
timestamp = +String(timestamp).padEnd(13, '0')

尾逗号 Trailing commas

ES8 允许函数的最后一个参数有尾逗号(Trailing commas)。此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。

function clownsEverywhere(
    param1,
    param2
) {
    /* ... */
}

clownsEverywhere(
    'foo',
    'bar'
)

上面代码中,如果param2或bar后面加一个逗号,就会报错。

如果像上面这样,将参数写成多行(即每个参数占据一行), 以后修改代码的时候,想为函数clownsEverywhere添加第三参数, 或者调整参数的次序,就势必要在原来最后一份参数后面添加一个逗号。 这对于版本管理系统来说,就会显示添加逗号的那一行也发生了变动。 这看上去有点冗余,因此新的语法允许定义和调用时,尾部直接可以加上一个逗号。

function clownsEverywhere(
    param1,
    param2,
) {
    /* ... */
}

clownsEverywhere(
    'foo',
    'bar',
)

这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。

async/await

介绍

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

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

function foo() {
    return 'jimmy'
}

console.log(f00()) // jimmy

添加async后

async function foo() {
    return 'jimmy' //Promise.resolve('jimmy')
}

console.log(foo()) //Promise

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

function timeout() {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(1)
            resolve()
        }, 1000)
    })
}

//不加aasync和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(onerror => {
        console.log(error)
    })

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

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

foo()

通过上面代码,你会发现整个异步处理的逻辑都是使用同步代码的方式来实现的, 而且还支持try catch来捕获异常,这感觉就在写同步代码,所以是非常符合人的线性思维

注意点

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