ES6'daki Yenilikler Neler? Bir CoffeeScript Dönüştürme Perspektifi

Yayınlanan: 2022-03-11

İki yıldan fazla bir süredir CoffeeScript hayranıyım. CoffeeScript yazarken daha üretken olduğumu, daha az aptalca hata yaptığımı ve CoffeeScript kodunda hata ayıklama kaynak haritaları desteğiyle tamamen ağrısız olduğumu düşünüyorum.

ES6'daki Yenilikler Neler? Bir CoffeeScript Dönüştürme Perspektifi

Son zamanlarda Babel kullanarak ES6 ile oynuyorum ve genel olarak bir hayranıyım. Bu makalede, bir CoffeeScript dönüştürme perspektifinden ES6 ile ilgili bulgularımı derleyeceğim, sevdiğim şeylere bakacağım ve hala neleri kaçırdığımı göreceğim.

Sözdizimsel Olarak Önemli Girinti: Bir Nimet mi, Bir Felaket mi?

CoffeeScript'in sözdizimsel olarak anlamlı girintisiyle her zaman biraz sevgi/nefret ilişkim oldu. Her şey yolunda gittiğinde "Bak anne, eller yok!" Böyle süper-minimalist bir sözdizimi kullanmanın övünme hakları. Ancak işler ters gittiğinde ve yanlış girinti gerçek hatalara neden olduğunda (inan bana, olur), faydaların değerli olduğunu hissetmez.

 if iWriteCoffeeScript if iAmNotCareful badThings = 'happen'

Genellikle bu hatalar, iç içe birkaç ifade içeren bir kod bloğunuz olduğunda ve yanlışlıkla bir ifadeyi yanlış girintiye aldığınızda ortaya çıkar. Evet, genellikle yorgun gözlerden kaynaklanır, ancak bence CoffeeScript'te olması çok daha olası.

ES6 yazmaya başladığımda, içgüdüsel tepkimin ne olacağından pek emin değildim. Görünüşe göre tekrar kaşlı ayraçları kullanmaya başlamak gerçekten çok güzelmiş . Modern bir düzenleyici kullanmak da yardımcı olur, çünkü genellikle yalnızca küme ayracı açmanız yeterlidir ve düzenleyiciniz sizin için kapatmak için yeterince naziktir. CoffeeScript'in vudu büyüsünden sonra sakin, berrak bir evrene geri dönmek gibi geldi.

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

Tabii ki noktalı virgülleri atmakta ısrar ediyorum. İhtiyacımız yoksa atalım diyorum. Onları çirkin buluyorum ve fazladan yazıyor.

Sınıf Desteği

ES6'daki sınıf desteği harika ve CoffeeScript'ten taşınıyorsanız, kendinizi evinizde hissedeceksiniz. İkisi arasındaki sözdizimini basit bir örnekle karşılaştıralım:

ES6 Sınıfı

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

CoffeeScript Sınıfı

 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"

İlk izlenimler

Fark edebileceğiniz ilk şey, ES6'nın CoffeeScript'ten çok daha ayrıntılı olduğudur. CoffeeScript'te çok kullanışlı olan bir kısayol, yapıcıda örnek değişkenlerinin otomatik olarak atanmasına yönelik destektir:

 constructor: (@numberOfLegs) ->

Bu, ES6'da aşağıdakine eşdeğerdir:

 constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs }

Tabii ki bu gerçekten dünyanın sonu değil ve eğer bir şey varsa, bu daha açık sözdizimini okumak daha kolaydır. Eksik olan bir başka küçük şey de, bunun için @ kısayolumuz olmamasıdır; this , Ruby arka planından gelmek her zaman iyi hissettirir, ancak yine de büyük bir anlaşma kırıcı değildir. Genel olarak burada kendimizi evimizde gibi hissediyoruz ve aslında yöntemleri tanımlamak için ES6 sözdizimini tercih ediyorum.

Ne kadar Süper!

ES6'da super işlenme şekliyle ilgili küçük bir sorun var. CoffeeScript super Ruby yolunda işler ("lütfen aynı adı taşıyan üst sınıf yöntemine bir mesaj gönderin"), ancak ES6'da "çıplak" bir super kullanabileceğiniz tek zaman yapıcıdır. ES6, super'in super sınıf örneğine bir referans olduğu daha geleneksel Java benzeri bir yaklaşımı izler.

Geri döneceğim

