免责声明:本文为翻译文章,原文为ES13JavaScript的11个惊人新特性和其他语言一样,JavaScript也在不断迭代和进化。JS每年都会加入很多新的特性,让自己变得更加强大,而也正是这样,我们开发者才能写出更具表现力和准确度的代码。在本文中,我们将通过一些示例来了解最新的ECMAScript2022(ES13)为我们的开发人员带来的11项令人敬畏的新功能。1.类成员声明在ES13之前,我们只能在构造函数中声明类成员,而不能像大多数其他语言那样在类的最外层作用域中声明:classCar{constructor(){this.颜色='蓝色';这个年龄=2;}}constcar=newCar();console.log(car.color);//blueconsole.log(car.age);//2说实话挺不方便的,不过ES13出来后,也没什么大不了的。现在我们可以突破这个限制,写出这样的代码:classCar{color='blue';age=2;}constcar=newCar();console.log(car.color);//蓝色控制台。日志(车龄);//22.在ES13定义类的私有方法和成员变量之前,我们不可能为类定义私有成员。所以有些程序员为了表明成员变量是私有属性,在属性名后面加上下划线(_)作为后缀。但这只能作为程序员之间的君子协定,因为这些变量还是可以被外界访问到的。类人{_firstName='约瑟夫';_lastName='史蒂文斯';getname(){return`${this._firstName}${this._lastName}`;}}constperson=newPerson();console.log(person.name);//JosephStevens//这些所谓的私有属性其实是可以被外界访问的console.log(person._firstName);//Josephconsole.log(person._lastName);//Stevens//也可以改成person._firstName='Robert';person._lastName='Becker';console.log(person.name);//RobertBecker但ES13出来后,妈妈再也不用担心我们的私有属性被别人访问和更改了。在ES13中,我们只需要在我们的属性名称前加上井号(#),属性就变成了私有的。当我们的财产成为私有财产时,任何外部访问都将出错。类人{#firstName='约瑟夫';#lastName='史蒂文斯';获取姓名(){返回`${this.#firstName}${this.#lastName}`;}}constperson=newPerson();console.log(person.name);//SyntaxError:Privatefield'#firstName'mustbedeclaredinanenclosingclassconsole.log(person.#firstName);控制台日志(人。#lastName);值得一读这里提到,上面提到的SyntaxError是编译时抛出的错误,所以你不会等到你的代码运行起来才知道这个属性被非法访问。3.支持最外层写await。我们都知道在JS中,await操作符的作用是,当我们遇到一个promise时,我们可以使用await暂停当前代码的执行,直到promise被解决(fulfilled或rejected),我们继续执行该promise当前代码。但是,之前使用await的时候,有一个很头疼的问题。它必须在异步函数中使用,而不是在全局范围内使用。如果这样写,会报错:functionsetTimeoutAsync(timeout){returnnewPromise((resolve)=>{setTimeout(()=>{resolve();},timeout);});}//SyntaxError:await只在异步函数中有效awaitsetTimeoutAsync(3000);ES13出来之后就舒服多了,我们终于可以这样写代码了:;})}//慢慢等待时间GoaheadawaitsetTimeoutAsync(3000);4、类支持定义静态成员和静态私有方法在ES13中,我们还可以为类定义静态成员和静态私有函数。类的静态方法可以使用this关键字访问其他私有或公共静态成员,类的实例方法可以通过this.constructor.classPerson{static#count=0;访问这些静态属性。staticgetCount(){返回这个。#count;}constructor(){this.constructor.#incrementCount();}static#incrementCount(){这个。#count++;}}constperson1=newPerson();constperson2=newPerson();console.log(Person.getCount());//25.类支持定义静态代码块ES13允许通过static关键字在类中定义一系列静态代码块,这些代码块只会在类创建时执行一次。这其实有点像C#、Java等其他一些面向对象编程语言中静态构造函数的用法。一个类可以定义任意数量的静态代码块,这些代码块连同穿插在它们之间的静态成员变量,会在类初始化时按照定义的顺序执行一次。我们还可以使用super关键字来访问父类的属性。classVehicle{staticdefaultColor='blue';}classCarextendsVehicle{staticcolors=[];static{this.colors.push(super.defaultColor,'red');}static{this.colors.push('green');}console.log(Car.colors);['blue','red','green']}6.用in判断一个对象是否有私有属性这个新属性的名字其实是ErgonomicBrandChecksforPrivateFields,原谅我的无知,我真的不知道怎么翻译,就大概表达一下它的作用。一般用于判断一个对象是否具有特定的私有属性,由in运算符判断。类汽车{#color;hasColor(){在此返回#color;}}constcar=newCar();console.log(car.hasColor());//truethisin运算符甚至可以区分不同类的同名私有属性:classCar{#color;hasColor(){在此返回#color;}}classHouse{#color;hasColor(){在此返回#color;}}constcar=newCar();consthouse=newHouse();console.log(car.hasColor());//trueconsole.log(car.hasColor.call(house));//falseconsole.log(house.hasColor());//trueconsole.log(house.hasColor.call(car));//错误7。使用at函数对元素进行索引一般来说,如果我们要访问数组的第N个元素,我们会使用方括号[N-1]:constarr=['a','b','c','d'];控制台日志(arr[1]);//b大部分时候是这样写的,没有问题,其他语言也是这样写的但是当我们需要访问数组的最后第N个元素时,我们的代码就变得有点奇怪了:constarr=['a','b','c','d'];//倒数第一个元素console.log(arr[arr.length-1]);//d//倒数第二个元素console.log(arr[arr.length-2]);//c你这样看你的代码是不是突然变丑了?别怕,ES13的at()函数帮你写出更优雅的代码!使用新的new()方法,当我们要访问最后第N个元素时,只需要将-N传递给at()即可:constarr=['a','b','c','d'];//第一个最后一个元素console.log(arr.at(-1));//d//最后一个元素第二个元素console.log(arr.at(-2));//看看c,你的代码是不是一下子表达了更多的意思!除了数组,string和TypedArray对象也支持at()函数!conststr='编码之美';console.log(str.at(-1));//yconsole.log(str.at(-2));//tconsttypedArray=newUint8Array([16,32,48,64]);console.log(typedArray.at(-1));//64console.log(typedArray.at(-2));//488.正则表达式匹配字符串时,支持返回起止索引。简单地说,这个新属性允许我们告诉RegExp在返回匹配对象时返回匹配子串的开始和结束索引。在ES13之前,我们只能得到正则表达式匹配到的子串的起始索引:conststr='sunandmoon';常量正则表达式=/和/;constmatchObj=regex.exec(str);//['and',index:4,input:'sunandmoon',groups:undefined]console.log(matchObj);ES13之后,我们可以在正则表达式中加上d标记,让它给我们返回匹配子串的起始位置和结束位置:conststr='sunandmoon';const正则表达式=/and/d;constmatchObj=regex.exec(str);/**['and',index:4,input:'sunandmoon',groups:undefined,indices:[[4,7],groups:undefined]]*/控制台日志(匹配对象);你看,设置了d标志后,多了一个索引数组,就是匹配子串的范围!9.Object.hasOwn()方法在JS中,我们可以使用Object.prototype.hasOwnProperty()来检查一个对象本身是否拥有一个属性:classCar{color='green';age=2;}constcar=newCar();console.log(car.hasOwnProperty('age'));//trueconsole.log(car.hasOwnProperty('name'));//false上面的写法其实有两个问题。第一个问题是:Object.prototype.hasOwnProperty()方法不受保护。换句话说,它可以被类定义的hasOwnProperty()方法覆盖,而自定义方法所做的可能与Object.prototype.hasOwnProperty()所做的完全不同:classCar{color='green';年龄=2;//你看,这个方法并没有告诉我们这个类的对象是否有某个属性hasOwnProperty(){returnfalse;}}constcar=newCar();console.log(car.hasOwnProperty('age'));//falseconsole.log(car.hasOwnProperty('name'));//falseabove第二个问题是:当一个对象通过Object.create(null)创建,原型为null时,如果要调用这个对象的hasOwnProperty方法,会报错:constobj=Object.创建(空);obj.color='绿色';obj.age=2;//类型错误:obj.hasOwnProperty不是函数console.log(obj.hasOwnProperty('color'));解决这个问题的一种方法是调用FunctionObject.prototype.hasOwnProperty的调用方法:constobj=Object.create(null);obj.color='绿色';obj.age=2;obj.hasOwnProperty=()=>false;对象.原型。hasOwnProperty.call(obj,'color')当hasOwnProperty需要被多次调用时,我们可以将这部分逻辑抽象成一个方法来减少重复代码:functionobjHasOwnProp(obj,propertyKey){returnObject.prototype.hasOwnProperty.call(obj,propertyKey);}constobj=Object.create(null);obj.color='green';obj.age=2;obj.hasOwnProperty=()=>false;console.log(objHasOwnProp(obj,'颜色'));//trueconsole.log(objHasOwnProp(obj,'name'));//false包被打包了,但是看起来好麻烦。于是ES13诞生了一个全新的Object.hasOwn()函数来帮我们做上面重复的工作。这个新的内置函数接收两个参数,一个是对象,另一个是属性。如果对象本身有这个属性,这个函数会返回true,否则会返回false:constobj=Object.create(null);obj.color='绿色';obj.age=2;obj.hasOwnProperty=()=>false;console.log(Object.hasOwn(obj,'color'));//trueconsole.log(Object.hasOwn(obj,'name'));//错误10。在ES13的Error对象的Cause属性之后,Error对象还有一个cause属性来表示错误发生的原因。该属性可以帮助我们为错误添加更多的上下文信息,从而帮助用户更好地定位错误。这个属性就是我们在创建错误对象时传入的第二个参数对象的cause属性:functionuserAction(){try{apiCallThatCanThrow();}catch(err){thrownewError('新错误信息',{cause:err});}}try{userAction();}catch(err){console.log(err);console.log(`Causeby:${err.cause}`);}11.数组支持反向搜索在JS中,我们可以使用数组的find()函数来查找数组中第一个满足一定条件的元素。同样,我们也可以使用findIndex()函数来返回这个元素的位置。但是不管是find()还是findIndex(),都是从数组头部开始查找元素,但是在某些情况下,我们可能需要从数组后面查找元素。比如我们知道要查找的元素在后面的位置,从后面开始查找性能会更好,如下例:constletters=[{value:'v'},{value:'w'},{value:'x'},{value:'y'},{value:'z'},];//我们要找的y元素比较晚,顺序查找性能不好constfound=letters.find((item)=>item.value==='y');constfoundIndex=letters.findIndex((item)=>item.value==='y');控制台日志(找到);//{value:'y'}console.log(foundIndex);//3这种情况下,也可以使用find()和findIndex(),但是性能较差。ES13出来后,我们终于有了办法处理这种情况,那就是使用新的findLast()和findLastIndex()函数。这两个函数会从数组的末尾开始寻找满足条件的元素:constletters=[{value:'v'},{value:'w'},{value:'x'},{value:'y'},{value:'z'},];//后序查找很快,所以constfound=letters.findLast((item)=>item.value==='y');constfoundIndex=letters.findLastIndex((item)=>item.value==='y');控制台日志(找到);//{value:'y'}console.log(foundIndex);//3more使用findLast()和findLastIndex()的一种场景是,我们要找到满足某个条件的最后一个元素,比如找到数组中的最后一个偶数。这时候我们也可以用find()和findIndex()得到结果是错误的:constnums=[7,14,3,8,10,9];//返回了14,结果应该是10constlastEven=nums.find((value)=>value%2===0);//返回1,结果应该是4constlastEvenIndex=nums.findIndex((value)=>value%2===0);console.log(lastEven);//14console.log(lastEvenIndex);//1想象一下要得到正确答案,如果我们还需要用到find()和findIndex(),我们应该怎么改呢?首先我们需要使用reverse()来反转数组,然后调用find()和findIndex()函数。然而,这种方法有两个问题。一个问题是需要更改原始数组。这个问题可以通过复制数组来解决,只是占用空间更大。另一个问题是,在findIndex()得到索引后,我们还需要做一些额外的计算才能得到原始数组元素的位置。具体方法是:constnums=[7,14,3,8,10,9];//在调用reverse之前复制函数,防止改变原数组constreversed=[...nums].reverse();//这次返回正确,是10constlastEven=reversed.find((value)=>value%2===0);//findIndex的结果还是错误的constreversedIndex=reversed.findIndex((value)=>value%2===0);//需要多一步计算才能得到正确的结果constlastEvenIndex=reversed.length-1-reversedIndex;console.log(lastEven);//10console.log(reversedIndex);//1console.log(lastEvenIndex);//4看着真麻烦,findLast()和findLastIndex()出来后,数组支持后序查找元素。实现同样的需求,代码一下子就简单了很多:constnums=[7,14,3,8,10,9];constlastEven=nums.findLast((num)=>num%2===0);constlastEvenIndex=nums.findLastIndex((num)=>num%2===0);console.log(lastEven);//10console.log(lastEvenIndex);//4有没有看到代码短了很多,可读性和正确性都提高了!结语上面我们介绍了ES13最新的11个属性。作为开发人员,我们可以利用它们来提高我们的生产效率,写出更简洁、更有表现力的代码。不如赶紧在项目中实践一下?关注我的公众号个人技术动态——进击的大葱得到我的最新技术推送
