前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2021年,快速了解 ES2022 新特性(一)

2021年,快速了解 ES2022 新特性(一)

作者头像
coder_koala
发布2021-11-10 10:29:47
5210
发布2021-11-10 10:29:47
举报

作为一个后端的菜菜,其实应该多写点后端的东西,奈何同学们都喜欢看点前端的文章。这就没法子了🙄,前两天看到掘金群里有位同学不知道在哪儿看ES的新特性和ES规范与新特性的对应关系。我觉得我的机会来了🤣,忙了两天,今天抽点时间给大家科普一下,顺便讲讲 ES2016-ES2022 到底有啥内容,顺便复习一下,加深印象。

ES是啥?

ES的全称是ECMAScript,由 ECMA国际[3] (前身为欧洲计算机制造商协会)在标准 ECMA-262[4] 中定义的脚本语言规范,从2015年起,每年一个版本,到 ES2022 已经是第十三个版本了。我们常用的 JavaScript 就是 ECMA-262[5] 标准的实现和拓展。现在我直接贴一个官网的地址 ECMAScript[6] 吧,详细的内容大家可以直接查看官网的介绍。我就不过多介绍这个东西了。水字数没必要。。。🤥

尽管 ES2022 的还没有正式发布,但是提案已经完成,板上钉钉。下面我就按照大概的情况列一下各个ES版本提案的全称、发布年份、缩写信息等,这样大家就不会迷惑ES的命名了。

全称

发布年份

缩写 / 简称

ECMAScript 2015

2015

ES2015 / ES6

ECMAScript 2016

2016

ES2016 / ES7

ECMAScript 2017

2017

ES2017 / ES8

ECMAScript 2018

2018

ES2018 / ES9

ECMAScript 2019

2019

ES2019 / ES10

ECMAScript 2020

2020

ES2020 / ES11

ECMAScript 2021

2021

ES2021 / ES12

ECMAScript 2022

2022

ES2022 / ES13

有的同学可能会有这样的疑问,为啥 ES2015 对应的是 ES6 ,而不是 ES5 呢? 这里说明一下,这两个数字不是同一个东西,2015 代表的是发布的日期,而 6 表示的是ECMAScript语言规范的第几个版本。这样不懂的同学可能就明白了,再回头一看上面的表格,这样印象就更加深刻了。

ES2016 - ES2022的特性

骚话不多说,下面我们直接开始吧。我会将 ES2016 - ES2022 的内容分成一段段举例演示,内容主要以 Github已完成 提案[7] 上的顺序讲解。(PS: 没有 ES2015 / ES6 ,直接 ES2016 / ES7 起步)

ES2016 / ES7

Array.prototype.includes 数组是否包含某个元素

这个特性很多小伙伴肯定都用过,这个主要的作用是查找一个元素是否在数组中。在这个方法没出来之前,偷懒同学可能是通过数组的 indexOf 方法来做校验的

代码语言:javascript
复制
[1, 2, 3].indexOf(1) >= 0

// 结果: true
复制代码

正常情况下,indexOf 方法并没有啥毛病,但是这个方法存在一个漏洞,当需要查看的元素是 NaN 时,这个 indexOf 方法将不能够准确的判断出元素是否被包含在数组中

代码语言:javascript
复制
[1, 2, NaN].indexOf(NaN) >= 0

// 结果: false
复制代码

另外一个问题是,indexOf 这个方法主要想表明的是一个元素在数组中的 索引位置 而不是确定一个元素是否包含在数组中,所以勤快的同学一般会通过手写一个循环方法来处理这个问题,但是引用这个方法又比较麻烦,直接套在原型上?不敢不敢。。。

代码语言:javascript
复制
function contains(array, val) {
    for (var i = 0; i < array.length; i++) {
        if (array[i] === val) {
            return true;
        }
    }
    return false;
}
复制代码

现在我们有了 includes 方法,上述的问题都将解决,并且更加好使,我们来测试一下以下代码,大家可以脑补一下下面的结果

代码语言:javascript
复制
console.log([1, 2, 3].includes(2) === true);
console.log([1, 2, 3].includes(4) === false);
console.log([1, 2, NaN].includes(NaN) === true);

console.log([1, 2, -0].includes(+0) === true);
console.log([1, 2, +0].includes(-0) === true);

console.log(["a", "b", "c"].includes("a") === true);
console.log(["a", "b", "c"].includes("a", 1) === false);
复制代码

我们现在公布答案,上面的七个结果均为 true 。毋庸置疑。。。

Exponentiation operator 幂运算

这个没有啥好讲的,直接看下面的代码片段就明白了

代码语言:javascript
复制
let squared = 2 ** 2;
// same as: 2 * 2

