Apa yang Baru di ES6? Perspektif dari Konversi CoffeeScript

Diterbitkan: 2022-03-11

Saya telah menjadi penggemar CoffeeScript selama lebih dari dua tahun sekarang. Saya merasa saya lebih produktif menulis CoffeeScript, membuat kesalahan yang tidak terlalu konyol, dan dengan dukungan untuk peta sumber, debugging kode CoffeeScript benar-benar tidak menyakitkan.

Apa yang Baru di ES6? Perspektif dari Konversi CoffeeScript

Baru-baru ini saya bermain dengan ES6 menggunakan Babel, dan secara keseluruhan saya adalah penggemarnya. Dalam artikel ini, saya akan mengkompilasi temuan saya di ES6 dari perspektif konversi CoffeeScript, melihat hal-hal yang saya sukai dan melihat hal-hal apa yang masih saya lewatkan.

Indentasi Signifikan Secara Sintaksis: Anugerah atau Kutukan?

Saya selalu memiliki sedikit hubungan cinta/benci dengan lekukan yang signifikan secara sintaksis dari CoffeeScript. Ketika semuanya berjalan dengan baik, Anda mendapatkan "Lihat ma, tidak ada tangan!" hak membual menggunakan sintaksis super-minimalis seperti itu. Tetapi ketika ada yang salah dan lekukan yang salah menyebabkan bug nyata (percayalah, itu terjadi), rasanya manfaatnya tidak berharga.

 if iWriteCoffeeScript if iAmNotCareful badThings = 'happen'

Biasanya bug ini disebabkan ketika Anda memiliki blok kode dengan beberapa pernyataan bersarang, dan Anda secara tidak sengaja membuat indentasi salah satu pernyataan. Ya itu biasanya disebabkan oleh mata yang lelah, tetapi itu lebih mungkin terjadi di CoffeeScript menurut saya.

Ketika saya mulai menulis ES6, saya tidak yakin apa reaksi naluri saya nantinya. Ternyata rasanya sangat menyenangkan untuk mulai menggunakan kurung kurawal lagi. Menggunakan editor modern juga membantu, karena umumnya Anda hanya perlu membuka kurung kurawal dan editor Anda cukup baik untuk menutupnya untuk Anda. Rasanya seperti kembali ke alam semesta yang tenang dan jernih setelah keajaiban voodoo CoffeeScript.

 if (iWriteES6) { if (iWriteNestedStatements) { let badThings = 'are less likely to happen' } }

Tentu saja saya bersikeras membuang titik koma. Jika kita tidak membutuhkannya, saya katakan buang saja. Saya menemukan mereka jelek dan itu mengetik ekstra.

Dukungan Kelas

Dukungan kelas di ES6 luar biasa, dan jika Anda pindah dari CoffeeScript, Anda akan merasa seperti di rumah sendiri. Mari kita bandingkan sintaks antara keduanya dengan contoh sederhana:

Kelas ES6

 class Animal { constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs } toString() { return `I am an animal with ${this.numberOfLegs} legs` } } class Monkey extends Animal { constructor(bananas) { super(2) this.bananas = bananas } toString() { let superString = super.toString() .replace(/an animal/, 'a monkey') return `${superString} and ${this.bananas} bananas` } }

Kelas CoffeeScript

 class Animal constructor: (@numberOfLegs) -> toString: -> "I am an animal with #{@numberOfLegs} legs" class Monkey extends Animal constructor: (@numberOfBananas) -> super(2) toString: -> superString = super.toString() .replace(/an animal/, 'a monkey') "#{superString} and #{@numberOfLegs} bananas"

Kesan pertama

Hal pertama yang mungkin Anda perhatikan adalah bahwa ES6 masih jauh lebih bertele-tele daripada CoffeeScript. Salah satu pintasan yang sangat berguna di CoffeeScript adalah dukungan untuk penetapan otomatis variabel instan di konstruktor:

 constructor: (@numberOfLegs) ->

Ini setara dengan yang berikut di ES6:

 constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs }

Tentu saja itu bukan akhir dunia, dan sintaks yang lebih eksplisit ini lebih mudah dibaca. Hal kecil lainnya yang hilang adalah bahwa kami tidak memiliki pintasan @ untuk this , yang selalu terasa menyenangkan berasal dari latar belakang Ruby, tetapi sekali lagi bukan pemecah masalah besar. Secara umum kami merasa cukup betah di sini, dan saya sebenarnya lebih suka sintaks ES6 untuk mendefinisikan metode.

Bagaimana Super!

Ada masalah kecil dengan cara super ditangani di ES6. CoffeeScript menangani super dengan cara Ruby ("mohon kirim pesan ke metode superclass dengan nama yang sama"), tetapi satu-satunya waktu Anda dapat menggunakan super "telanjang" di ES6 adalah di konstruktor. ES6 mengikuti pendekatan seperti Java yang lebih konvensional di mana super adalah referensi ke instance superclass.

Aku akan kembali

