Inovasi JavaScript: hasil Google I / O 2019. Bagian 1

Materi, bagian pertama dari terjemahan yang kami terbitkan hari ini, dikhususkan untuk fitur JavaScript standar baru yang dibahas pada konferensi Google I / O 2019 . Secara khusus, di sini kita akan berbicara tentang ekspresi reguler, tentang bidang kelas, tentang bekerja dengan string.



Pemeriksaan Ekspresi Reguler


Ekspresi reguler (Ekspresi Reguler, singkatnya - RegEx atau RegExp) adalah teknologi pemrosesan string yang kuat yang diimplementasikan dalam banyak bahasa pemrograman. Ekspresi reguler sangat berguna dalam kasus-kasus di mana Anda perlu, misalnya, untuk mencari fragmen string dengan pola yang kompleks. Sampai saat ini, implementasi JavaScript dari ekspresi reguler memiliki segalanya kecuali melihat ke belakang.

Untuk memahami apa itu pemeriksaan retrospektif, mari kita bicara tentang lookaheads yang sudah didukung dalam JavaScript.

β†’ Bagian kedua

▍ Cek muka


Sintaks pemeriksaan terdepan dalam ekspresi reguler memungkinkan Anda untuk mencari fragmen string ketika diketahui bahwa fragmen lain berada di sebelah kanannya. Misalnya, saat bekerja dengan string MangoJuice, VanillaShake, GrapeJuice Anda dapat menggunakan sintaks dari pemeriksaan awal positif untuk menemukan kata yang segera diikuti oleh kata Juice . Dalam kasus kami, ini adalah kata Mango dan Grape .

Ada dua jenis cek terkemuka. Ini adalah lookaheads positif dan lookaheads negatif.

Pemeriksaan timah positif


Pemeriksaan awal positif digunakan untuk mencari garis di sebelah kanan yang merupakan garis lain yang sebelumnya diketahui. Berikut ini sintaks ekspresi reguler yang digunakan untuk pemeriksaan ini:

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

Template ini memungkinkan Anda untuk memilih kata-kata yang terdiri dari huruf kecil atau huruf besar, diikuti oleh kata Juice . Jangan bingung antara struktur yang menggambarkan pemeriksaan terkemuka dan retrospektif dengan kelompok tangkap. Meskipun kondisi pemeriksaan ini ditulis dalam tanda kurung, sistem tidak menangkapnya. Mari kita lihat contoh pemeriksaan timah positif.

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

Pemeriksaan Timbal Negatif


Jika kami mempertimbangkan, menggunakan baris di atas, mekanisme aksi pemeriksaan negatif, ternyata mereka memungkinkan Anda menemukan kata di sebelah kanan yang tidak ada kata Juice . Sintaks pemeriksaan awal negatif mirip dengan sintaks pemeriksaan positif. Namun, ada satu fitur di dalamnya, yaitu simbol = (sama) berubah menjadi simbol ! (tanda seru). Begini tampilannya:

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

Ekspresi reguler ini memungkinkan Anda memilih semua kata di sebelah kanan yang tidak ada kata Juice . Tetapi ketika menerapkan template seperti itu, semua kata dalam baris akan dipilih ( MangoJuice, VanillaShake, GrapeJuice ). Faktanya adalah bahwa, menurut sistem, tidak ada satu kata pun di sini yang berakhir dengan Juice . Akibatnya, untuk mencapai hasil yang diinginkan, Anda perlu memperjelas ekspresi reguler dan menulis ulang seperti ini:

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

Dengan menggunakan templat ini, Anda dapat memilih kata Mango , atau Vanilla , atau Grape , setelah itu tidak ada kata Juice . Berikut ini sebuah contoh:

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

▍ Pemeriksaan Retrospektif


Dengan analogi dengan sintaks pemeriksaan terkemuka, sintaks pemeriksaan retrospektif memungkinkan Anda untuk memilih urutan karakter hanya jika di sebelah kiri urutan ini adalah pola yang diberikan. Misalnya, saat memproses string FrozenBananas, DriedApples, FrozenFish kita dapat menggunakan pemeriksaan retrospektif positif untuk menemukan kata di sebelah kiri yang terdapat kata Frozen . Dalam kasus kami, kata Bananas dan Fish sesuai dengan kondisi ini.

Ada, seperti halnya dengan pemeriksaan terkemuka, pemeriksaan retrospektif positif (terlihat positif di belakang) dan pemeriksaan retrospektif negatif (terlihat negatif atau negatif).

Ulasan retrospektif positif


