Kaynak Kodu Okuyarak JavaScript Bilginizi Geliştirin

Yayınlanan: 2022-03-10
Kısa özet ↬ Programlama kariyerinizin henüz başlarındayken, açık kaynak kitaplıkların ve çerçevelerin kaynak kodunu araştırmak göz korkutucu bir çaba olabilir. Bu yazıda Carl Mungazi, korkusunu nasıl yendiğini ve bilgi ve becerilerini geliştirmek için kaynak kodu kullanmaya başladığını paylaşıyor. Ayrıca bir kütüphaneyi parçalamaya nasıl yaklaştığını göstermek için Redux'u kullanıyor.

Sık kullandığınız bir kitaplığın veya çerçevenin kaynak kodunu ilk kez derinlemesine incelediğinizi hatırlıyor musunuz? Benim için o an, üç yıl önce bir ön uç geliştirici olarak ilk işim sırasında geldi.

E-öğrenme kursları oluşturmak için kullandığımız eski bir iç çerçeveyi yeniden yazmayı yeni bitirmiştik. Yeniden yazmanın başlangıcında, Mithril, Inferno, Angular, React, Aurelia, Vue ve Polymer dahil olmak üzere bir dizi farklı çözümü araştırmak için zaman harcadık. Daha çok acemi olduğum için (gazetecilikten web geliştirmeye yeni geçmiştim), her bir çerçevenin karmaşıklığından korktuğumu ve her birinin nasıl çalıştığını anlamadığımı hatırlıyorum.

Seçtiğimiz çerçeve Mithril'i daha derinlemesine araştırmaya başladığımda anlayışım arttı. O zamandan beri, iş yerinde veya kendi projelerimde her gün kullandığım kitaplıkların derinliklerine inmek için harcadığım saatler, JavaScript ve genel olarak programlama bilgime büyük ölçüde yardımcı oldu. Bu yazıda, favori kütüphanenizi veya çerçevenizi alıp bir eğitim aracı olarak kullanmanın bazı yollarını paylaşacağım.

Mithril'in hiper simge işlevi için kaynak kodu
Kod okumaya ilk girişim Mithril'in hiper simge işlevi aracılığıyla oldu. (Büyük önizleme)
Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Kaynak Kodu Okumanın Faydaları

Kaynak kodu okumanın en büyük faydalarından biri, öğrenebileceğiniz şeylerin sayısıdır. Mithril'in kod tabanına ilk baktığımda, sanal DOM'un ne olduğu hakkında belirsiz bir fikrim vardı. Bitirdiğimde, sanal DOM'nin, kullanıcı arabiriminizin nasıl görünmesi gerektiğini tanımlayan bir nesne ağacı oluşturmayı içeren bir teknik olduğu bilgisine ulaştım. Bu ağaç daha sonra document.createElement gibi DOM API'leri kullanılarak DOM öğelerine dönüştürülür. Güncellemeler, kullanıcı arabiriminin gelecekteki durumunu açıklayan yeni bir ağaç oluşturularak ve ardından eski ağaçtan nesnelerle karşılaştırılarak gerçekleştirilir.

Bunların hepsini çeşitli makalelerde ve eğitimlerde okumuştum ve yardımcı olsa da, gönderdiğimiz bir uygulama bağlamında iş yerinde gözlemleyebilmek benim için çok aydınlatıcı oldu. Ayrıca farklı çerçeveleri karşılaştırırken hangi soruları sormam gerektiğini de öğretti. Örneğin GitHub yıldızlarına bakmak yerine, "Her çerçevenin güncellemeleri gerçekleştirme şekli performansı ve kullanıcı deneyimini nasıl etkiler?" gibi sorular sormayı biliyordum.

Başka bir fayda, iyi uygulama mimarisini takdir etme ve anlamada bir artıştır. Çoğu açık kaynak projesi genellikle depolarıyla aynı yapıyı takip ederken, her biri farklılıklar içerir. Mithril'in yapısı oldukça düzdür ve API'sine aşina iseniz, render , router ve request gibi klasörlerdeki kod hakkında doğru tahminlerde bulunabilirsiniz. Öte yandan, React'in yapısı yeni mimarisini yansıtıyor. Bakımcılar, UI güncellemelerinden sorumlu modülü ( react-reconciler -reconciler) DOM öğelerini oluşturmaktan sorumlu modülden ( react-dom ) ayırdı.

