JavaScript创新:Google I / O 2019成绩。第1部分

该材料是我们今天要发布的翻译的第一部分,专门介绍了在Google I / O 2019大会上讨论的新标准JavaScript功能。 特别是在这里,我们将讨论正则表达式,类字段和字符串处理。



正则表达式检查


正则表达式(正则表达式,简称RegEx或RegExp)是一种强大的字符串处理技术,已通过多种编程语言实现。 在需要例如通过复杂模式搜索字符串片段的情况下,正则表达式非常有用。 直到最近,正则表达式的JavaScript实现还具有回溯功能。

为了了解什么是追溯检查,让我们首先讨论一下JavaScript中已经支持的先行。

第二部分

▍预先检查


正则表达式中的前导检查语法使您可以在已知其他片段右边的情况下搜索字符串的片段。 例如,当使用字符串MangoJuice, VanillaShake, GrapeJuice您可以使用肯定前导检查的语法来查找紧随其后的单词Juice 。 在我们的案例中,这些是MangoGrape

领导检查有两种类型。 这些是正面的前瞻性和负面的前瞻性。

正面铅检查


正引导检查用于搜索右边的其他先前已知行。 这是用于此检查的正则表达式语法如下所示:

 /[a-zA-Z]+(?=Juice)/ 

该模板允许您选择由小写或大写字母组成的单词,然后选择单词Juice 。 不要将描述领导和追溯检查的结构与捕获组混淆。 尽管这些检查的条件写在括号中,但系统不会捕获它们。 让我们看一个正面铅检查的例子。

 const testString = "MangoJuice, VanillaShake, GrapeJuice"; const testRegExp = /[a-zA-Z]+(?=Juice)/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Mango", "Grape"] 

负铅检查


如果我们使用上述内容考虑否定超前检查的作用机制,那么事实证明,它们使您可以在右边没有单词Juice找到单词。 否定领先检查的语法与肯定检查的语法相似。 但是,其中有一个功能,即符号= (等于)变为符号! (感叹号)。 看起来是这样的:

 /[a-zA-Z]+(?!Juice)/ 

此正则表达式使您可以选择右边没有单词Juice所有单词。 但是,当应用此类模板时,将选择该行中的所有单词( MangoJuice, VanillaShake, GrapeJuice )。 事实是,根据系统,此处没有一个单词以Juice结尾。 结果,为了获得所需的结果,您需要澄清正则表达式并像这样重写它:

 /(Mango|Vanilla|Grape)(?!Juice)/ 

使用此模板,您可以选择“ Mango ,“ Vanilla ”或“ Grape一词,之后没有“ Juice一词。 这是一个例子:

 const testString = "MangoJuice, VanillaShake, GrapeJuice"; const testRegExp = /(Mango|Vanilla|Grape)(?!Juice)/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Vanilla"] 

▍追溯检查


与前导检查的语法类似,追溯检查的语法仅在字符序列的左侧是给定模式时才允许您选择字符序列。 例如,当处理字符串FrozenBananas, DriedApples, FrozenFish我们可以使用肯定的追溯检查在单词的左侧找到单词Frozen 。 在我们的案例中,“ BananasFish ”一词对应于这种情况。

与前导检查一样,正面回顾性检查(正向后看)和负面回顾性检查(负向或负向后看)也是如此。

积极回顾性审查


积极回顾性检查用于搜索左侧的其他模式。 这是用于描述此类检查的语法示例:

 /(?<=Frozen)[a-zA-Z]+/ 

此处使用符号< ,但领导检查的描述中未使用该符号。 另外,正则表达式中的条件不是位于我们感兴趣的模板的右侧,而是位于左侧。 使用以上模板,您可以选择所有以Frozen开头的单词。 考虑一个例子:

 const testString = "FrozenBananas, DriedApples, FrozenFish"; const testRegExp = /(?<=Frozen)[a-zA-Z]+/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Bananas", "Fish"] 

负追溯检查


否定性追溯检查机制使您可以在没有指定模式的左侧的行中搜索模式。 例如,如果需要在FrozenBananas, DriedApples, FrozenFish行中选择不以Frozen开头的FrozenBananas, DriedApples, FrozenFish可以尝试使用以下正则表达式:

 /(?<!Frozen)[a-zA-Z]+/ 

但是,由于使用此构造将导致从字符串中选择所有单词,因为它们都不以Frozen开头,因此需要澄清正则表达式:

 /(?<!Frozen)(Bananas|Apples|Fish)/ 