Pemeriksaan retrospektif positif digunakan untuk mencari pola di sebelah kiri yang merupakan pola lainnya. Berikut adalah contoh sintaks yang digunakan untuk menggambarkan pemeriksaan tersebut:

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

Simbol < digunakan di sini, yang tidak dalam deskripsi pemeriksaan awal. Selain itu, kondisi dalam ekspresi reguler terletak tidak di sebelah kanan templat yang menarik bagi kami, tetapi di sebelah kiri. Menggunakan templat di atas, Anda dapat memilih semua kata yang dimulai dengan Frozen . Pertimbangkan sebuah contoh:

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

Pemeriksaan Retrospektif Negatif


Mekanisme pemeriksaan retrospektif negatif memungkinkan Anda mencari pola pada garis di sebelah kiri yang tidak ada pola tertentu. Misalnya, jika Anda perlu memilih kata-kata yang tidak dimulai dengan Frozen di FrozenBananas, DriedApples, FrozenFish line, Anda dapat mencoba menggunakan ekspresi reguler ini:

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

Tapi, karena menggunakan konstruksi ini akan mengarah pada pemilihan semua kata dari string, karena tidak ada yang dimulai dengan Frozen , ekspresi reguler perlu diklarifikasi:

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

Berikut ini sebuah contoh:

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

β†’ Dukungan


Bagian ini dan bagian serupa lainnya akan memberikan informasi tentang tahap harmonisasi fitur yang dijelaskan JS dalam Komite Teknis 39 (Komite Teknis 39, TC39), yang bertanggung jawab dalam ECMA Internasional untuk mendukung spesifikasi ECMAScript. Bagian tersebut juga akan memberikan data tentang versi Chrome dan Node.js (dan terkadang pada versi Firefox), dimulai dengan mana Anda dapat menggunakan fitur yang sesuai.


Bidang kelas


Bidang kelas adalah konstruk sintaksis baru yang digunakan untuk mendefinisikan properti instance kelas (objek) di luar konstruktor kelas. Ada dua jenis bidang kelas: bidang kelas publik dan bidang kelas pribadi.

Fields Bidang kelas publik


Sampai saat ini, sifat-sifat objek harus didefinisikan di dalam konstruktor kelas. Properti ini bersifat publik (publik). Ini berarti bahwa mereka dapat diakses dengan bekerja dengan instance kelas (objek). Berikut adalah contoh menyatakan properti publik:

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

Ketika itu perlu untuk membuat kelas yang akan memperpanjang kelas induk tertentu, perlu untuk memanggil super() di konstruktor kelas anak. Ini harus dilakukan sebelum propertinya dapat ditambahkan ke kelas anak. Begini tampilannya:

 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! 

Berkat kemunculan sintaks bidang publik suatu kelas, dimungkinkan untuk menggambarkan bidang kelas di luar konstruktor. Sistem akan membuat panggilan implisit ke super() .

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

Ketika secara implisit memanggil super() , semua argumen yang disediakan oleh pengguna saat membuat instance kelas diteruskan ke dalamnya (ini adalah perilaku standar JavaScript, tidak ada yang istimewa tentang bidang kelas privat). Jika konstruktor dari kelas induk membutuhkan argumen yang disiapkan dengan cara khusus, Anda perlu memanggil super() sendiri. Lihatlah hasil panggilan konstruktor implisit dari kelas induk saat membuat turunan dari kelas anak.

 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!' ] 

▍ Bidang kelas pribadi


Seperti yang Anda ketahui, dalam JavaScript tidak ada pengubah akses ke bidang kelas seperti public , private , atau protected . Semua properti objek bersifat publik secara default. Ini berarti bahwa akses ke mereka tidak terbatas. Hal terdekat untuk membuat properti dari objek yang mirip dengan properti pribadi adalah dengan menggunakan tipe data Symbol . Ini memungkinkan Anda untuk menyembunyikan properti objek dari dunia luar. Anda mungkin menggunakan nama properti yang diawali dengan _ (garis bawah) untuk menunjukkan bahwa properti yang sesuai harus dianggap dimaksudkan hanya untuk digunakan dalam objek. Namun, ini hanya semacam pemberitahuan bagi mereka yang akan menggunakan fasilitas ini. Ini tidak memecahkan masalah pembatasan nyata akses ke properti.

Berkat mekanisme bidang privat kelas, dimungkinkan untuk membuat properti kelas hanya dapat diakses di dalam kelas ini. Ini mengarah pada fakta bahwa mereka tidak dapat diakses dari luar dan bekerja dengan instance kelas (objek). Ambil contoh sebelumnya dan coba akses properti kelas dari luar, ketika awalan _ digunakan.

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