Bunun faydalarından biri, geliştiricilerin react-reconciler paketine bağlanarak kendi özel oluşturucularını yazmalarının artık daha kolay olmasıdır. Son zamanlarda üzerinde çalıştığım bir modül paketleyici olan Parcel, React gibi bir packages klasörüne de sahip. Anahtar modül parcel-bundler olarak adlandırılır ve demetler oluşturmaktan, etkin modül sunucusunu çalıştırmaktan ve komut satırı aracından sorumlu kodu içerir.

JavaScript belirtiminin Object.prototype.toString'in nasıl çalıştığını açıklayan bölümü
Okumakta olduğunuz kaynak kodun sizi JavaScript spesifikasyonuna yönlendirmesi uzun sürmeyecektir. (Büyük önizleme)

Benim için hoş bir sürpriz olarak gelen bir başka fayda da, dilin nasıl çalıştığını tanımlayan resmi JavaScript spesifikasyonunu okurken daha rahat olmanızdır. Spesifikasyonu ilk okuduğumda, throw Error throw new Error arasındaki farkı araştırıyordum (spoiler uyarısı - hiçbiri yok). Bunu araştırdım çünkü Mithril'in m işlevinin uygulanmasında throw Error kullandığını fark ettim ve onu throw new Error üzerinde kullanmanın bir yararı olup olmadığını merak ettim. O zamandan beri, mantıksal operatörlerin && ve || mutlaka boolean döndürmez, == eşitlik operatörünün değerleri nasıl zorladığını yöneten kuralları ve Object.prototype.toString.call({}) öğesinin '[object Object]' döndürmesinin nedenini buldu.

Kaynak Kodu Okuma Teknikleri

Kaynak koduna yaklaşmanın birçok yolu vardır. Başlamanın en kolay yolunun, seçtiğiniz kitaplıktan bir yöntem seçmek ve onu çağırdığınızda ne olduğunu belgelemek olduğunu buldum. Her adımı belgelemeyin, genel akışını ve yapısını belirlemeye çalışın.

Bunu yakın zamanda ReactDOM.render ile yaptım ve sonuç olarak React Fiber ve uygulamasının arkasındaki bazı nedenler hakkında çok şey öğrendim. Neyse ki React popüler bir framework olduğu için aynı konuda diğer geliştiriciler tarafından yazılmış birçok makaleye rastladım ve bu süreci hızlandırdı.

Bu derin dalış beni aynı zamanda işbirlikli zamanlama kavramları, window.requestIdleCallback yöntemi ve bağlantılı listelerin gerçek dünyadan bir örneğiyle tanıştırdı (React, güncellemeleri, önceliklendirilmiş güncellemelerin bağlantılı bir listesi olan bir kuyruğa koyarak işler). Bunu yaparken kütüphaneyi kullanarak çok temel bir uygulama oluşturmanız tavsiye edilir. Bu, diğer kitaplıkların neden olduğu yığın izleriyle uğraşmak zorunda kalmadığınız için hata ayıklamayı kolaylaştırır.

Derinlemesine inceleme yapmıyorsam, üzerinde çalıştığım bir projede /node_modules klasörünü açarım veya GitHub deposuna giderim. Bu genellikle bir hata veya ilginç bir özellikle karşılaştığımda olur. GitHub'da kod okurken, en son sürümden okuduğunuzdan emin olun. Dalları değiştirmek için kullanılan butona tıklayıp “etiketler”i seçerek en son sürüm etiketine sahip komisyonlardan gelen kodu görüntüleyebilirsiniz. Kitaplıklar ve çerçeveler sonsuza kadar değişime uğrar, bu nedenle bir sonraki sürümde bırakılabilecek bir şey hakkında bilgi edinmek istemezsiniz.

