像其他语言一样,JavaScript也在不断迭代和进化。JS每年都会加入很多新的功能来让自己变得越发强大,也正是这样,我们开发者才能写出更加表意和准确的代码。

在这篇文章中我们会通过一些例子来看一下最新的ECMAScript 2022(ES13)给我们开发者带来的11个超赞的新功能。

1. 类成员声明

在ES13之前,我们只能在构造函数里面声明类的成员,而不能像其他大多数语言一样在类的最外层作用域里面声明成员:

class Car {constructor() {this.color = 'blue';this.age = 2;}
}const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

老实说这么写起来挺不方便的,不过ES13出来之后,这都不算什么事儿了。现在我们终于可以突破这个限制,写下面这样的代码了:

class Car {color = 'blue';age = 2;
}const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

2. 给类定义私有方法和成员变量

ES13之前,我们是不可能给类定义私有成员的。所以一些程序员为了表示某个成员变量是一个私有属性,会给该属性名添加一个下划线(_)作为后缀。可是这只能作为程序员之间的君子之约来使用,因为这些变量其实还是可以被外界访问到的。

class Person {_firstName = 'Joseph';_lastName = 'Stevens';get name() {return `${this._firstName} ${this._lastName}`;}
}const person = new Person();
console.log(person.name); // Joseph Stevens// 这些所谓的私有属性其实还是可以被外界访问到的
console.log(person._firstName); // Joseph
console.log(person._lastName); // Stevens// 而且还能被改来改去
person._firstName = 'Robert';
person._lastName = 'Becker';console.log(person.name); // Robert Becker