CoffeeScript'te, aşağıdaki gibi güzel kodlar oluşturmak için örtük dönüşleri kullanabiliriz:

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

Burada foo() 'u aradığınızda çok küçük bir "foo" alma şansınız var. ES6 bunların hiçbirine sahip değil ve bizi return anahtar sözcüğünü kullanarak geri dönmeye zorluyor:

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

Yukarıdaki örnekte gördüğümüz gibi, bu genellikle iyi bir şey, ama yine de eklemeyi unuttuğumu ve her yerde beklenmedik tanımsız geri dönüşler aldığımı görüyorum! Karşılaştığım, bunun gibi bir gömlek için örtülü bir dönüş elde ettiğiniz Ok İşlevleri ile ilgili bir istisna var:

 array.map( x => x * 10 )

Bu biraz kullanışlıdır, ancak küme parantezlerini eklerseniz return ihtiyacınız olduğu için biraz kafa karıştırıcı olabilir:

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

Ancak, yine de mantıklı. Bunun nedeni, küme parantezlerini eklediyseniz, bunun nedeni, birden çok satır kullanmak istemenizdir ve birden çok satırınız varsa, ne döndürdüğünüzün net olması mantıklıdır.

ES6 Sınıfı Sözdizimi Bonusları

Konu sınıfları tanımlamaya geldiğinde CoffeeScript'in birkaç sözdizimsel püf noktası olduğunu gördük, ancak ES6'nın da kendine ait birkaç püf noktası var.

Alıcılar ve Ayarlayıcılar

ES6, aşağıdaki örnekte gösterildiği gibi alıcılar ve ayarlayıcılar aracılığıyla kapsülleme için güçlü bir desteğe sahiptir:

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

Alıcı sayesinde bananaStore.bananas , yalnızca olgun muzları döndürür. Bu, CoffeeScript'te olduğu gibi gerçekten harika, bunu bananaStore.getBananas() gibi bir alıcı yöntemiyle uygulamamız gerekecek. JavaScript'te özelliklere doğrudan erişmeye alıştığımızda bu hiç doğal gelmiyor. Ayrıca geliştirmeyi kafa karıştırıcı hale getirir çünkü her bir özellik için ona nasıl erişmemiz gerektiğini düşünmemiz gerekir - bu .bananas yoksa getBananas() mı?

Ayarlayıcı, yukarıdaki oyuncak örneğinde gösterildiği gibi, veri kümesini temizleyebildiğimiz ve hatta geçersiz veriler ayarlandığında bir istisna oluşturabildiğimiz için eşit derecede yararlıdır. Bu, Ruby geçmişine sahip herkese çok tanıdık gelecektir.

Şahsen bunun, her şey için alıcıları ve ayarlayıcıları kullanarak çıldırmanız gerektiği anlamına geldiğini düşünmüyorum. Örnek değişkenlerinizi alıcılar ve ayarlayıcılar (yukarıdaki örnekte olduğu gibi) aracılığıyla kapsülleyerek bir şeyler elde edebiliyorsanız, devam edin ve yapın. Ancak isEditable gibi bir boole bayrağınız olduğunu hayal edin. isEditable özelliğini doğrudan açığa çıkararak hiçbir şey kaybetmezseniz, en basit olduğu için bunun en iyi yaklaşım olduğunu iddia ederim.

statik yöntemler

ES6, bu yaramaz numarayı yapmamızı sağlayan statik yöntemler için desteğe sahiptir:

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

Şimdi new Foo() yazmaktan nefret ediyorsanız, şimdi Foo.new() yazabilirsiniz. new ayrılmış bir kelime olduğu için saflar homurdanacaklar, ancak çok hızlı bir testten sonra Node.js'de ve modern tarayıcılarda gayet iyi çalışıyor gibi görünüyor. Ama muhtemelen bunu üretimde kullanmak istemezsiniz!

Ne yazık ki, ES6'da statik özellikler için destek olmadığı için, sınıf sabitlerini tanımlamak ve onlara statik yönteminizde erişmek istiyorsanız, bunun gibi bir şey yapmanız gerekir, bu biraz tuhaftır:

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

Bildirime dayalı örnek ve sınıf özelliklerini uygulamak için bir ES7 Önerisi var, böylece böyle bir şey yapabiliriz, bu iyi olurdu:

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

Bu, CoffeeScript'te zaten oldukça zarif bir şekilde yapılabilir:

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