Kaynak kodu okumanın daha az ilgili bir yolu, 'kursiyer bakış' yöntemi olarak adlandırmaktan hoşlandığım yöntemdir. Kod okumaya başladığımda, express.js dosyasını kurdum, /node_modules klasörünü açtım ve bağımlılıklarını inceledim. README bana tatmin edici bir açıklama getirmediyse, kaynağı okurum. Bunu yapmak beni şu ilginç bulgulara götürdü:

  • Express, her ikisi de nesneleri birleştiren, ancak bunu çok farklı şekillerde yapan iki modüle bağlıdır. merge-descriptors yalnızca doğrudan kaynak nesnede bulunan özellikleri ekler ve ayrıca numaralandırılamayan özellikleri birleştirirken, utils-merge yalnızca bir nesnenin numaralandırılabilir özellikleri ve prototip zincirinde bulunanlar üzerinde yinelenir. merge-descriptors Object.getOwnPropertyNames() ve Object.getOwnPropertyDescriptor() kullanırken utils-merge for..in kullanır;
  • setprototypeof modülü, somutlaştırılmış bir nesnenin prototipini ayarlamak için platformlar arası bir yol sağlar;
  • escape-html , HTML içeriğinde enterpolasyon yapılabilmesi için bir içerik dizisinden kaçmak için 78 satırlık bir modüldür.

Bulguların hemen faydalı olması muhtemel olmasa da, kütüphaneniz veya çerçeveniz tarafından kullanılan bağımlılıklar hakkında genel bir anlayışa sahip olmak faydalıdır.

Ön uç kodunda hata ayıklama söz konusu olduğunda, tarayıcınızın hata ayıklama araçları en iyi arkadaşınızdır. Diğer şeylerin yanı sıra, programı istediğiniz zaman durdurmanıza ve durumunu incelemenize, bir işlevin yürütülmesini atlamanıza veya bir işleve girip çıkmanıza izin verir. Bazen bu, kod küçültülmüş olduğu için hemen mümkün olmayacaktır. Onu küçültmeye ve küçültülmemiş kodu /node_modules klasöründeki ilgili dosyaya kopyalamaya eğilimliyim.

ReactDOM.render işlevi için kaynak kodu
Hata ayıklamaya diğer uygulamalarda olduğu gibi yaklaşın. Bir hipotez oluşturun ve ardından test edin. (Büyük önizleme)

Vaka Çalışması: Redux'un Bağlantı İşlevi

React-Redux, React uygulamalarının durumunu yönetmek için kullanılan bir kütüphanedir. Bunun gibi popüler kütüphanelerle uğraşırken, uygulanması hakkında yazılmış makaleleri arayarak başlıyorum. Bu vaka çalışması için bunu yaparken, bu makaleye rastladım. Bu, kaynak kodunu okumakla ilgili bir başka iyi şey. Araştırma aşaması genellikle sizi bunun gibi yalnızca kendi düşünce ve anlayışınızı geliştiren bilgilendirici makalelere yönlendirir.

connect , React bileşenlerini bir uygulamanın Redux deposuna bağlayan bir React-Redux işlevidir. Nasıl? Eh, belgelere göre, aşağıdakileri yapar:

"...geçirdiğiniz bileşeni saran yeni, bağlı bir bileşen sınıfı döndürür."

Bunu okuduktan sonra şu soruları sorardım:

  • İşlevlerin bir girdi aldığı ve ardından aynı girdiyi ek işlevlerle sarılmış olarak döndürdüğü herhangi bir kalıp veya kavram biliyor muyum?
  • Böyle bir kalıp biliyorsam, bunu belgelerde verilen açıklamaya dayanarak nasıl uygularım?

Genellikle bir sonraki adım, connect kullanan çok basit bir örnek uygulama oluşturmak olacaktır. Ancak bu vesileyle, en sonunda bir üretim ortamına girecek olan bir uygulama bağlamında connect anlamak istediğim için Limejump'ta geliştirdiğimiz yeni React uygulamasını kullanmayı seçtim.

Odaklandığım bileşen şöyle görünüyor:

 class MarketContainer extends Component { // code omitted for brevity } const mapDispatchToProps = dispatch => { return { updateSummary: (summary, start, today) => dispatch(updateSummary(summary, start, today)) } } export default connect(null, mapDispatchToProps)(MarketContainer);

Dört küçük bağlantılı bileşeni saran bir kap bileşenidir. connect yöntemini dışa aktaran dosyada ilk karşılaştığınız şeylerden biri şu yorumdur: connect, connectAdvanced üzerinden bir cephedir . Çok ileri gitmeden ilk öğrenme anımıza sahibiz: cephe tasarım desenini çalışırken gözlemleme fırsatı . Dosyanın sonunda, connect createConnect adlı bir işlevin çağrısını dışa aktardığını görüyoruz. Parametreleri, şu şekilde tahrip edilmiş bir grup varsayılan değerdir:

 export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {})

