其他分享
首页 > 其他分享> > 17.(ECMAScript)es11完全解读

17.(ECMAScript)es11完全解读

作者:互联网

文章目录

1. 重点提炼


2. 字符串扩展

在正则表达式中经常使用match,匹配正则。


2.1 定义一个html模版的字符串。 => 需求:获取div标签中的内容。

定义一个html模版的字符串。 => 需求:获取div标签中的内容。


2.1.1 exec g实现

如何用一个正则表达式来得到所有匹配项,可以使用execg修饰符,如果正则表达式有/g标志,那么多次调用.exec()就会得到所有匹配的结果。 如果没有匹配的结果,.exec()就会返回null。在这之前会返回每个匹配的匹配对象。 这个对象包含捕获的子字符串和更多信息。

即在正则中首先会想到exec方法,即执行,可以对某个字符串进行正则匹配,并且可以结合g修饰符,进行全局匹配。

封装一个方法,其第一个参数是正则表达式,第二个参数是对应目标字符串。

const str = `
    <html>
        <body>
            <div>第一个div</div>
            <p>这是p</p>
            <div>第二个div</div>
            <span>这是span</span>
            <div>第三个div</div>
        </body>
    </html>
`
// exec g
function selectDiv(regExp, str){
    let matches = [] // 输出匹配结果数组
    while(true){
        const match = regExp.exec(str)
        // 未匹配上
        if(match == null){
            break
        }
        matches.push(match)
    }
    return matches
}
const regExp = /<div>(.*)<\/div>/g
const res = selectDiv(regExp, str)
console.log(res)

其返回的数组第0个,是整个文本匹配的结果,即未分组前的整体。从第1个开始就是子表达式匹配的结果了,即分组。

image-20201130114107715

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.12
Branch: branch02

commit description:a4.12(字符串扩展——exec使用1)

tag:a4.12


因此如果需要获取div标签里的内容,即该分组,应该获取下标为1的数组。

function selectDiv(regExp, str){
    let matches = [] // 输出匹配结果数组
    while(true){
        // console.log(regExp.lastIndex)
        const match = regExp.exec(str)
        // console.log(match)
        // 未匹配上
        if(match == null){
            break
        }
        matches.push(match[1])
    }
    return matches
}
const regExp = /<div>(.*)<\/div>/g
const res = selectDiv(regExp, str)
console.log(res)

image-20201130120531524

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.13
Branch: branch02

commit description:a4.13(字符串扩展——exec使用2)

tag:a4.13


2.1.2 /g作用

注意:如果不全局匹配 =>

const regExp = /<div>(.*)<\/div>/

如上代码会陷入死循环,因为不是全局匹配,则每一次都是从字符串的开头进行匹配,则match永远不可能是null,则陷入死循环。

实际正则表达式中存在一个lastIndex属性(初始值为0),每次执行exec的时候,实际是根据lastIndex属性值去执行(从该属性的位置开始执行,即决定开始匹配的位置)。如果正则表达式没有/g标志,那么运行一次.exec()时,不会改变lastIndex的值,导致下一次运行exec()时,匹配仍旧是从字符串0的位置开始。当正则表达式加了/g标志后,运行一次exec(),正则表达式的lastIndex就会改变,下次运行exec()就会从前一次的结果之后开始匹配。

不过如果没有使用/g的正则模式,.match 的效果和RegExp.prototype.exec()是一致的。

const str = `
    <html>
        <body>
            <div>第一个div</div>
            <p>这是p</p>
            <div>第二个div</div>
            <span>这是span</span>
            <div>第三个div</div>
        </body>
    </html>
`
// exec g
function selectDiv(regExp, str){
    let matches = [] // 输出匹配结果数组
    while(true){
        console.log(regExp.lastIndex)
        const match = regExp.exec(str)
        console.log(match)
        // 未匹配上
        if(match == null){
            break
        }
        matches.push(match[1])
    }
    return matches
}
const regExp = /<div>(.*)<\/div>/g
const res = selectDiv(regExp, str)
console.log(res)