不过ES13出来后,妈妈再也不用怕我们的私有属性会被别人访问和更改了。在ES13中,我们只需要给我们的属性名添加一个hashtag(#)前缀,这个属性就变成私有的了。当我们的属性变为私有后,任何外界对其的访问都会出错哦。

class Person {#firstName = 'Joseph';#lastName = 'Stevens';get name() {return `${this.#firstName} ${this.#lastName}`;}
}const person = new Person();
console.log(person.name);// SyntaxError: Private field '#firstName' must be
// declared in an enclosing class
console.log(person.#firstName);
console.log(person.#lastName);

这里值得一提的是,上面说的SyntaxError是一个编译时抛出的错误,所以你不会等你的代码运行后才知道这个属性被非法访问了。

3. 支持在最外层写await

我们都知道在JS中,await操作符的作用就是当我们碰到一个promise的时候,我们可以使用await来暂停当前代码的执行,等到这个promise被settled(fulfilled或者rejected)了,我们才继续当前代码的执行。

可是之前使用await的时候有个很头疼的地方就是一定要在一个async的函数里面使用而不能在全局作用域里面使用,像下面这么写就会报错:

function setTimeoutAsync(timeout) {return new Promise((resolve) => {setTimeout(() => {resolve();}, timeout);});
}// SyntaxError: await is only valid in async functions
await setTimeoutAsync(3000);

ES13出来后,就舒服多了,我们终于可以这么写代码了:

function setTimeoutAsync(timeout) {return new Promise((resolve) => {setTimeout(() => {resolve();}, timeout);})
}// 慢慢地等时间流逝吧
await setTimeoutAsync(3000);

4. 类支持定义静态成员和静态私有方法

在ES13中,我们还可以给类定义静态成员和静态私有函数。类的静态方法可以使用this关键字访问其他的私有或者公有静态成员,而对于类的实例方法则可以通过this.constructor来访问这些静态属性.

class Person {static #count = 0;static getCount() {return this.#count;}constructor() {this.constructor.#incrementCount();}static #incrementCount() {this.#count++;}
}const person1 = new Person();
const person2 = new Person();console.log(Person.getCount()); // 2

5. 类支持定义静态代码块

ES13允许在类中通过static关键字定义一系列静态代码块,这些代码块只会在类被创造的时候执行一次。这其实有点像一些其他的如C#和Java等面向对象的编程语言的静态构造函数的用法。

一个类可以定义任意多的静态代码块,这些代码块会和穿插在它们之间的静态成员变量一起按照定义的顺序在类初始化的时候执行一次。我们还可以使用super关键字来访问父类的属性。

class Vehicle {static defaultColor = 'blue';
}class Car extends Vehicle {static colors = [];static {this.colors.push(super.defaultColor, 'red');}static {this.colors.push('green');}console.log(Car.colors); ['blue', 'red', 'green']
}

6. 使用in来判断某个对象是否拥有某个私有属性

这个新属性的名字其实叫做Ergonomic Brand Checks for Private Fields,原谅我才疏学浅,我实在不知道怎么翻译,所以大概将它的作用表达了出来。总的来说,它就是用来判断某个对象是否拥有某个特定的私有属性,是通过in操作符来判断的。

class Car {#color;hasColor() {return #color in this;}
}const car = new Car();
console.log(car.hasColor()); // true

这个in操作符甚至还可以区分不同类的同名私有属性:

class Car {#color;hasColor() {return #color in this;}
}class House {#color;hasColor() {return #color in this;}
}const car = new Car();
const house = new House();console.log(car.hasColor()); // true
console.log(car.hasColor.call(house)); // false
console.log(house.hasColor()); // true
console.log(house.hasColor.call(car)); // false

7. 使用at函数来索引元素

一般来说如果我们想要访问数组的第N个元素,我们会使用方括号[N - 1]:

const arr = ['a', 'b', 'c', 'd'];
console.log(arr[1]); //b

这样写大多数时候都是没有什么问题的,其他语言也是这么做的,可是当我们需要访问数组倒数第N个元素的时候,我们的代码就变得有点奇怪了:

const arr = ['a', 'b', 'c', 'd'];// 倒数第一个元素
console.log(arr[arr.length - 1]); // d// 倒数第二个元素
console.log(arr[arr.length - 2]); // c

这么看你的代码是不是一下子变丑了?不用怕,ES13的at()函数帮你写出更优雅的代码!使用新的new()方法,当我们想要访问倒数第N个元素时,我们只需要传入-Nat()即可:

const arr = ['a', 'b', 'c', 'd'];// 倒数第一个元素
console.log(arr.at(-1)); // d
// 倒数第二个元素
console.log(arr.at(-2)); // c

你看,你的代码是不是一下子表意多了!除了数组,stringTypedArray对象也支持at()函数哦!

const str = 'Coding Beauty';
console.log(str.at(-1)); // y
console.log(str.at(-2)); // tconst typedArray = new Uint8Array([16, 32, 48, 64]);
console.log(typedArray.at(-1)); // 64
console.log(typedArray.at(-2)); // 48

8. 正则表达式匹配字符串的时候支持返回开始和结束索引

简单来说这个新属性就是允许我们告诉RegExp在返回match对象的时候,给我们返回匹配到的子字符串的开始和结束索引。

ES13之前,我们只能获取正则表达式匹配到的子字符串的开始索引:

const str = 'sun and moon';
const regex = /and/;
const matchObj = regex.exec(str);// [ 'and', index: 4, input: 'sun and moon', groups: undefined ]
console.log(matchObj);

ES13后,我们就可以给正则表达式添加一个d的标记来让它在匹配的时候给我们既返回匹配到的子字符串的起始位置还返回其结束位置:

const str = 'sun and moon';
const regex = /and/d;
const matchObj = regex.exec(str);
/**
['and',index: 4,input: 'sun and moon',groups: undefined,indices: [ [ 4, 7 ], groups: undefined ]
]*/
console.log(matchObj);

你看,设置完d标记后,多了一个indices的数组,里面就是匹配到的子字符串的范围了!

9. Object.hasOwn()方法

在JS中,我们可以使用Object.prototype.hasOwnProperty()来检查某个对象自身是否拥有某个属性:

class Car {color = 'green';age = 2;
}const car = new Car();console.log(car.hasOwnProperty('age')); // true
console.log(car.hasOwnProperty('name')); // false

上面的写法其实是有两个问题的。第一个问题是:Object.prototype.hasOwnProperty()这个方法是不受保护的,换句话来说就是它可以被某个类自定义的hasOwnProperty()方法覆盖掉,而自定义方法做的事情可能和Object.prototype.hasOwnProperty()做的事情完全不一样:

class Car {color = 'green';age = 2;// 你看这个方法就没有告诉我们这个类的对象是不是有某个属性hasOwnProperty() {return false;}
}const car = new Car();console.log(car.hasOwnProperty('age')); // false
console.log(car.hasOwnProperty('name')); // false

上面的写法第二个问题就是:当一个对象是通过Object.create(null)创建出来的具有null原型的对象时,你想在这个对象上面调用hasOwnProperty这个方法是会报错的:

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;// TypeError: obj.hasOwnProperty is not a function
console.log(obj.hasOwnProperty('color'));

解决这个问题的一种办法就是调用Object.prototype.hasOwnProperty这个Functioncall方法:

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;Object.prototype.hasOwnProperty.call(obj, 'color')

hasOwnProperty需要被多次调用的时候,我们可以通过将这部分逻辑抽象成一个方法来减少重复的代码:

function objHasOwnProp(obj, propertyKey) {return Object.prototype.hasOwnProperty.call(obj, propertyKey);
}const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;console.log(objHasOwnProp(obj, 'color')); // true
console.log(objHasOwnProp(obj, 'name')); // false

封装是封装了,不过看着好麻烦有木有?所以ES13诞生了一个全新的Object.hasOwn()函数来帮我们做上面这些重复的工作。这个新的内置函数接收两个参数,一个是对象,一个是属性,如果这个对象本身就有这个属性的话,这个函数就会返回true,否则就返回false

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;console.log(Object.hasOwn(obj, 'color')); // true
console.log(Object.hasOwn(obj, 'name')); // false

10. Error对象的Cause属性

ES13后,Error对象多了一个cause属性来指明错误出现的原因。这个属性可以帮助我们为错误添加更多的上下文信息,从而帮助使用者们更好地定位错误。这个属性是我们在创建error对象时传进去的第二个参数对象的cause属性:

function userAction() {try {apiCallThatCanThrow();} catch (err) {throw new Error('New error message', { cause: err });}
}try {userAction();
} catch (err) {console.log(err);console.log(`Cause by: ${err.cause}`);
}

11. 数组支持逆序查找

在JS中,我们可以使用数组的find()函数来在数组中找到第一个满足某个条件的元素。同样地,我们还可以通过findIndex()函数来返回这个元素的位置。可是,无论是find()还是findIndex(),它们都是从数组的头部开始查找元素的,可是在某些情况下,我们可能有从数组后面开始查找某个元素的需要。例如我们知道待查找的元素在比较靠后的位置,从后面开始寻找的话会有更好的性能,就像下面这个例子:

const letters = [{ value: 'v' },{ value: 'w' },{ value: 'x' },{ value: 'y' },{ value: 'z' },
];// 我们想要找的y元素比较靠后, 顺序查找性能不好
const found = letters.find((item) => item.value === 'y');
const foundIndex = letters.findIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }
console.log(foundIndex); // 3

在这种情况下使用find()findIndex()也可以,就是性能差点而已。ES13出来后,我们终于有办法处理这种情况了,那就是使用新的findLast()findLastIndex()函数。这两个函数都会从数组的末端开始寻找某个满足条件的元素:

const letters = [{ value: 'v' },{ value: 'w' },{ value: 'x' },{ value: 'y' },{ value: 'z' },
];// 后序查找一下子快了,有木有
const found = letters.findLast((item) => item.value === 'y');
const foundIndex = letters.findLastIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }
console.log(foundIndex); // 3

另外一种使用findLast()findLastIndex()的场景就是我们本身就是想要寻找最后一个满足某个条件的元素,例如找到数组里面最后一个偶数,这个时候还用find()findIndex()的话得到的结果是错误的:

const nums = [7, 14, 3, 8, 10, 9];// 返回了14, 结果应该是10才对
const lastEven = nums.find((value) => value % 2 === 0);// 返回了1, 结果应该是4才对
const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);
console.log(lastEven); // 14
console.log(lastEvenIndex); // 1