Yine, başka bir öğrenme anı ile karşılaşıyoruz: çağrılan işlevleri dışa aktarma ve varsayılan işlev argümanlarını yok etme . Yıkıcı kısım bir öğrenme anı çünkü kod şöyle yazılmıştı:

 export function createConnect({ connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory })

Uncaught TypeError: Cannot destructure property 'connectHOC' of 'undefined' or 'null'. Bunun nedeni, işlevin geri dönecek varsayılan bir argümanı olmamasıdır.

Not : Bununla ilgili daha fazla bilgi için David Walsh'un makalesini okuyabilirsiniz. Dil bilginize bağlı olarak bazı öğrenme anları önemsiz görünebilir ve bu nedenle daha önce görmediğiniz veya hakkında daha fazla şey öğrenmeniz gereken şeylere odaklanmak daha iyi olabilir.

createConnect kendisi, işlev gövdesinde hiçbir şey yapmaz. Burada kullandığım connect adlı bir işlevi döndürür:

 export default connect(null, mapDispatchToProps)(MarketContainer)

Tamamı isteğe bağlı olmak üzere dört bağımsız değişken alır ve ilk üç bağımsız değişkenin her biri, bağımsız değişkenlerin mevcut olup olmadığına ve değer türlerine göre davranışlarını tanımlamaya yardımcı olan bir match işlevinden geçer. Şimdi, match için sağlanan ikinci argüman connect içine aktarılan üç işlevden biri olduğu için, hangi iş parçacığını takip edeceğime karar vermem gerekiyor.

Bu argümanlar işlevlerse connect için ilk argümanı sarmak için kullanılan proxy işlevi, düz nesneleri kontrol etmek için kullanılan isPlainObject yardımcı programı veya hata ayıklayıcınızı tüm istisnaları kıracak şekilde nasıl ayarlayabileceğinizi gösteren warning modülü ile öğrenme anları vardır. Eşleştirme işlevlerinden sonra, React bileşenimizi alıp connectHOC işlevine geliyoruz. Bu, aslında bileşeni mağazaya bağlamayı işleyen wrapWithConnect işlevini döndüren başka bir işlev çağrısıdır.

connectHOC uygulamasına baktığımda, uygulama ayrıntılarını gizlemek için neden connect gerektiğini anlayabiliyorum. React-Redux'un kalbidir ve connect aracılığıyla açığa çıkarılması gerekmeyen mantığı içerir. Derin dalışı burada bitirecek olsam da, devam etmiş olsaydım, bu, kod tabanının inanılmaz derecede ayrıntılı bir açıklamasını içerdiğinden daha önce bulduğum referans materyaline başvurmak için mükemmel bir zaman olurdu.

Özet

Kaynak kodu okumak başta zordur ama her şeyde olduğu gibi zamanla daha kolay hale gelir. Amaç her şeyi anlamak değil, farklı bir bakış açısı ve yeni bilgilerle yola çıkmaktır. Anahtar, tüm süreç hakkında bilinçli olmak ve her şeyi yoğun bir şekilde merak etmektir.

Örneğin, isPlainObject işlevini ilginç buldum çünkü verilen argümanın düz bir nesne olduğundan emin olmak için if (typeof obj !== 'object' || obj === null) return false kullanıyor. Uygulamasını ilk okuduğumda, neden daha az kod olan ve nesneler ile Date gibi nesne alt türleri arasında ayrım yapan Object.prototype.toString.call(opts) !== '[object Object]' kullanmadığını merak ettim. nesne. Bununla birlikte, sonraki satırı okumak, örneğin connect kullanan bir geliştiricinin bir Date nesnesi döndürmesi gibi pek olası olmayan bir durumda, bunun Object.getPrototypeOf(obj) === null denetimi tarafından ele alınacağını ortaya çıkardı.

isPlainObject bir başka entrika parçası şu koddur:

 while (Object.getPrototypeOf(baseProto) !== null) { baseProto = Object.getPrototypeOf(baseProto) }

Bazı Google aramaları beni bu StackOverflow dizisine ve bu kodun bir iFrame'den kaynaklanan nesnelere karşı kontrol gibi durumları nasıl ele aldığını açıklayan Redux sorununa yönlendirdi.

Kaynak Kodu Okuma Üzerine Faydalı Linkler

  • “Mühendis Çerçeveleri Nasıl Tersine Çevirilir,” Max Koretskyi, Medium
  • “Kod Nasıl Okunur?” Aria Stewart, GitHub