这是一个例子:

 const testString = "FrozenBananas, DriedApples, FrozenFish"; const testRegExp = /(?<!Frozen)(Bananas|Apples|Fish)/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Apples"] 

→支持


本部分和其他类似部分将提供有关协调技术委员会39(技术委员会39,TC39)中描述的JS功能的阶段的信息,该委员会由ECMA International负责支持ECMAScript规范。 这些部分还将提供有关Chrome和Node.js版本(有时甚至是Firefox版本)的数据,从这些数据开始,您可以使用相应的功能。


类字段


类字段是一种新的语法构造,用于定义类构造函数外部的类实例(对象)的属性。 类字段有两种类型:公共类字段和私有类字段。

class公共课领域


直到最近,还必须在类构造函数中定义对象的属性。 这些属性是公共的(public)。 这意味着可以通过使用类(对象)的实例来访问它们。 这是一个声明公共财产的例子:

 class Dog {    constructor() {        this.name = 'Tommy';    } } 

当需要创建将扩展某个父类的类时,有必要在子类的构造函数中调用super() 。 必须先完成此操作,然后才能将其自身的属性添加到子类中。 看起来是这样的:

 class Animal {} class Dog extends Animal {    constructor() {        super(); //  super   `this`          this.sound = 'Woof! Woof!';    }    makeSound() {        console.log( this.sound );    } } //    const tommy = new Dog(); tommy.makeSound(); // Woof! Woof! 

由于类的公共字段语法的出现,因此可以在构造函数之外描述类字段。 系统将隐式调用super()

 class Animal {} class Dog extends Animal {    sound = 'Woof! Woof!'; //       makeSound() {        console.log( this.sound );    } } //    const tommy = new Dog(); tommy.makeSound(); // Woof! Woof! 

当隐式调用super() ,用户在创建类实例时提供的所有参数都将传递给它(这是标准的JavaScript行为,私有类字段没有什么特别的)。 如果父类的构造函数需要以特殊方式准备的参数,则需要自己调用super() 。 在创建子类的实例时,请看一下父类的隐式构造函数调用的结果。

 class Animal {    constructor( ...args ) {        console.log( 'Animal args:', args );    } } class Dog extends Animal {    sound = 'Woof! Woof!'; //    makeSound() {        console.log( this.sound );    } } //    const tommy = new Dog( 'Tommy', 'Loves', 'Toys!' ); tommy.makeSound(); // Animal args: [ 'Tommy', 'Loves', 'Toys!' ] 

▍私人课


如您所知,在JavaScript中,没有对类字段(如publicprivateprotected访问修饰符。 默认情况下,对象的所有属性都是公共的。 这意味着对它们的访问不受限制。 使对象的属性类似于私有属性最接近的事情是使用Symbol数据类型。 这使您可以隐藏外界的对象属性。 您可能已经使用带有_ (下划线)前缀的属性名称来表示应将相应的属性视为仅打算在对象内使用。 但是,对于使用该设施的人来说,这只是一种通知。 这不能解决实际限制访问属性的问题。

由于类的私有字段的机制,可以使类属性只能在该类内部访问。 这导致无法从外部访问它们并且无法使用类(对象)的实例进行访问。 以前面的示例为例,尝试使用_前缀时从外部访问该类的属性。

 class Dog {    _sound = 'Woof! Woof!'; //            makeSound() {        console.log( this._sound );    } } //    const tommy = new Dog(); console.log( tommy._sound ); // Woof! Woof! 

如您所见,使用_前缀不能解决我们的问题。 可以使用与公共字段相同的方式声明类的私有字段,但是必须在其名称中添加井号( # )形式的前缀,而不是下划线形式的前缀。 尝试以这种方式未经授权访问对象的私有属性将导致以下错误:

 SyntaxError: Undefined private field 

这是一个例子:

 class Dog {    #sound = 'Woof! Woof!'; //  -      makeSound() {        console.log( this.#sound );    } } //    const tommy = new Dog(); tommy.makeSound() // Woof! Woof! //console.log( tommy.#sound ); // SyntaxError 

请注意,只能从声明私有属性的类中访问私有属性。 结果,后代类不能直接使用父类的相似属性。

可以声明私有(和公共)字段,而无需在其中写入某些值:

 class Dog {    #name;    constructor( name ) {        this.#name = name;    }    showName() {        console.log( this.#name );    } } //    const tommy = new Dog( 'Tommy' ); tommy.showName(); // Tommy 

→支持


  • TC39: 阶段3
  • 铬:74+
  • 节点:12岁以上

字符串方法.matchAll()


String数据类型原型具有.match()方法,该方法返回与正则表达式指定的条件匹配的字符串片段数组。 这是使用此方法的示例:

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /([A-Z0-9]+)/g; console.log( colors.match( matchColorRegExp ) ); // : ["EEE", "CCC", "FAFAFA", "F00", "000"] 

但是,使用此方法时,不会提供有关找到的字符串片段的其他信息(如索引)。 如果从传递给.match()方法的正则表达式中删除g标志,它将返回一个数组,其中包含有关搜索结果的其他信息。 但是,使用这种方法,只会找到与正则表达式匹配的字符串的第一个片段。

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /#([A-Z0-9]+)/; console.log( colors.match( matchColorRegExp ) ); // : (       ) ["#EEE", "EEE", index: 0, input: "<colors>"] 

为了获得相似的东西,但是对于一个字符串的多个片段,您将必须使用正则表达式方法.exec() 。 所需的结构比使用单字符串方法获得相似结果的结构更为复杂。 特别是在这里,我们需要一个while ,该while将一直执行到.exec()返回null为止。 使用这种方法时,请记住.exec()不会返回迭代器。

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /#([A-Z0-9]+)/g; //        , // Uncaught ReferenceError: match is not defined while( match = matchColorRegExp.exec( colors ) ) {  console.log( match ); } // : (       ) ["#EEE", "EEE", index: 0, input: "<colors>"] ["#CCC", "CCC", index: 6, input: "<colors>"] ["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"] ["#F00", "F00", index: 21, input: input: "<colors>"] ["#000", "000", index: 27, input: input: "<colors>"] 

为了解决这些问题,我们现在可以使用字符串方法.matchAll() ,该方法返回一个迭代器。 对该迭代器的.next()方法的每次调用都会.next()搜索结果中.next()下一个元素。 结果,上面的示例可以重写如下:

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /#([A-Z0-9]+)/g; console.log( ...colors.matchAll( matchColorRegExp ) ); // : (       ) ["#EEE", "EEE", index: 0, input: "<colors>"] ["#CCC", "CCC", index: 6, input: "<colors>"] ["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"] ["#F00", "F00", index: 21, input: input: "<colors>"] ["#000", "000", index: 27, input: input: "<colors>"] 

→支持


  • TC39: 第4阶段
  • 铬: 73+
  • 节点:12岁以上
  • Firefox:67岁以上

正则表达式中的命名组


正则表达式机制的JavaScript实现中的组概念与其他语言中类似概念的实现略有不同。 即,当使用JavaScript将RegEx模板放在括号中时(括号用于追溯或高级检查时除外),该模板将成为一个组。

该组捕获的字符串的片段将反映在应用正则表达式的结果中。

在上一个示例中,您可以看到带有搜索结果的数组的第一个元素是与整个正则表达式匹配的元素,第二个是与组相对应的元素。 这是这个数组元素:

 ["#EEE", "EEE", index: 0, input: "<colors>"] 

如果正则表达式中有多个组,那么它们将按照在正则表达式中描述的顺序进入处理字符串的结果。 考虑一个例子:

 const str = "My name is John Doe."; const matchRegExp = /My name is ([az]+) ([az]+)/i; const result = str.match( matchRegExp );console.log( result ); //   result  null -   console.log( { firstName: result[1], lastName: result[2] } ); // : ["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: undefined] {firstName: "John", lastName: "Doe"} 

在这里,您可以看到输出的第一行是与正则表达式相对应的整行。 第二和第三个元素代表了小组所捕获的内容。

使用命名组可以保存在groups对象中捕获的groups ,这些对象的属性名称与分配给组的名称相对应。

 const str = "My name is John Doe."; const matchRegExp = /My name is (?<firstName>[az]+) (?<lastName>[az]+)/i; const result = str.match( matchRegExp ); console.log( result ); console.log( result.groups ); // : ["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: {firstName: "John", lastName: "Doe"}] {firstName: "John", lastName: "Doe"} 

应该注意的是,命名组可以与.matchAll()方法配合使用。

→支持



待续...

亲爱的读者们! 您是否使用过此处介绍的任何JavaScript创新?

Source: https://habr.com/ru/post/zh-CN464129/


All Articles