试想一下要得到正确的答案,我们还要使用find()findIndex()的话,要怎么改呢?首先我们需要用reverse()来反转数组,然后再调用find()findIndex()函数。不过这个做法有两个问题,一个问题就是需要改变原数组,这个问题可以通过拷贝数组来解决,占用空间多点而已。另一个问题是findIndex()得到索引后我们还要做一些额外的计算才能得到元素原数组的位置,具体做法是:

const nums = [7, 14, 3, 8, 10, 9];// 在调用reverse之前先拷贝函数,以防改变原数组
const reversed = [...nums].reverse();
// 这次返回对了,是10
const lastEven = reversed.find((value) => value % 2 === 0);
// findIndex得到的结果还是不对的
const reversedIndex = reversed.findIndex((value) => value % 2 === 0);
// 需要多一步计算才能得出正确结果
const lastEvenIndex = reversed.length - 1 - reversedIndex;
console.log(lastEven); // 10
console.log(reversedIndex); // 1
console.log(lastEvenIndex); // 4

看着的确麻烦,findLast()findLastIndex()出来后,数组就支持后序查找元素了,实现同样的需求,代码一下子简洁了不少:


const nums = [7, 14, 3, 8, 10, 9];const lastEven = nums.findLast((num) => num % 2 === 0);
const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);console.log(lastEven); // 10
console.log(lastEvenIndex); // 4

你看代码是不是短了很多,并且可读性和正确性都提高了!