第一次从0的位置开始找,然后匹配上了第一个div;这个时候字符是第39个,然后隔了17个字符(找到的部分),下一次则从39+17=56的位置开始找;然后依次往下开始找,一直找到头。

故如果没有全局匹配,每一次的lastIndex都是0

image-20201130121628458

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.14
Branch: branch02

commit description:a4.14(字符串扩展——lastIndex属性)

tag:a4.14


2.1.3 match实现

如果用 .match 方法结合/g 的正则模式,将会把所有的匹配打包成一个数组返回,换句话说所有的捕获被忽略。

const str = `
    <html>
        <body>
            <div>第一个div</div>
            <p>这是p</p>
            <div>第二个div</div>
            <span>这是span</span>
            <div>第三个div</div>
        </body>
    </html>
`

const regExp = /<div>(.*)<\/div>/g

// match
console.log(str.match(regExp))

match方法也可进行匹配,但是和需求则有一些的出入,需求不用div标签,仅仅是div标签中的内容。

match的作用就是找到包含的结果,而exec可以设置子表达式,即捕获组,打算match方法不行。

image-20201130122235187

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.15
Branch: branch02

commit description:a4.15(字符串扩展——match)

tag:a4.15


2.1.4 replace实现

可以使用一个技巧通过.replace() 收集捕获,我们使用一个函数来计算替换值。

const str = `
    <html>
        <body>
            <div>第一个div</div>
            <p>这是p</p>
            <div>第二个div</div>
            <span>这是span</span>
            <div>第三个div</div>
        </body>
    </html>
`

const regExp = /<div>(.*)<\/div>/g

// replace
function selectDiv(regExp, str){
    let matches = []
    str.replace(regExp, (all, first) => {
        matches.push(first)
    })
    return matches
}
const res = selectDiv(regExp, str)
console.log(res)

image-20201130122901387

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.16
Branch: branch02

commit description:a4.16(字符串扩展——replace)

tag:a4.16


replace方法的第一个参数可以是正则的规则,第二个参数是一个回调函数,或者一般都是用一个固定的字符串。

回调函数第一个参数,是完整的匹配结果,第二个参数是第一个子表达式的结果(如果有多个则依次类推),和exec有些类似。

第三个参数 是offset , 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1

第四个参数string,被匹配的原字符串。

官网例子 =>

[^\d]* => 非数字(匹配多次)

\d* => 数字(匹配多次)

[^\w]* => 非数字、字母、下划线(匹配多次)

function replacer(match, p1, p2, p3, offset, string) {
  // p1 is nondigits, p2 digits, and p3 non-alphanumerics
  return [p1, p2, p3].join(' - ');
}
var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString);  // abc - 12345 - #$*%

2.1.5 matchAll

最为简洁的方式 =>es11 => matchAll

str.matchAll(regexp)

matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代

参数含义必选
regexp正则表达式对象Y

根据正则匹配字符串中所有的匹配项得出结果并返回一个迭代器,利用for…of迭代。

注意

  1. 如果所传参数不是一个正则表达式对象,则会隐式地使用new RegExp(obj)将其转换为一个 RegExp
  2. 返回值一个迭代器,但是不可重用,结果耗尽需要再次调用方法,获取一个新的迭代器
const str = `
    <html>
        <body>
            <div>第一个div</div>
            <p>这是p</p>
            <div>第二个div</div>
            <span>这是span</span>
            <div>第三个div</div>
        </body>
    </html>
`

const regExp = /<div>(.*)<\/div>/g

// matchAll
function selectDiv(regExp, str){
    let matches = []
    for(let match of str.matchAll(regExp)){
        matches.push(match[1])
    }
    return matches
}
const res = selectDiv(regExp, str)
console.log(res)

实际和exec g 方法类似,但是更为简洁。

image-20201130123625538

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.17
Branch: branch02

commit description:a4.17(字符串扩展——matchAll)