Seperti yang Anda lihat, menggunakan _ awalan tidak menyelesaikan masalah kami. Bidang pribadi kelas dapat dideklarasikan dengan cara yang sama dengan bidang publik, tetapi alih-alih awalan dalam bentuk garis bawah, Anda harus menambahkan awalan dalam bentuk tanda pound ( # ) ke nama mereka. Upaya akses tidak sah ke properti pribadi objek yang dinyatakan dengan cara ini akan menghasilkan kesalahan berikut:

 SyntaxError: Undefined private field 

Berikut ini sebuah contoh:

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

Perhatikan bahwa properti pribadi hanya dapat diakses dari kelas di mana mereka dinyatakan. Akibatnya, kelas turunan tidak dapat langsung menggunakan properti serupa dari kelas induk.

Bidang pribadi (dan publik) dapat dideklarasikan tanpa menuliskan nilai tertentu ke dalamnya:

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

β†’ Dukungan



Metode String .matchAll ()


Prototipe tipe data String memiliki metode .match() yang mengembalikan array fragmen string yang cocok dengan kondisi yang ditentukan oleh ekspresi reguler. Berikut ini contoh menggunakan metode ini:

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

Ketika menggunakan metode ini, bagaimanapun, tidak ada informasi tambahan yang diberikan (seperti indeks) tentang fragmen string yang ditemukan. Jika Anda menghapus flag g dari ekspresi reguler yang dilewatkan ke metode .match() , itu akan mengembalikan array yang akan berisi informasi tambahan tentang hasil pencarian. Namun, dengan pendekatan ini, hanya fragmen pertama dari string yang cocok dengan ekspresi reguler yang akan ditemukan.

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

Untuk mendapatkan sesuatu yang serupa, tetapi untuk beberapa bagian string, Anda harus menggunakan metode ekspresi reguler .exec() . Konstruksi yang diperlukan untuk ini lebih rumit daripada yang di mana metode string tunggal akan digunakan untuk mendapatkan hasil yang serupa. Secara khusus, di sini kita memerlukan while yang akan mengeksekusi sampai .exec() mengembalikan null . Menggunakan pendekatan ini, perlu diingat bahwa .exec() tidak mengembalikan iterator.

 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>"] 

Untuk mengatasi masalah tersebut, kita sekarang dapat menggunakan metode string .matchAll() , yang mengembalikan iterator. Setiap panggilan ke metode .next() dari iterator ini .next() elemen berikutnya dari hasil pencarian. Hasilnya, contoh di atas dapat ditulis ulang sebagai berikut:

 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>"] 

β†’ Dukungan



Grup Bernama dalam Ekspresi Reguler


Konsep grup dalam implementasi JavaScript dari mekanisme ekspresi reguler sedikit berbeda dari implementasi konsep serupa dalam bahasa lain. Yaitu, ketika menggunakan JavaScript, template RegEx ditempatkan dalam tanda kurung (kecuali ketika tanda kurung digunakan untuk pemeriksaan retrospektif atau lanjutan), templat menjadi grup.

Fragmen string yang ditangkap oleh grup akan tercermin dalam hasil penerapan ekspresi reguler.

Pada contoh sebelumnya, Anda bisa melihat bahwa elemen pertama array dengan hasil pencarian adalah elemen yang cocok dengan seluruh ekspresi reguler, dan yang kedua adalah elemen yang sesuai dengan grup. Berikut ini elemen array ini:

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

Jika ada beberapa grup dalam ekspresi reguler, maka mereka akan masuk ke hasil pemrosesan string dalam urutan deskripsi mereka dalam ekspresi reguler. Pertimbangkan sebuah contoh:

 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"} 

Di sini Anda dapat melihat bahwa baris pertama dari output adalah seluruh baris yang sesuai dengan ekspresi reguler. Elemen kedua dan ketiga mewakili apa yang ditangkap oleh kelompok.

Menggunakan grup yang diberi nama memungkinkan Anda untuk menyimpan apa yang ditangkap groups objek groups , yang nama propertinya bersesuaian dengan nama yang ditetapkan untuk grup.

 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"} 

Perlu dicatat bahwa grup yang diberi nama berfungsi dengan baik bersama dengan metode .matchAll() .

β†’ Dukungan



Dilanjutkan ...

Pembaca yang budiman! Sudahkah Anda menggunakan salah satu inovasi JavaScript yang dijelaskan di sini?

Source: https://habr.com/ru/post/id464129/


All Articles