ES13的11个超赞的新属性相关推荐

  1. 11 款超赞的 MySQL 图形化工具,好用!

    大家好,我是宝哥! MySQL 是一个非常流行的小型关系型数据库管理系统,2008年1月16号被Sun公司收购.目前 MySQL 被广泛地应用在中小型 网站中.由于其体积小.速度快.总体拥有成本低,尤 ...

  2. 11 款超赞的 MySQL 图形化管理工具,推荐收藏!

    公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! ​ MySQL 是一个非常流行的小型关系型数据库管理系统,2008年1月16号被Sun公司收购.目前 MySQL ...

  3. 分享29个超赞的响应式Web设计

    原文自:http://www.csdn.net/article/2013-01-16/2813678-responsive-design-websites 最近几年,响应式Web设计不断印入人们眼帘, ...

  4. 超赞网站推荐_字体(更多)超赞-标志性发明

    超赞网站推荐 by Pubudu Dodangoda 通过Pubudu Dodangoda 字体(更多)超赞-标志性发明 (Font (More) Awesome - an iconic invent ...

  5. 白嫖我常用的 11 个超火的前端必备在线工具,终于有时间上班摸鱼了

    大家好,我是你们的 猫哥,一个不喜欢吃鱼.又不喜欢喵 的超级猫 ~ 前言 猫哥是一个常年混迹在 GitHub 上的猫星人,所以发现了不少好的前端开源项目,在此分享给大家. 公众号:前端GitHub,专 ...

  6. Science上发表的超赞聚类算法

    论文. Clustering by fast search and find of density peak. Alex Rodriguez, Alessandro Laio 参考链接:Science ...

  7. Cloud一分钟 |百度遭北京信管局行政处罚;双11计算能力超100万核;腾讯回应高管被抓系谣言...

    Hello,everyone: 11月14日早,星期三,新的一天祝大家工作愉快! CSDN一分钟新闻时间: 百度遭北京信管局行政处罚 进入电信业务不良名单 相关部门表示:"后续相关公司如果再 ...

  8. PC版微信,也终于上线了这个超赞的功能

    微信有3个超赞的免打扰功能,能大大提升你微信使用效率. 一个是消息免打扰. 不管是群聊,还是单聊,都可以开启免打扰. 只要是给我发过广告的微商或类微商,我都开启免打扰.会清净一些. 另一个是「折叠置顶 ...

  9. 天猫、京东双11销售总额超8894亿!媒体:双11成交额崇拜可休矣

    5403亿!第13个天猫双11总交易额再创新高 2021年11月12日零点,一年一度的天猫双11落下帷幕.根据阿里巴巴公布的数据显示,2021年天猫双11总交易额定格在5403亿元,再次创下新高.相比 ...

最新文章

  1. 数学建模题目及论文_三道适合作为试题的数学建模题目及其评分标准
  2. ubuntu 12.04 联想thinkpad e430 安装wifi驱动
  3. 渲染树构建、布局及绘制
  4. Java序列化报错serialVersionUID不一致
  5. python自动化办公设置_python自动化办公之 python操作Excel
  6. 制作简单启动型 U 盘_附制作工具下载
  7. 关于UCINET软件使用计算密度的笔记
  8. vlan划分-通过物理接口实现vlan通信
  9. 用 RIME 定制输入法
  10. matlab ode 初值,关于ODE45初值问题和erf函数的问题
  11. (论文笔记)An Attention Enhanced Graph Convolutional LSTM Network for Skeleton-Based Action Recognition
  12. Python还在原地踏步?今天女友程序员教你函数的定义与使用
  13. HTTP协议详解你确定不看吗
  14. java16 新特性
  15. C#判断某一年是 “平年”||“闰年”,一月有多少天。
  16. tiny linux u盘_多系统U盘启动盘制作工具(YUMI)下载-多系统U盘启动盘制作工具(YUMI)PC版下载v2.0.7.6...
  17. MFC之图像绘制---高速绘图控件(High-speed Charting Control)应用(一)
  18. 消防vr虚拟救援模拟教学软件开发
  19. ImageMagick快速入门
  20. feawfwefwf

热门文章

  1. Android 组件化框架PPAP(直接公布源码) 绝了 感谢ARouter
  2. 江南天安环境的编译和使用(tassl)
  3. 学会使用IntelliJ IDEA 12 之教程二 (字体设置,光标设置)
  4. [东师培训D5T2] 公主的朋友
  5. 人工智能是不是走错了方向?
  6. php微信短网址生成,微信公众号实现长链接转短链接!w.url.cn短网址生成
  7. 【C51】STC单片机使用Keil 4编程配置
  8. python爬虫之手机模拟
  9. java-net-php-python-springboot易家养牛管理系统2020计算机毕业设计程序
  10. iPhone使用Itunes上传手机铃音