围观!我国第一个推进到浏览器标准的 ECMaScript 提案!
以下文章来源于前端很美 ,作者ginkgo6
在2022年6月22日,第123届Ecma大会批准了ECMAScript 2022语言规范。一起来看看ES13给我们带来了哪些新特性吧。其中最后一个特性,是我国第一个推进到浏览器标准的EcmaScript提案。
1.私有属性和私有方法
之前js中class的一个痛点就是没有私有属性,毕竟封装是面向对象的三大特性,没有私有属性如何实现封装。尽管我们可以用很多方法去曲线实现私有属性比如闭包proxy、weakmap、symbol等等,但是他们各自有各自的缺陷,下面的代码演示其中以_作为命名约定的方式实现私有属性的缺点。
class User {
constructor() {
// public 属性
this.name = "Tom";
// private 属性
this._lastName = "Brown";
}
getFullName(){
return `${this.name} ${this._lastName}`
}
}
const user = new User();
user.name
// "Tom"
user._lastName
// "Brown"
// no error thrown, we can access it from outside the class
在构造函数中,我们定义了两个字段,其中一个字段是以_开头的, 这是一个命名约定用于将字段声明为私有字段,但是当我们在类外面访问该字段时,并没有引起报错,原因是因为它只是一个命名规范而已。
现在在ES13中,我们可以用更简单的方法声明公共和私有字段,第一,我们不必在构造函数中定义它们了,其次,我们可以通过在字段前面添加#号来定义一个真正的私有字段。
class User {
name = "Tom";
#lastName = "Brown"
getFullName(){
return `${this.name} ${this.#lastName}`
}
}
const user = new User();
user.name
// "Tom"
user.getFullName();
// "Tom Brown"
user.#lastName
// SyntaxError - cannot be accessed or modified from outside the class
有了这个特性,我们终于可以简单轻松的定义一个真正的私有属性或私有方法了。
2.私有属性检测
当我们尝试访问对象上不存在的私有属性时会报错,所以就需要一种方法来检测对象是否具有某私有属性,此时,我们可以在class中使用in关键字来完成此工作。
class Person {
#name;
constructor(name) {
this.#name = name;
}
static check(obj) {
return #name in obj;
}
}
Person.check(new Person()), // true
当然,你可能会发问对于具有相同名称的私有属性的类,in 关键字是否能正常工作,别担心,请参考以下例子,User类和Person类具有相同的私有属性name,in关键字可以正确的区分。
class User {
#name;
constructor(name) {
this.#name = name;
}
static check(obj) {
return #name in obj;
}
}
class Person {
#name;
constructor(name) {
this.#name = name;
}
static check(obj) {
return #name in obj;
}
}
User.check(new User()), // true
User.check(new Person()), // false
Person.check(new Person()), // true
Person.check(new User()), // false
3. 类静态初始化块(Class Static Block)
类在初始化的时候会执行静态初始化块(Class Static Block),一个类可以拥有任意个静态初始化块,它们将会按照声明的顺序执行。静态初始块内还可以访问父类的静态属性。
class Dictionary {
static words = [ "yes", "no", "maybe" ];
}
class Words extends Dictionary {
static englishWords = [];
static #localWord = 'ok';
// 第一个静态初始化块
static {
const words = super.words;
this.englishWords.push(...words);
}
// 第二个静态初始化块
static {
this.englishWords.push(this.#localWord);
}
}
console.log(Words.englishWords)
//输出 -> ["yes", "no", "maybe", "ok"]
下面这个例子,我们还可以通过静态初始化块将私有属性暴露出去。
let getClassPrivateField;
class Person {
#privateField;
constructor(value) {
this.#privateField = value;
}
static {
getClassPrivateField = (obj) => obj.#privateField;
}
}
getClassPrivateField(new Person('private value'));
4.Regexp Match Indices
之前 ECMAScript 中的 「RegExp.prototype.exec」 方法的返回值已经提供了对于匹配的捕获组 文本与对应的捕获组在正则表达式中的索引。尽管它已经包含输入字符串,模式匹配的索引以及包含任何已命名捕获组的子字符串的group对象,但是对于复杂情况,此信息可能不足。
const fruits = 'Fruits: apple, banana, orange'
const regex = /(banana)/g;
const matchObj = regex.exec(fruits);
console.log(matchObj);
// [
// 'banana',
// 'banana',
// index: 15,
// input: 'Fruits: apple, banana, orange',
// groups: undefined
// ]
所以ES13通过新增/d修饰符,向匹配结果添加一个属性 .indices,此属性是一个索引数组,其中包含每个捕获的子字符串的一对开始索引和结束索引。
const fruits = 'Fruits: apple, banana, orange'
const regex = /(banana)/gd;
const matchObj = regex.exec(fruits);
console.log(matchObj);
// [
// 'banana',
// 'banana',
// index: 15,
// indices:[
// [15, 21],
// [15, 21]
// ]
// input: 'Fruits: apple, banana, orange',
// groups: undefined
// ]
5.Await operator at the top-level
之前await关键词只能在aysnc function里进行使用,想要在顶层使用await就必须要加个aysnc自执行函数,这样十分的不方便, 所以ES13中,引入了可以直接在顶层使用Await关键字的特性。
动态加载模块:
const strings = await import(`./example.mjs`); |
依赖回退:
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}
加载最快的资源
const resource = await Promise.any([
fetch('http://example1.com'),
fetch('http://example2.com'),
]);
6 .at()方法
之前,我们如果想要获取一个数组的元素,如果是正序获取一个元素,我们可以采取arr[0]这样的方式, 但是如果我们要倒序访问一个数组的元素,就需要采取arr[arr.length - 2]这样的方式,数组的名称arr我们写了两次,而且还要写个length,这很不优雅,复杂的情况下就更是难受。
const arr = [100,200,300,400]
arr[0] // 100
arr[arr.length - 2] // 300
arr.slice(-2)[0] // 300
为了解决这个问题, ES13引入了at()方法,无论正序还是倒序获取元素都非常的优雅,有了这个方法,我们可以在数组、字符串、TypedArray上通过索引值方便的获取元素。
const arr = [100,200,300,400]
arr.at(0) // 100
arr.at(-2) // 300
const str = "ABCD"
str.at(-1) // 'D'
str.at(0) // 'A'
7.Object.hasOwn(obj, propKey)
之前我们如果想要判断某对象是否具有某属性,我们会通过in和obj.hasOwnProperty,但是如果指定的属性位于原型链中,“in”运算符也会返回true。所以之前要想判断对象自身是否具有某属性我们一般都通过obj.hasOwnProperty来判断。
但是obj.hasOwnProperty也有自身的缺点:因为hasOwnProperty是不受保护的属性,所以,人们可能会在对象上定义个自己的hasOwnProperty,如下所示,自定义的hasOwnProperty永远返回false,这是一个坑。
const person = {
name: "Roman",
hasOwnProperty:()=> {
return false
}
}
person.hasOwnProperty('name'); // false
另一个问题是使用Object.create(null) 创建一个不继承自 Object.prototype 的对象,使 hasOwnProperty 方法时会报错。
Object.create(null).hasOwnProperty("name")
// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function
所以,为了解决上面的问题,ES13引入了**Object.hasOwn(obj, propKey)**。用法如下所示:
const object = { name: "Mark" };
Object.hasOwn(object, "name"); // true
const object2 = Object.create({ name: "Roman" });
Object.hasOwn(object2, "name"); // false
Object.hasOwn(object2.__proto__, "name"); // true
const object3 = Object.create(null);
Object.hasOwn(object3, "name"); // false
8.Error Cause
Error Cause提案由阿里巴巴的「昭朗」同学负责,是我国第一个成为浏览器标准的EcmaScript提案。
以前因为 Error Cause 没有标准化的参数定义及官方实现,所以容易丢失 error 的属性或需要写比较多的代码自定义等,并且开发者工具也难以依赖于非语言特性的自定义方案。
try {
apiCallThatCanThrow();
} catch (err) {
throw new Error('New error message', { cause: err });
}
有了这个新特性,借助cause属性,我们可以记下导致这个Error的前一个Error对象,错误对象就可以以一种简单优雅的方式链接起来。
9.总结
以上就是ES13的新特性,新特性会给js生态带来更多好的东西,提高开发者的效率和体验。
作者:ginkgo6
欢迎关注微信公众号 :前端印象