tag:a4.17


报错,matchAll要求必须有全局的匹配参数,即要求必须写g,因此该方法更为安全,不容易死循环。

因此在项目中,如果有需求需要用正则匹配字符串中所有的匹配项,强烈推荐使用matchAll

const regExp = /<div>(.*)<\/div>/

image-20201130124217836

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.18
Branch: branch02

commit description:a4.18(字符串扩展——matchAll安全性)

tag:a4.18


3. 动态导入:Dynamic import()

按需 import提案几年前就已提出,如今终于能进入ES正式规范。这里个人理解成“按需”更为贴切。现代前端打包资源越来越大,打包成几MJS资源已成常态,而往往前端应用初始化时根本不需要全量加载逻辑资源,为了首屏渲染速度更快,很多时候都是按需加载,比如懒加载图片等。而这些按需执行逻辑资源都体现在某一个事件回调中去加载。


=> 按需导入,实际就是懒加载。目的就是加快首屏渲染速度,不需要一进页面,加载大量的资源,部分页面可以用户进入的时候再加载资源。

实现 => 点击按钮,再进行导入之前我封装的ajax请求的文件。

ajax是通过export default导出的,因此这里import返回的Promise参数就是该对象,利用default直接调用即可。

const oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', () => {
    import('./ajax').then(mod => {
        console.log(mod)
        mod.default('static/a.json', res => {
            console.log(res)
        })
    })
})

image-20201130150215974

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.19
Branch: branch02

commit description:a4.19(动态导入:Dynamic import())

tag:a4.19


Vue生态圈中经常用到vue-router => 路由懒加载就应用该原理。并且webpack目前已很好的支持了该特性。


4. 新的原始数据类型:BigInt

es11之前,支持的最大整数,是253次方。哪怕对其加1,最后的结果还是253次方。

Number.MAX_SAFE_INTEGER == 2的53次方 - 1

const max = 2 ** 53
console.log(max)

console.log(Number.MAX_SAFE_INTEGER)

console.log(max === max + 1) // true

es11之前,所谓的整型是存在最大数限制的。

image-20201130151033407

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.20
Branch: branch02

commit description:a4.20(新的原始数据类型:BigInt——es11之前,所谓的整型是存在最大数限制的)

tag:a4.20


4.1 数字后面增加n

但如果现在需求,需要更大数字。 => 利用es11引进的新的原始数据类型:BigInt => 数字末尾加n

BigInt,表示一个任意精度的整数,可以表示超长数据,可以超出253次方。

const bigInt = 9007199254740993n
console.log(bigInt)
console.log(typeof bigInt) // bigint

image-20201130153214993

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.21
Branch: branch02

commit description:a4.21(新的原始数据类型:BigInt——基本使用)

tag:a4.21


BigInt数据类型对应小一些数,与正常数组类型对比,两等成立,三等不成立。

即与普通数相比,同样的数值相等但类型不等。

console.log(1n == 1) // true
console.log(1n === 1) // false

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.22
Branch: branch02

commit description:a4.22(新的原始数据类型:BigInt——与普通数相比,同样的数值相等但类型不等)

tag:a4.22


4.2 使用 BigInt 函数

除了直接在数字末尾加n,也可通过BigInt函数传递参数创建BigInt数据。

很大的数也可进行运算,但是出来的结果末尾带n,如果不想要n,可以调用toString方法。

注意:超过范围的数字,只能用字符串存储,用number类型是存不下的。

const bigInt = 9007199254740993n

const bigInt2 = BigInt(9007199254740993n)
console.log(bigInt2)

const num = bigInt + bigInt2
console.log(num.toString())

image-20201130153751555

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.23
Branch: branch02

commit description:a4.23(新的原始数据类型:BigInt——特性)

tag:a4.23


5. Promise扩展:Promise.allSettled()


Promisees6提出,作用是对于异步状态进行管理的,其内有一个静态方法是all,它的作用是,其参数是一个数组,数组里的每一个元素都是一个promise对象,需要等待数组中的所有promise对象都执行完成以后,如果想执行某些逻辑操作,就可将其放在all里。