let cubed = 2 ** 3;
// same as: 2 * 2 * 2

let a = 2;
a **= 2;
// same as: a = a * a;

let b = 3;
b **= 3;
// same as: b = b * b * b;
复制代码

ES2017 / ES8

Object.values / Object.entries 对象值、对象对

这个也没啥好说的,Object.values 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同(区别在于 for-in 循环枚举原型链中的属性),看代码

代码语言:javascript
复制
let point = {x: 12, y: 6};
Object.values(point);

// 结果: [12, 6]
复制代码

Object.entries 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

听起来有点绕,咱们直接看代码

代码语言:javascript
复制
let point = {x: 12, y: 6};
Object.entries(point);

// 结果: [["x", 12], ["y", 6]]
复制代码

字符串也同样ok

代码语言:javascript
复制
Object.entries("hello world");

// 结果: [["0","h"],["1","e"],["2","l"],["3","l"],["4","o"],["5"," "],["6","w"],["7","o"],["8","r"],["9","l"],["10","d"]]
复制代码

来试试顺序

代码语言:javascript
复制
let object = {
    3: "z",
    1: "x",
    2: "y",
    z: 3,
    x: 1,
    y: 2
};

for (var key in object) {
    console.log(key);
}
// 1
// 2
// 3
// z
// x
// y

Object.entries(object);

// 结果: [["1","x"],["2","y"],["3","z"],["z",3],["x",1],["y",2]]
复制代码

我们可以看到,输出结果和上面我们的方法描述一致(其排列与使用 for...in 循环遍历该对象时返回的顺序一致)。同时,这儿还有个隐藏的 bufffor...in 会对数字的类型的 key 升序放在前面,不相信的同学自己也可以尝试一下哦。

String.prototype.padStart / String.prototype.padEnd 填充字符串

这两个方法还是比较常见的,我可以指定一个字符最大的宽度,当字符串超过最大长度则截断替换字符,保留左边(padStart)或者右边(padEnd)的内容当字符串宽度没有达到的时候,将会在开始(padStart )或者结尾(padEnd )填充指定的字符,填充字符未指定默认是一个 空格(U+0020) ,它主要用来格式化字符串,当然你也可以把它玩儿出花来。我们先来看一下定义

代码语言:javascript
复制
interface String {
    /**
     * 用给定字符串(可能重复)填充当前字符串,以使生成的字符串达到给定长度。填充从当前字符串的开始(左)开始应用。
     *
     * @param maxLength 填充当前字符串后所得字符串的长度。如果此参数小于当前字符串的长度,则当前字符串将按原样返回。
     *
     * @param fillString 用于填充当前字符串的字符串。如果此字符串太长,将截断它,并应用最左边的部分。参数的默认值为' '(U+0020)。
     */
    padStart(maxLength: number, fillString?: string): string;

    /**
     * 用给定字符串(可能重复)填充当前字符串,以使生成的字符串达到给定长度。从当前字符串的末尾(右侧)应用填充。
     *
     * @param maxLength 填充当前字符串后所得字符串的长度。如果此参数小于当前字符串的长度,则当前字符串将按原样返回。
     *
     * @param fillString 用于填充当前字符串的字符串。如果此字符串太长,将截断它,并应用最左边的部分。参数的默认值为' '(U+0020)。
     */
    padEnd(maxLength: number, fillString?: string): string;
}
复制代码

有的同学可能对上面的超出长度的截断的含义比较模糊,下面我们直接上手,看下输出就明白了

代码语言:javascript
复制
'foo'.padStart(5)
// 结果: '  foo'
'foo'.padStart(5, '-')
// 结果: '--foo'
'foo'.padStart(5, 'xyz')
// 结果: 'xyfoo' (超出长度,'z' 字符被截断去掉了,保留了左边部分的内容)
'bar'.padEnd(5)
// 结果: 'bar  '
'bar'.padEnd(5, '-')
// 结果: 'bar--'
'bar'.padEnd(5, 'xyz')
// 结果: 'barxy' (超出长度,'z' 字符被截断去掉了,保留了左边部分的内容)

// 原字符串长度超出最大字符串长度情况下
'foo'.padStart(2, 'xyz')
// 结果: 'foo'
'foo'.padStart(2, 'xyz')
// 结果: 'foo'
复制代码
Object.getOwnPropertyDescriptors 获取自身属性描述符

这个方法我们可以根据方法含义来理解,那就是获取自身属性的描述符。它常常配合 Object.create() 这个方法,来实现拷贝对象属性/对象属性特性以及原型。在理解这个方法之前,我们得知道啥是属性描述符。我们先定义一个对象打印一手,直接获取了对象的属性的描述符

