JavaScript进阶(二十六):ES各版本特性详解
文章目录
- 一、ECMAScript简介
- 二、ES6 (ES2015)
- 三、ES7 (ES2016)
- 四、ES8 (ES2017)
- 五、ES9 (ES2018)
- 六、ES10 (ES2019)
- 七、ES11 (ES2020)
- 八、ES12 (ES2021)
- 九、ES13 (ES2022)
- 9.1 Strings、Arrays、TypedArrays 的 at 方法
- 9.2 私有类元素
- 9.3 静态块
- 9.4 私有 in 操作符
- 9.5 正则 /d 标志
- 9.6 Error 对象的 cause 属性
- 9.7 Object.hasOwn
- 十、���展阅读
一、ECMAScript简介
ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,所以它可以理解为是JavaScript的一个标准,但实际上后两者是ECMA-262标准的实现和扩展。
()二、ES6 (ES2015)
-
Class
-
模块化语法(import、export)
() -
箭头函数 () => {…},是函数的缩写。
-
函数参数默认值
function fn(x,y='World') { console.log(x, y); }
-
模板字面量
之前实现中,长字符串的组合是通过 + 号来连接的。
它的可读性很差,使用模板字符串,它更容易阅读。
-
解构赋值
允许 JavaScript 轻松地从数组和对象中获取内容。
-
扩展运算符
它是用三点(...)表示,Array是可以扩展的,如果是Object,会按照key-value进行扩展。
-
对象属性简写
如果构成对象的字段名称与前面段落中的变量相同,则可以省略该值,看起来更流线型。
-
Promise
Promise 是一种异步(非同步)写法的解决方案,比原来的回调写法更加优雅。ES8(ES2017)发布了更完美的async,await,直接让异步写得像同步一样。缺点是当思路落到复杂的业务逻辑上时,有时会错过await,在运行时发生错误。
-
let, const 替换 var
- let:通用变量,可以被覆盖。
- const:一旦声明,其内容不可修改。因为数组和对象都是指针,所以它们的内容可以增加或减少, 但不改变其指针。
早期,JavaScript的var作用域是全局的。也就是说,var变量是在使用后声明的,执行的时候会自动提到顶层,后面会赋值。
更容易受到污染。
三、ES7 (ES2016)
- Array.prototype.includes()
用于判断数组是否包含指定值,如果是,则返回true;否则,返回假。和之前indexOf的用法一样,可以认为是返回一个布尔值,语义上更加清晰。
- 幂运算符 console.log(2**10); // 1024
四、ES8 (ES2017)
-
async, await
异步函数是使用 async 关键字声明的函数,并且允许在其中使用 await 关键字。async 和 await 关键字使异步的、基于 Promise 的行为能够以更简洁的方式编写,避免了显式配置 Promise 链的需要。
-
Object.values()
返回对象自身属性的所有值,不包括继承的值。
-
Object.entries()
返回可枚举键,即传入对象本身的值。
-
字符串 padStart() & padEnd()
你可以在字符串的开头或结尾添加其他内容,并将其填充到指定的长度。
过去,这些功能通常是通过通用的辅助工具包(如 lodash)引入的,并将它们放在一起。
-
尾随逗号
允许在函数参数列表末尾使用逗号。
-
Object.getOwnPropertyDescriptors()
获取你自己的描述符,一般的开发业务需求通常不会用到。
-
共享数组缓冲区
SharedArrayBuffer 是一个固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer。
可用于在共享内存上创建数据。与 ArrayBuffer 不同,SharedArrayBuffer 不能分离。
-
Atomics object
Atomics 对象,它提供了一组静态方法来对 SharedArrayBuffer 执行原子操作。原子的所有属性和函数都是静态的。
如果一个多线程同时在同一个位置读写数据,原子操作保证了正在操作的数据如预期的那样:即在上一个子操作结束后执行下一个,操作不中断。可以说是针对Node.Js中多线程Server的开发而加强的功能,在前端开发中使用的机会相当低。chrome 已经提供了支持。
五、ES9 (ES2018)
- 循环等待
在异步函数中,有时需要在同步 for 循环中使用异步(非同步)函数。
async function process(array) { for (const i of array) { await doSomething(i); } } async function process(array) { array.forEach(async i => { await doSomething(i); }); }
上面的代码不会像预期的那样输出期望的结果。
for循环本身还是同步的,会在循环中的异步函数完成之前执行整个for循环,然后将里面的异步函数逐一执行。
ES9 增加了异步迭代器,允许 await 与 for 循环一起使用,逐步执行异步操作。
async function process(array) { for await (const i of array) { doSomething(i); } }
-
promise.finally()
无论是成功(.then())还是失败(.catch()),Promise 后面都会执行的部分。
-
Rest, Spread
在 ES2015 中,Rest 不定长度参数…,可以转换成数组传入。
-
正则表达式组
RegExp 可以返回匹配的数据包
const regExpDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/; const match = regExpDate.exec('2020-06-25'); const year = match[1]; // 2020 const month = match[2]; // 06 const day = match[3]; // 25
- 正则表达式 dotAll
. 表示匹配除输入以外的任何符号,添加这些标志后,允许匹配输入。
/hello.world/.test('hello\nworld'); // false /hello.world/s.test('hello\nworld'); // true
六、ES10 (ES2019)
- 更友好的 JSON.stringify
如果输入是 Unicode 但超出范围,则 JSON.stringify 最初会返回格式错误的 Unicode 字符串。
现在是第 3 阶段的提案,使其成为有效的 Unicode 并以 UTF-8 呈现。
-
Array.prototype.flat() & Array.prototype.flatMap()
展平阵列
-
String.prototype.trimStart() & String.prototype.trimEnd()
trimStart() 方法从字符串的开头删除空格,trimLeft() 是此方法的别名。
-
Object.fromEntries()
Object.fromEntries() 方法将键值对列表转换为对象。
-
String.prototype.matchAll
matchAll() 方法返回将字符串与正则表达式匹配的所有结果的迭代器,包括捕获组。
-
fixed catch 绑定
在使用catch之前,不管有用与否,一定要传入一个eparameter来表示接收到的错误。
如果现在不用,可以省略。
-
BigInt(新数字类型)(重要)
BigInt 值,有时也称为 BigInt,是一个 bigint 原语,通过将 n 附加到整数文字的末尾,或通过调用 BigInt() 函数(没有 new 运算符)并给它一个整数值或字符串来创建值。
- ES5:String, Number, Boolean, Null, Undefined
- ES6 新增:Symbol,到ES6就一共有6 种类型
- ES10 新增:BigInt,就达到 7 种类型
七、ES11 (ES2020)
- Promise.allSettled()
Promise.allSettled() 方法返回一个在所有给定的 Promise 都已实现或拒绝后实现的 Promise,并带有一组对象,每个对象都描述了每个 Promise 的结果。
它通常用于当有多个不依赖于彼此成功完成的异步任务,或者总是想知道每个 Promise 的结果时。
相比之下,Promise.all() 返回的 Promise 可能更合适,如果任务相互依赖/如果想立即拒绝其中任何一个拒绝。
- 可选链操作符?.
在开发中,很容易遇到先判断数据是否存在,判断是否写入。
const isUserExist = user && user.info; if (isUserExist) { username = user.info.name; }
如果返回的数据为null或者用户对象下没有相应属性,则会抛出Uncaught TypeError: Cannot read property...。
导致程序无法继续执行
使用 ?.,语法更简单
const username = user?.info?.name;
如果存在,获取name的值,如果不存在,赋值undefined
与 || 一起使用,只需一行!
const username = user?.name || 'guest';
-
Nullish 合并运算符 ??
在JavaScript中,遇到0、null、undefined时会自动转为false。
但有时0其实是一个正常的值,只能容错undefined和null,但是使用??,可以保持简洁。
-
Dynamic-import
从字面上看,应该很容易理解,就是在需要的时候加载相关的逻辑。
-
GlobalThis
全局 globalThis 属性包含全局 this 值,类似于全局对象。
八、ES12 (ES2021)
- Promise.any()
Promise.any() 接受一个可迭代的 Promise 对象。它返回一个单一的 Promise,只要 iterable 中的任何一个 Promise 完成,就会返回一个 Promise,并带有已完成的 Promise 的值。
如果可迭代的实现中没有任何承诺(如果所有给定的承诺都被拒绝),则返回的承诺会被 AggregateError 拒绝,AggregateError 是 Error 的一个新子类,它将单个错误组合在一起。
- 逻辑赋值运算符
在开发过程中,可以使用 ES2020 中提出的逻辑运算符 ||、&& 和 `??(Nullish coalescing operator)来解决一些问题。
而 ES2021 会提出 ||= , &&= , ??= ,概念类似于 += :
let b = 2 b += 1 // equal to b = b + 1 let a = null a ||= 'some random text' // a become to'some random text' // equal a = a || 'some random text' let c = 'some random texts' c &&= null // c become to null // equal to c = c && null let d = null d ??= false // d become to false // equal to d = d ?? false
- WeakRef
WeakRef 对象包含对对象的弱引用,该对象称为其目标或引用对象。对对象的弱引用是不会阻止对象被垃圾收集器回收的引用。相反,普通(或强)引用将对象保存在内存中,当一个对象不再有任何强引用时,JavaScript 引擎的垃圾收集器可能会销毁该对象并回收其内存。如果发生这种情况,将无法再从弱引用中获取对象。
九、ES13 (ES2022)
ES13 带来了 6 个新特性:
- 模块顶层作用域支持 await 表达式。
- 新增私有类元素(#)、静态块;in 操作符支持私有类元素。
- 正则新增 d 标志和其对应的 hasIndices 属性,提供了获取捕获组开始索引和结束索引的方法。
- Error 实例增加 cause 属性,可携带更多错误信息。
- Strings、Arrays、TypedArrays 新增 at 方法,支持关联访问。
- Object.hasOwn 代替 Object.prototype.hasOwnProperty,判断对象是否含有属性。
9.1 Strings、Arrays、TypedArrays 的 at 方法
利用下标访问数组元素时,下标会被转换为字符串,负数下标也对应着一个独立的数组元素,所以 Js 的数组是不支持关联访问的。ES13 新增了 at 方法,可以利用负数索引进行关联访问,例如以下示例,可以利用 at 方法和负数索引 -2 来访问倒数第二个元素。
const array = [5, 12, 8, 130, 44]; array.at(-2); // 130
9.2 私有类元素
在类的定义中,以 “#” 开头的标识符为私有标识符,由私有标识符定义的类元素被称为私有类元素,私有类元素只能在类中才能被访问到。类的属性、静态属性、方法、静态方法、访问器、静态访问器都可以被定义为私有类元素。示例代码如下:
class ClassA { // 私有属性 #privateProperty; // 静态私有属性 static #privateStaticProperty; // 静态私有 Getter static get #privateStaticGet() { return 'private-static-get'; } // 静态私有 Setter static set #privateStaticSet(val) { } // 静态私有方法 static #privateStaticMethod() { return 'private-static-method' } constructor(propertyValue) { // 初始化私有属性 this.#privateProperty = propertyValue; } // 私有 Get get #privateGet() { return 'private-get' } // 私有 Set set #privateSet() { } // 私有方法 #privateMethod() { return 'private-method' } }
在类的内部可以正常访问私有类元素,在类的外部访问私有类元素会抛出句法错误。
class ClassA { // 私有属性 #privateProperty; constructor(property, privateProperty) { this.property = property; this.#privateProperty = privateProperty; } // 在方法中访问私有属性 getPrivateProperty() { return this.#privateProperty; } } const instance = new ClassA('property', 'private-property'); instance.property; // 'property' instance.#privateProperty; // Uncaught SyntaxError: Private field '#privateProperty' must be declared in an enclosing class instance.getPrivateProperty(); // 'private-property'
以上就是私有类元素的用法,它有效隔离了类内外的数据和逻辑,进一步增强了封装的效果,使程序更加健壮。
9.3 静态块
静态块提供了更加灵活的静态类元素初始化渠道,可以在静态块中使用一系列的语句来完成静态类元素的初始化。可以利用 this 在静态块中访问类的其他静态属性(包括私有属性)。
class ClassA { // 静态属性 static staticProperty; // 静态块初始化静态属性,捕捉错误 static { try { this.staticProperty = getStaticProperty(); } catch { console.log('Error'); } } }
当一个类具有多个静态块时,它们会按照定义的顺序进行执行。
class ClassA { // 静态属性 A static staticPropertyA; // 静态块 A static { this.staticPropertyA = 'static-block-a'; console.log('static-block-a'); } // 静态属性 B static staticPropertyB; // 静态块 B static { this.staticPropertyB - 'static-block-b'; console.log('static-block-b'); } } // 输出 // static-block-a // static-block-b
当一个类具有父类时,会先执行父类的静态块,再执行子类的静态块。可以利用 super 在子类的静态块中访问父类的属性。
// 父类 class ParentClass { // 父类属性 static parentProperty; // 父类静态块 static { this.parentProperty = 'parent-property'; console.log('parent-static-block'); } } // 子类 class ChildClass extends ParentClass { // 子类静态块 static { console.log(super.parentProperty); console.log('child-static-block'); } } // 输出 // parent-static-block // parent-property // child-static-block
9.4 私有 in 操作符
in 操作符可以判断实例中是否存在属性,当新增了私有化类元素后,也可以和下面例子一样,在类定义内使用 in 操作符判断私有化类元素存在与否。
class ClassA { // 私有属性 #privateProperty; // 利用 in 操作符判断私有属性是否存在 static hasPrivateProperty(instance) { return #privateProperty in instance } constructor(privateProperty) { this.#privateProperty = privateProperty; } } const instance = new ClassA('private-property'); ClassA.hasPrivateProperty(instance); // 输出 // true
9.5 正则 /d 标志
正则表达式通常用来处理字符串,正则表达式有很多标志,它决定了正则状态机的行为和输出结果。ES13 新增的 d 标志对应正则实例的 hasIndices 属性,当设置 d 标志时,hasIndices 为 true 。使用 d 标志后,正则表达式的匹配结果将包含每个捕获组捕获子字符串的开始和结束游标。
// 字符串 const str = "today is saturday"; // 正则表达式 const reg = /saturday/d; // 匹配结果输出游标 const [start, end] = reg.exec(str).indices[0]; console.log([start, end]); // [9, 17]
9.6 Error 对象的 cause 属性
在以往 Error 对象只能传递消息信息,现在 ES13 为 Error 增添了 cause 属性,它可以是任意数据类型,方便获取更多地错误信息。
try { // 抛出带 cause 属性的 Error实例 throw new Error('faied message', { cause: 'cause' }); } catch (error) { // 捕获 error 并输出 cause console.log(error.cause) } // 输出 // cause
9.7 Object.hasOwn
ES13 新增了 Object.hasOwn 方法判断这个实例上是否有属性,以代替之前的 Object.prototype.hasOwnProperty 方法。
const object = { count: 1 }; Object.hasOwn(object, 'count'); // true Object.hasOwn(object, 'toString'); // false Object.hasOwn(object, 'property'); // false
十、拓展阅读
- 《JavaScript进阶(十四):详解 ES6 中的 export 和 import》
- 《JavaScript进阶(十三):JavaScript 空值合并运算符、可选链操作符、空值赋值运算符讲解》
- 《JavaScript进阶(十二):JS 模块化编程规范-CommonJS、AMD、CMD、ES6》
- 《JavaScript进阶(十八):ES6 Symbol 用法》
- 《JavaScript进阶(二十):精解 ES6 Promise 用法》
- 《JavaScript进阶(二十四):ES8 中 async 与 await 使用方法详解》
- 《JavaScript进阶(二十五):Promise 详解》
- Promise.allSettled()
- Array.prototype.includes()
-