实际Promise.all存在一些缺陷,满足不了有些应用场景。

它的最大问题就是如果其中某个任务出现异常(reject),所有任务都会挂掉,Promise直接进入 reject状态。

Promise.all([
    Promise.resolve({
        code: 200,
        data: [1, 2, 3]
    }),
    Promise.resolve({
        code: 200,
        data: [4, 5, 6]
    }),
    Promise.resolve({
        code: 200,
        data: [7, 8, 9]
    }),
]).then(res=>{
    console.log(res)
    console.log('成功')
}).catch(err=>{
    console.log(err)
    console.log('失败')
})

image-20201130154728517

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.24
Branch: branch02

commit description:a4.24(Promise扩展——Promise.all都成功)

tag:a4.24


假设以上三次请求,中间的请求是失败的,Promise.all 会认为都是失败的。这样就存在一个问题,请求成功的结果就获取不到的。这种一个请求失败,就导致所有请求失败的需求显然是不太好的。

Promise.all([
    Promise.resolve({
        code: 200,
        data: [1, 2, 3]
    }),
    Promise.reject({
        code: 500,
        data: []
    }),
    Promise.resolve({
        code: 200,
        data: [7, 8, 9]
    }),
]).then(res=>{
    console.log(res)
    console.log('成功')
}).catch(err=>{
    console.log(err)
    console.log('失败')
})

image-20201130155942247

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.25
Branch: branch02

commit description:a4.25(Promise扩展——Promise.all对应一个请求失败)

tag:a4.25


实际最好失败的请求则返回失败,成功的请求也可以返回对应的结果。 => 这种场景就无法使用Promise.all 了。

这就需要一种机制,对于并发任务的处理,不管哪个是成功的?还是失败的?我们都可以得到对应的状态。 => Promise.allSettled()

Promise.allSettled([
    Promise.resolve({
        code: 200,
        data: [1, 2, 3]
    }),
    Promise.reject({
        code: 500,
        data: []
    }),
    Promise.resolve({
        code: 200,
        data: [7, 8, 9]
    }),
]).then(res=>{
    console.log(res)
    console.log('成功')
}).catch(err=>{
    console.log(err)
    console.log('失败')
})

虽然并发请求返回三个对象,但是每一个都对应一个promise的状态。

image-20201130160207012

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.26
Branch: branch02

commit description:a4.26(Promise扩展——Promise.allSettled()基本使用)

tag:a4.26


根据不同的状态,可以进行对应逻辑处理。如:只过滤出成功的状态。

Promise.allSettled([
    Promise.resolve({
        code: 200,
        data: [1, 2, 3]
    }),
    Promise.reject({
        code: 500,
        data: []
    }),
    Promise.resolve({
        code: 200,
        data: [7, 8, 9]
    }),
]).then(res=>{
    const data = res.filter(item => item.status === 'fulfilled')
    console.log(data)
}).catch(err=>{
    console.log(err)
    console.log('失败')
})

也可以再对失败的请求,给用户一个提示等等。

image-20201130160536606

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.27
Branch: branch02

commit description:a4.27(Promise扩展——Promise.allSettled()只过滤出成功的状态)

tag:a4.27


6. 全局对象:globalThis

Javascript在不同的环境获取全局对象有不通的方式:

note:self => 打开任何一个网页,浏览器会首先创建一个窗口,这个窗口就是一个window对象,也是js运行所依附的全局环境对象和全局作用域对象。self指窗口本身,它返回的对象跟window对象是一模一样的。也正因为如此,window对象的常用方法和函数都可以用self代替window


self.setTimeout(()=>{
    console.log('es2020')
}, 1000)

es2020

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.28
Branch: branch02

commit description:a4.28(全局对象:globalThis——self)

tag:a4.28


在之前环境中,想要获取任何环境下的全局变量则需要判断。