Di CoffeeScript kita dapat menggunakan pengembalian implisit untuk membangun kode yang indah seperti berikut:

 foo = -> if Math.random() > 0.5 if Math.random() > 0.5 if Math.random() > 0.5 "foo"

Di sini Anda memiliki peluang yang sangat kecil untuk mendapatkan "foo" kembali ketika Anda memanggil foo() . ES6 tidak memiliki semua ini dan memaksa kami untuk kembali menggunakan kata kunci return :

 const foo = () => { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { return "foo" } } } }

Seperti yang dapat kita lihat pada contoh di atas, ini umumnya merupakan hal yang baik, tetapi saya masih menemukan diri saya lupa untuk menambahkannya dan mendapatkan pengembalian tidak terdefinisi yang tidak terduga di semua tempat! Ada satu pengecualian yang saya temui, mengenai Fungsi Panah, di mana Anda mendapatkan pengembalian implisit untuk satu liner seperti ini:

 array.map( x => x * 10 )

Ini agak berguna tetapi bisa sedikit membingungkan karena Anda membutuhkan return jika Anda menambahkan kurung kurawal:

 array.map( x => { return x * 10 })

Namun, itu masih masuk akal. Alasannya adalah jika Anda telah menambahkan kurung kurawal itu karena Anda ingin menggunakan banyak baris, dan jika Anda memiliki banyak baris, masuk akal untuk memperjelas apa yang Anda kembalikan.

Bonus Sintaks Kelas ES6

Kita telah melihat bahwa CoffeeScript memiliki beberapa trik sintaksis dalam hal mendefinisikan kelas, tetapi ES6 juga memiliki beberapa triknya sendiri.

Getter dan Setter

