申明:本文为翻译文章,原文为11 Amazing New JavaScript Features in ES13
像其余语言一样,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); // blueconsole.log(car.age); // 2
诚实说这么写起来挺不不便的,不过ES13进去之后,这都不算什么事儿了。当初咱们终于能够冲破这个限度,写上面这样的代码了:
class Car { color = 'blue'; age = 2;}const car = new Car();console.log(car.color); // blueconsole.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); // Josephconsole.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 classconsole.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 functionsawait 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()); // trueconsole.log(car.hasColor.call(house)); // falseconsole.log(house.hasColor()); // trueconsole.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
个元素时,咱们只须要传入-N
给at()
即可:
const arr = ['a', 'b', 'c', 'd'];// 倒数第一个元素console.log(arr.at(-1)); // d// 倒数第二个元素console.log(arr.at(-2)); // c
你看,你的代码是不是一下子表意多了!除了数组,string
和TypedArray
对象也反对at()
函数哦!
const str = 'Coding Beauty';console.log(str.at(-1)); // yconsole.log(str.at(-2)); // tconst typedArray = new Uint8Array([16, 32, 48, 64]);console.log(typedArray.at(-1)); // 64console.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')); // trueconsole.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')); // falseconsole.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 functionconsole.log(obj.hasOwnProperty('color'));
解决这个问题的一种方法就是调用Object.prototype.hasOwnProperty
这个Function
的call
办法:
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')); // trueconsole.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')); // trueconsole.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); // 14console.log(lastEvenIndex); // 1
试想一下要失去正确的答案,咱们还要应用find()
和findIndex()
的话,要怎么改呢?首先咱们须要用reverse()
来反转数组,而后再调用find()
和findIndex()
函数。不过这个做法有两个问题,一个问题就是须要扭转原数组,这个问题能够通过拷贝数组来解决,占用空间多点而已。另一个问题是findIndex()
失去索引后咱们还要做一些额定的计算能力失去元素原数组的地位,具体做法是:
const nums = [7, 14, 3, 8, 10, 9];// 在调用reverse之前先拷贝函数,以防扭转原数组const reversed = [...nums].reverse();// 这次返回对了,是10const 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); // 10console.log(reversedIndex); // 1console.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); // 10console.log(lastEvenIndex); // 4
你看代码是不是短了很多,并且可读性和正确性都进步了!
论断
下面咱们介绍了ES13最新的11个属性。作为一个开发者,咱们能够应用它们来进步本人的生产效率和编写更加简洁和表意的代码,你还不连忙在我的项目外面实际一把?
集体技术动静
关注我的公众号 - 进击的大葱获取我的最新技术推送