const getGlobal = () => {
    if (typeof self !== 'undefined') {
        return self
    }
    if (typeof window !== 'undefined') {
        return window
    }
    if (typeof global !== 'undefined') {
        return global
    }
    throw new Error('无法找到全局对象')
}
const global = getGlobal()
console.log(global)

image-20201201092751215

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.29
Branch: branch02

commit description:a4.29(全局对象:globalThis——在之前环境中,想要获取任何环境下的全局变量则需要判断)

tag:a4.29


es11中使用globalThis,在任何环境中都可以获取到全局对象。

globalThis提供了一个标准的方式来获取不同环境下的全局 this对象(也就是全局对象自身)。不像 window或者 self这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this就是 globalThis

console.log(globalThis)

web端

image-20201201092751215

node端

image-20201201093127452

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.30
Branch: branch02

commit description:a4.30(全局对象:globalThis——基本使用)

tag:a4.30


7. 可选链:Optional chaining

在开发当中会有很多对象,对象下面属性可能有很多层,经常需要判断对象下面有没有该属性,之前判断都非常麻烦,有了可选链会更为方便。

即可让我们在查询具有多层级的对象时,不再需要进行冗余的各种前置校验。


在之前的语法中,想获取到深层属性或方法,不得不做的前置校验,否则很容易命中 Uncaught TypeError: Cannot read property... 这种错误,这极有可能让你整个应用挂掉。

调用未知或者后端传来的对象,必须使用如下方式,否则万一属性没有就会报错,这样会非常不安全。

const user = {
    address: {
        street: 'xx街道',
        getNum() {
            return '80号'
        }
    }
}
const street = user && user.address && user.address.street
console.log(street)

const num = user && user.address && user.address.getNum && user.address.getNum()
console.log(num)

image-20201201093608672

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.31
Branch: branch02

commit description:a4.31(可选链:Optional chaining——之前判断方式)

tag:a4.31


可选链 => ?.

可选链中的 ? 表示如果问号左边表达式有值, 就会继续查询问号后面的字段。根据下面可以看出,用可选链可以大量简化类似繁琐的前置校验操作,而且更安全。

// 可选链
const user = {
    address: {
        street: 'xx街道',
        getNum() {
            return '80号'
        }
    }
}

const street = user?.address?.street
console.log(street)
const num = user?.address?.getNum?.()
console.log(num)

image-20201201093608672

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.32
Branch: branch02

commit description:a4.32(可选链:Optional chaining——基本使用)

tag:a4.32


8. 空值合并运算符:Nullish coalescing Operator

空值合并运算符??)是一个 逻辑运算符 。当左侧操作数为 nullundefined时,其返回右侧的操作数。否则返回左侧的操作数。

很多时候需要给某些值设置默认值,经常使用或运算符设置默认值,但是或运算符会有一些缺陷。

因此es11提出空值合并运算符

const b = 2
const a = b || 5
console.log(a)

2

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.33
Branch: branch02

commit description:a4.33(空值合并运算符——或运算符-默认值有值的情况)

tag:a4.33


const b = 0
const a = b || 5
console.log(a)

弊端:0代表false,因此如果值是0,会认为是没值。

注意:一般设置默认值都是为了防止nullundefined。但是如果是false0,也被赋值默认值,这是很大的弊端。

5

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.34
Branch: branch02

commit description:a4.34(空值合并运算符——或运算符-弊端)

tag:a4.34


空值合并运算符?? 我们仅在第一项为 nullundefined时设置默认值

const b = null
const a = b ?? 6
console.log(a)

6

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.35
Branch: branch02

commit description:a4.35(空值合并运算符——null)

tag:a4.35


const b = false
const a = b ?? 6
console.log(a)

false

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a4.36
Branch: branch02

commit description:a4.36(空值合并运算符——false)

tag:a4.36




(后续待补充)

标签:const,log,17,Promise,ECMAScript,regExp,console,es11,match
来源: https://blog.csdn.net/u013946061/article/details/113849445