代码语言:javascript
复制
let point = {
    x: 12,
    y: 16,
    move({x, y}) {
        this.x = x;
        this.y = y;
    }
}
Object.getOwnPropertyDescriptors(point);
// {
//     x: {
//         configurable: true,
//         enumerable: true,
//         value: 12,
//         writable: true,
//         [[Prototype]]: Object
//     }, 
//     y: {
//         configurable: true,
//         enumerable: true,
//         value: 16,
//         writable: true, 
//         [[Prototype]]: Object
//     },
//     move: {
//         configurable: true,
//         enumerable: true,
//         value: f move({x,y}),
//         writable: true, 
//         [[Prototype]]: Object
//     },
//     [[Prototype]]: Object
// }

let c = {}
Object.getOwnPropertyDescriptors(c);
// {}
复制代码

我们从上面的结果中,可以发现:如果对象为空,那么获取到的描述就是个空对象,如果有属性或者方法,会对每个属性进行描述,包含

  • configurable: 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false
  • enumerable: 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false
  • value: 该属性对应的值。可以是任何有效的 JavaScript 值( 数值对象函数 等)。默认为 undefined
  • writable: 当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。默认为 false

详情我们可以在 Object.defineProperty\(\)[8] 这儿看到,这里的属性描述符实际上是给 Object.defineProperty\(\)[9] 的第三个参数用的。从上面的例子中,我们可以看出,我们直接定义一个对象 point ,并且直接设置属性方法,对象属性默认 configurableenumerablewritable 设置为 true

我们现在给上面的例子再修改一下,加深理解。我们先遍历打印一下 pointkey

代码语言:javascript
复制
for (var key in point) {
    console.log(key);
}
// x
// y
// move
复制代码

接着使用 Object.defineProperty\(\)[10] 方法修改一下 pointmove 方法的描述符

代码语言:javascript
复制
Object.defineProperty(point, 'move', {
    enumerable: false
});
for (var key in point) {
    console.log(key);
}
// x
// y
复制代码

我们可以看到 move 这个方法在 for..in 中已经没有打印名称了。我们用 Object.getOwnPropertyDescriptor\(\)[11] 这个 老方法 再看看 move 的属性描述符

代码语言:javascript
复制
Object.getOwnPropertyDescriptor(point, 'move');
//{
//    configurable: true,
//    enumerable: false,
//    value: f move({x,y}),
//    writable: true, 
//    [[Prototype]]: Object
//}
复制代码

符合我们的预期。大家看明白了属性描述符,那对这个新增的方法就能理解的明明白白了

Async functions 异步方法

这个感觉没有啥好写的,懂得都懂。asyncawait 相当于一个语法糖,被 async 标记的方法将会返回一个 Promise 对象。我们可以在一个 async 标记的方法中使用 await 一个 Promise 对象,当 Promise 结束之后才执行下一语句,这让我们可以在 async 标记的方法中用同步的方法来书写异步的 Promise 。我们简单举一个例子

代码语言:javascript
复制
async function a() {
    return "a";
}
function b() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("b");
        }, 3000);
    });
}
async function c() {
    return "c";
}
async function d() {
    let a = await a();
    console.log(a);
    let b = await b();
    console.log(b);
    let c = await c();
    console.log(c);
}
console.log(d())

// 结果:
// Promise { <pending> } `async` 标记的方法返回了一个 `Promise` 对象
// a
// b  等待三秒之后继续输出后续内容
// c

复制代码

异步的方法同步的方式写,看起来很美好,但实际使用中还是有一点点问题。我之前写过一篇 JS中优雅的使用async await[12] ,有兴趣的小伙伴可以去瞅瞅。

Shared memory and atomics 共享内存和原子

这个是给浏览器的规范,我们可以通过 SharedArrayBufferAtomics 增强 js 的并行能力,想要了解的同学可以翻看 SharedArrayBuffer[13]Atomics[14] ,因为出现的场景很少,我就不细讲了。加油啊各位。

总结

文章到这儿暂时告一段落,我一开始膨胀了,文章最开始的标题是 一文快速了解ES2016 \- ES2022新特性 。但是写下来,照我这个讲解程度,光 ES7ES8 的篇幅就已经很长了,所以我现在打算将 ES2016 \- ES2022 的新特性拆分成多篇来写,目的呢,为了更好的让大家理解这些内容(才不是呢😝),给大家和我自己一点余地。嘻嘻

关于本文

作者:尽管如此世界依然美丽

https://juejin.cn/post/7021183043679289352

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员成长指北 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ES是啥?
  • ES2016 - ES2022的特性
    • ES2016 / ES7
      • ES2017 / ES8
      • 总结
      • 作者:尽管如此世界依然美丽
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档