ES6 memiliki dukungan kuat untuk enkapsulasi melalui getter dan setter, seperti yang diilustrasikan dalam contoh berikut:

 class BananaStore { constructor() { this._bananas = [] } populate() { // fetch our bananas from the backend here } get bananas() { return this._bananas.filter( banana => banana.isRipe ) } set bananas(bananas) { if (bananas.length > 100) { throw `Wow ${bananas.length} is a lot of bananas!` } this._bananas = bananas } }

Berkat getter, jika kita mengakses bananaStore.bananas itu hanya akan mengembalikan pisang matang. Ini sangat bagus karena dalam CoffeeScript kita perlu mengimplementasikan ini melalui metode pengambil seperti bananaStore.getBananas() . Ini sama sekali tidak terasa alami ketika dalam JavaScript kita terbiasa mengakses properti secara langsung. Itu juga membuat pengembangan membingungkan karena kita perlu memikirkan untuk setiap properti bagaimana kita harus mengaksesnya - apakah itu .bananas atau getBananas() ?

Setter sama bergunanya karena kita dapat membersihkan kumpulan data atau bahkan membuang pengecualian ketika data yang tidak valid disetel, seperti yang ditunjukkan pada contoh mainan di atas. Ini akan sangat akrab bagi siapa saja dari latar belakang Ruby.

Saya pribadi tidak berpikir ini berarti Anda harus menjadi gila menggunakan getter dan setter untuk semuanya. Jika Anda dapat memperoleh sesuatu dengan mengenkapsulasi variabel instan Anda melalui getter dan setter (seperti pada contoh di atas), lanjutkan dan lakukan. Tetapi bayangkan Anda hanya memiliki flag boolean seperti isEditable . Jika Anda tidak akan kehilangan apa pun dengan secara langsung mengekspos properti isEditable , saya berpendapat itu adalah pendekatan terbaik, karena ini yang paling sederhana.

Metode statis

ES6 memiliki dukungan untuk metode statis, yang memungkinkan kami melakukan trik nakal ini:

 class Foo { static new() { return new this } }

Sekarang jika Anda benci menulis new Foo() Anda sekarang dapat menulis Foo.new() . Purists akan menggerutu karena new adalah kata yang dicadangkan, tetapi setelah pengujian yang sangat cepat tampaknya berfungsi di Node.js dan dengan browser modern baik-baik saja. Tetapi Anda mungkin tidak ingin menggunakan ini dalam produksi!

Sayangnya karena tidak ada dukungan untuk properti statis di ES6, jika Anda ingin mendefinisikan konstanta kelas dan mengaksesnya dalam metode statis Anda, Anda harus melakukan sesuatu seperti ini, yang sedikit meretas:

 class Foo { static imgPath() { return `${this.ROOT_PATH}/img` } } Foo.ROOT_PATH = '/foo'

Ada Proposal ES7 untuk mengimplementasikan instance deklaratif dan properti kelas, sehingga kita dapat melakukan sesuatu seperti ini, yang akan menyenangkan:

 class Foo { static ROOT_PATH = '/foo' static imgPath() { return `${this.ROOT_PATH}/img` } }

Ini sudah bisa dilakukan dengan cukup elegan di CoffeeScript:

 class Foo @ROOT_PATH: '/foo' @imgPath: -> @ROOT_PATH

Interpolasi String

Waktunya telah tiba ketika kita akhirnya bisa melakukan interpolasi string dalam Javascript!

 `I am so happy that in the year ${new Date().getFullYear()} we can interpolate strings`

Berasal dari CoffeeScript/Ruby, sintaks ini terasa sedikit yuk. Mungkin karena latar belakang Ruby saya di mana backtick digunakan untuk menjalankan perintah sistem, awalnya terasa sangat salah. Juga menggunakan tanda dolar terasa seperti tahun delapan puluhan – tapi mungkin itu hanya saya.

Saya kira karena masalah kompatibilitas mundur, itu tidak mungkin untuk mengimplementasikan interpolasi string gaya CoffeeScript. "What a #{expletive} shame" .

Fungsi Panah

Fungsi panah diterima begitu saja di CoffeeScripter. ES6 cukup banyak mengimplementasikan sintaks panah gemuk dari CoffeeScript. Jadi kita mendapatkan sintaks pendek yang bagus, dan kita mendapatkan cakupan leksikal this , jadi kita tidak perlu melewati rintangan seperti ini saat menggunakan ES5:

 var that = this doSomethingAsync().then( function(res) { that.foo(res) })

Setara di ES6 adalah:

 doSomethingAsync().then( res => { this.foo(res) })

Perhatikan bagaimana saya meninggalkan tanda kurung di sekitar panggilan balik unary, karena saya bisa!

Literal Objek pada Steroid

Literal objek di ES6 memiliki beberapa peningkatan kinerja yang serius. Mereka dapat melakukan segala macam hal hari ini!

Nama Properti Dinamis

Saya tidak benar-benar menyadari sampai menulis artikel ini bahwa sejak CoffeeScript 1.9.1 kita sekarang dapat melakukan ini:

 dynamicProperty = 'foo' obj = {"#{dynamicProperty}": 'bar'}

Yang jauh lebih menyakitkan daripada sesuatu seperti ini yang diperlukan sebelumnya:

 dynamicProperty = 'foo' obj = {} obj[dynamicProperty] = 'bar'

ES6 memiliki sintaks alternatif yang menurut saya cukup bagus, tetapi bukan kemenangan besar:

 let dynamicProperty = 'foo' let obj = { [dynamicProperty]: 'bar' }

Pintasan Funky

Ini sebenarnya diambil dari CoffeeScript, tapi itu adalah sesuatu yang saya tidak tahu sampai sekarang. Bagaimanapun, ini sangat berguna:

 let foo = 'foo' let bar = 'bar' let obj = { foo, bar }

Sekarang isi obj adalah { foo: 'foo', bar: 'bar' } . Itu sangat berguna, dan patut diingat.

Semua yang Dapat Dilakukan Kelas Saya Juga Dapat Melakukannya!

Literal objek sekarang dapat melakukan hampir semua hal yang dapat dilakukan kelas, bahkan sesuatu seperti:

 let obj = { _foo: 'foo', get foo() { return this._foo }, set foo(str) { this._foo = str }, isFoo() { return this.foo === 'foo' } }

Tidak yakin mengapa Anda ingin mulai melakukan itu, tetapi hei, sekarang Anda bisa! Anda tidak dapat mendefinisikan metode statis tentu saja ... karena itu tidak masuk akal sama sekali.

For-loop untuk Membingungkan Anda

Sintaks for-loop ES6 akan memperkenalkan beberapa bug memori otot yang bagus untuk CoffeeScripter berpengalaman, karena for-of ES6 diterjemahkan menjadi for-in CoffeeSCript, dan sebaliknya.

ES6

 for (let i of [1, 2, 3]) { console.log(i) } // 1 // 2 // 3

KopiScript

 for i of [1, 2, 3] console.log(i) # 0 # 1 # 2

Haruskah Saya Beralih ke ES6?

Saat ini saya sedang mengerjakan proyek Node.js yang cukup besar menggunakan 100% CoffeeScript, dan saya masih sangat senang dan sangat produktif dengannya. Saya akan mengatakan bahwa satu-satunya hal yang saya benar-benar iri di ES6 adalah getter dan setter.

Juga masih sedikit menyakitkan menggunakan ES6 dalam praktik hari ini. Jika Anda dapat menggunakan versi Node.js terbaru, maka Anda mendapatkan hampir semua fitur ES6, tetapi untuk versi yang lebih lama dan di browser hal-hal masih kurang cerah. Ya, Anda dapat menggunakan Babel, tetapi tentu saja itu berarti mengintegrasikan Babel ke dalam tumpukan Anda.

Setelah mengatakan bahwa saya dapat melihat ES6 selama satu atau dua tahun ke depan mendapatkan banyak landasan, dan saya berharap untuk melihat hal-hal yang lebih besar di ES7.

Apa yang saya sangat senang dengan ES6 adalah bahwa "JavaScript tua biasa" hampir ramah dan kuat out-of-the-box sebagai CoffeeScript. Ini bagus untuk saya sebagai pekerja lepas – di masa lalu saya agak enggan mengerjakan proyek JavaScript – tetapi dengan ES6 semuanya tampak sedikit lebih berkilau.