dize enterpolasyonu

Sonunda Javascript'te string enterpolasyonu yapabileceğimiz zaman geldi!

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

CoffeeScript/Ruby'den gelen bu sözdizimi biraz ağır geliyor. Belki de sistem komutlarını yürütmek için bir backtick'in kullanıldığı Ruby geçmişim nedeniyle, ilk başta oldukça yanlış geliyor. Dolar işaretini kullanmak da seksenleri andırıyor – ama belki de bu sadece benim.

Sanırım geriye dönük uyumluluk endişeleri nedeniyle CoffeeScript stili dize enterpolasyonunu uygulamak mümkün değildi. "What a #{expletive} shame" .

Ok Fonksiyonları

CoffeeScripter'da ok işlevleri verilmiş kabul edilir. ES6, CoffeeScript'in şişman ok sözdizimini hemen hemen uyguladı. Böylece güzel bir kısa sözdizimi elde ederiz ve sözcüksel olarak kapsamını alırız this , böylece ES5 kullanırken böyle çemberler arasında atlamamız gerekmez:

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

ES6'daki eşdeğeri:

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

Tekli geri aramanın etrafındaki parantezleri nasıl dışarıda bıraktığıma dikkat edin, çünkü yapabilirim!

Steroidlerdeki Nesne Değişmezleri

ES6'daki nesne değişmezleri bazı ciddi performans geliştirmelerine sahiptir. Bugünlerde her türlü şeyi yapabilirler!

Dinamik Özellik Adları

Aslında bu makaleyi yazana kadar CoffeeScript 1.9.1'den beri bunu yapabileceğimizi bilmiyordum:

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

Daha önce gerekli olan bunun gibi bir şeyden çok daha az acı verici olan:

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

ES6'nın oldukça güzel olduğunu düşündüğüm alternatif bir sözdizimi var, ancak büyük bir kazanç değil:

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

Eğlenceli Kısayollar

Bu aslında CoffeeScript'ten alınmıştır, ancak şimdiye kadar cahil olduğum bir şeydi. Her halükarda çok faydalı:

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

Şimdi obj'nin içeriği { foo: 'foo', bar: 'bar' } . Bu çok faydalı ve hatırlamaya değer.

Bir Sınıfın Yapabildiği Her Şeyi Ben de Yapabilirim!

Nesne değişmezleri artık bir sınıfın yapabileceği hemen hemen her şeyi yapabilir, hatta şunun gibi bir şey:

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

Bunu neden yapmaya başlamak istediğinizden tam olarak emin değilsiniz ama hey, şimdi yapabilirsiniz! Tabii ki statik yöntemler tanımlayamazsınız… çünkü bu hiç mantıklı olmaz.

Kafanızı Karıştıracak For-döngüler

ES6 for-loop sözdizimi, deneyimli CoffeeScripters için bazı güzel kas hafızası hataları sunacak, çünkü ES6'nın for-of'u CoffeeSCript'in for-in'i anlamına gelir ve bunun tersi de geçerlidir.

ES6

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

Kahve Komut Dosyası

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

ES6'ya Geçmeli miyim?

Şu anda %100 CoffeeScript kullanan oldukça büyük bir Node.js projesi üzerinde çalışıyorum ve bununla hâlâ çok mutlu ve süper üretkenim. ES6'da gerçekten kıskandığım tek şeyin alıcılar ve ayarlayıcılar olduğunu söyleyebilirim.

Ayrıca bugün pratikte ES6 kullanmak hala biraz acı verici. En son Node.js sürümünü kullanabiliyorsanız, neredeyse tüm ES6 özelliklerine sahip olursunuz, ancak eski sürümler ve tarayıcıda işler hala daha az pembedir. Evet, Babel'i kullanabilirsiniz, ancak elbette bu, Babel'i yığınınıza entegre etmek anlamına gelir.

Önümüzdeki bir veya iki yıl içinde ES6'nın çok fazla zemin kazandığını görebileceğimi söyledikten sonra ve ES7'de daha da büyük şeyler görmeyi umuyorum.

ES6'dan gerçekten memnun olduğum şey, “düz eski JavaScript”in neredeyse CoffeeScript kadar kolay ve güçlü olması. Bu, bir serbest çalışan olarak benim için harika – geçmişte JavaScript projeleri üzerinde çalışmaktan biraz hoşlanmazdım – ama ES6 ile her şey biraz daha parlak görünüyor.