paint-brush
Kaynak Kod Nasıl Çalışılır: Express.js'de Nesne Prototipleri ve Mixinlerile@luminix
200 okumalar

Kaynak Kod Nasıl Çalışılır: Express.js'de Nesne Prototipleri ve Mixinler

ile Lumin-ix11m2024/08/19
Read on Terminal Reader

Çok uzun; Okumak

Kaynak kodunu incelemek, şüphesiz ki geliştirme kariyerinizin gidişatını değiştirebilir. Sadece bir seviyenin altına bakmak bile sizi çoğu ortalama geliştiriciden ayırabilir. Bu dizinin konusu budur: API ile yetinmemek, bunun ötesine geçmek, bu araçları yeniden yaratmayı öğrenmek. Bu yapay zeka abartısının dünyasında ortalama olmaktan çıkmak, bir geliştiriciyi ortalamanın ötesinde değerli kılan şeydir!
featured image - Kaynak Kod Nasıl Çalışılır: Express.js'de Nesne Prototipleri ve Mixinler
Lumin-ix HackerNoon profile picture
0-item
1-item

Kaynak kodunu incelemek, şüphesiz ki geliştirici kariyerinizin gidişatını değiştirebilir. Sadece bir seviyenin altına bakmak bile sizi çoğu ortalama geliştiriciden ayırabilir.


Ustalığa giden ilk adım!


İşte kişisel bir hikaye: Bir AI/ML girişimindeki mevcut işimde, ekip Neo4j verilerini sunucudan görselleştirme için ön uca nasıl aktaracağını çözemedi ve 12 saat içinde bir sunumları vardı. Serbest çalışan olarak işe alındım ve paniği açıkça görebiliyordunuz. Sorun, Neo4j tarafından döndürülen verilerin görselleştirme aracı neo4jd3 tarafından beklenen doğru biçimde olmamasıydı.


Şunu hayal edin: Neo4jd3 bir üçgen bekliyor ve Neo4j bir kare döndürüyor. İşte tam burada uyumsuz bir uyumsuzluk var!


neo4jd3


Yakında Mastered'da JavaScript ve Neo4j ile grafik veri bilimi yapabiliriz! Bu görüntü nostaljik.


Sadece iki seçenek vardı: Neo4j'in tüm arka ucunu yeniden yapmak veya Neo4jd3'ün kaynak kodunu incelemek, beklenen formatı bulmak ve ardından kareyi üçgene dönüştüren bir adaptör oluşturmak.


 neo4jd3 <- adapter <- Neo4j 

adaptör viz



Beynim kaynak kodunu okumaya başladı ve bir adaptör oluşturdum: neo4jd3-ts .


 import createNeoChart, { NeoDatatoChartData } from "neo4jd3-ts";


Bağdaştırıcı NeoDatatoChartData ve diğer her şey tarih oldu. Bu dersi ciddiye aldım ve her fırsatta kullandığım her araçta bir seviye daha aşağı iniyorum. O kadar yaygınlaştı ki bazen belgeleri bile okumuyorum.


Bu yaklaşım kariyerimi muazzam bir şekilde değiştirdi. Yaptığım her şey sihir gibi görünüyor. Birkaç ay içinde, kaynağa doğru bir adım attığım için kritik sunucu geçişleri ve projeleri yönetiyordum.


Bu dizi tam da bununla ilgili: API'ye razı olmamak, bunun ötesine geçmek, bu araçları yeniden yaratmayı öğrenmek. Yapay zeka çılgınlığının olduğu bu dünyada ortalama olmaktan çıkmak, bir geliştiriciyi ortalamanın ötesinde değerli kılan şeydir!


Bu seriyle ilgili planım, popüler JavaScript kütüphanelerini ve araçlarını inceleyerek bunların nasıl çalıştığını ve bunlardan ne gibi kalıplar öğrenebileceğimizi tek tek anlamak.


Ben çoğunlukla arka uç mühendisi olduğumdan (evet, tam yığın, ama zamanımın %90'ını arka uçla ilgilenerek geçiriyorum), başlamak için Express.js'den daha iyi bir araç yok.


Benim varsayımım, programlama deneyiminiz ve programlamanın temellerine dair iyi bir kavrayışınız olduğudur! İleri seviye başlangıç seviyesinde sınıflandırılabilirsiniz.


Temelleri öğretirken kaynak kodunu öğrenmeye/öğretmeye çalışmak gerçekten zor ve sıkıcı olacak. Seriye katılabilirsiniz, ancak zor olacağını bekleyin. Her şeyi kapsayamam, ancak elimden geldiğince deneyeceğim.


Bu makalenin Express öncesi olmasının bir nedeni var: Express'in bağımlı olduğu çok küçük bir kütüphane olan merge-descriptors'ı ele almaya karar verdim. Bunu yazdığım sırada bu kütüphane 27.181.495 kez indirilmişti ve yalnızca 26 satır kod içeriyordu.


Bu bize bir yapı kurma ve JavaScript modülleri oluşturmada kritik öneme sahip olan nesne temellerini tanıtma fırsatı verecek.

Kurmak

Devam etmeden önce, Express kaynak kodunun ve birleştirme tanımlayıcılarının sisteminizde olduğundan emin olun. Bu şekilde, onu bir IDE'de açabilir ve nerede aradığımıza dair satır numaralarıyla size rehberlik edebilirim.


Express güçlü bir kütüphanedir. Başka bir araca geçmeden önce birkaç makalede elimizden geldiğince çok şey ele alacağız.


IDE'nizde Express kaynak kodunuzu tercihen satır numaralarıyla açın, lib klasörüne gidin ve giriş dosyası olan express.js dosyasını açın.


17. satırda ilk kütüphanemiz var:


 var mixin = require('merge-descriptors');


Kullanım 42. ve 43. satırlardadır:


 mixin(app, EventEmitter.prototype, false); mixin(app, proto, false);


Burada neler olduğunu keşfetmeden önce, bir adım geri çekilip veri yapısının ötesinde JavaScript'teki nesneler hakkında konuşmamız gerekiyor. Kompozisyon, kalıtım, prototipler ve karışımları tartışacağız—bu makalenin başlığı.

JavaScript Nesnesi

Express kaynak kodunu kapatın ve bu önemli nesne temellerini öğrenirken takip edebileceğimiz yeni bir klasör oluşturun.


Bir nesne , Nesne Yönelimli Programlamanın (OOP) özünde yer alan veri ve davranışın bir kapsüllenmesidir. İlginç bilgi: JavaScript'teki hemen hemen her şey bir nesnedir.


 const person = { // data name: "Jane", age: 0, // behavior grow(){ this.age += 1; } };

person nesnesindeki açılış ve kapanış parantezleri arasındaki her şeye nesnenin kendi özellikleri denir. Bu önemlidir.


Kişinin kendi özellikleri, doğrudan doğruya nesne üzerinde bulunan özelliklerdir. name , age , grow person kendi özellikleridir.


Bu önemlidir çünkü her JavaScript nesnesinin bir prototype özelliği vardır. Yukarıdaki nesneyi, person nesnelerini dinamik olarak oluşturmamıza izin verecek bir fonksiyon planına kodlayalım.


 function createNewPerson(name, age){ this.name = name; this.age = age; } createNewPerson.prototype.print = function(){ console.log(`${this.name} is ${this.age}`); }; const john = new createNewPerson("John", 32);

Prototip, JavaScript nesnelerinin diğer nesnelerden özellikleri ve yöntemleri nasıl devraldığıdır. Own Properties ve Prototype arasındaki fark, bir nesnedeki bir özelliğe erişirken ortaya çıkar:


 john.name; // access


JavaScript, yüksek önceliğe sahip oldukları için önce Own Properties bakacaktır. Özelliği bulamazsa, null bulana ve bir hata fırlatana kadar nesnenin kendi prototype nesnesine yinelemeli olarak bakar.


Bir prototip nesnesi, kendi prototipi aracılığıyla başka bir nesneden miras alabilir. Buna prototip zinciri denir.


 console.log(john.hasOwnProperty('name')); // true console.log(john.hasOwnProperty('print')); // false, it's in the prototype


Ancak, john üzerinde print çalışmaları var:


 john.print(); // "John is 32"


JavaScript'in prototip tabanlı bir dil olarak tanımlanmasının nedeni budur. Prototiplerle miras gibi özellikler ve yöntemler eklemekten daha fazlasını yapabiliriz.


Mirasın "merhaba dünya"sı memeli nesnesidir. Bunu JavaScript ile yeniden yaratalım.


 // our Mammal blueprint function Mammal(name) { this.name = name; } Mammal.prototype.breathe = function() { console.log(`${this.name} is breathing.`); };


JavaScript'te Object nesnesinin içinde statik bir fonksiyon vardır:


 Object.create();


{} ve new functionBlueprint benzer bir nesne oluşturur, ancak fark create miras alacağı bir prototipi parametre olarak alabilmesidir.


 // we use a cat blueprint function here (implemented below) Cat.prototype = Object.create(Mammal.prototype); // correction after we inherited all the properties Cat.prototype.constructor = Cat;


Şimdi Cat Mammal bulunan breathe yöntemine sahip olacak, ancak bilinmesi gereken önemli şey Cat prototipi olarak Mammal işaret etmesidir.

Açıklama:

  1. Memeli Planı : Öncelikle Mammal fonksiyonunu tanımlıyoruz ve prototipine bir breathe metodu ekliyoruz.


  2. Cat Kalıtımı : Cat fonksiyonunu oluşturuyoruz ve Cat.prototype Object.create(Mammal.prototype) olarak ayarlıyoruz. Bu, Cat prototipinin Mammal kalıtım almasını sağlar, ancak constructor işaretçisini Mammal olarak değiştirir.


  3. Yapıcıyı Düzeltme : Cat.prototype.constructor Cat geri işaret edecek şekilde düzeltiyoruz, böylece Cat nesnesinin Mammal yöntemleri miras alırken kimliğini koruduğundan emin oluyoruz. Son olarak, Cat bir meow yöntemi ekliyoruz.


Bu yaklaşım, Cat nesnesinin hem Mammal (örneğin breathe ) hem de kendi prototipinden (örneğin meow ) gelen yöntemlere erişmesine olanak tanır.


Bunu düzeltmemiz gerekiyor. Tam örneği oluşturalım:


 function Cat(name, breed) { this.name = name; this.breed = breed; } Cat.prototype = Object.create(Mammal.prototype); // cat prototype pointing to mammal // correction after we inherited all the properties Cat.prototype.constructor = Cat; // we are re-pointing a pointer, the inherited properties are still there Cat.prototype.meow = function() { console.log(`${this.name} is meowing.`); };


Cat.prototype.constructor = Cat anlamak için işaretçiler hakkında bilgi sahibi olmanız gerekir. Object.create ile Mammal miras aldığımızda, Cat prototipimizin işaretçisini Mammal olarak değiştirir, bu da yanlıştır. Ebeveyni Mammal olmasına rağmen Cat kendi bireyi olmasını istiyoruz.


İşte bu yüzden bunu düzeltmemiz gerekiyor.


Bu örnekte Cat , prototip zincirini kullanarak Mammal miras alır. Cat nesnesi hem breathe hem de meow yöntemlerine erişebilir.


 const myCat = new Cat("Misty", "Ragdoll"); myCat.breathe(); // Misty is breathing. myCat.meow(); // Misty is meowing.


Memeliden miras alan bir köpek de yaratabiliriz:


 function Dog(name, breed) { this.name = name; this.breed = breed; } Dog.prototype = Object.create(Mammal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.bark = function() { console.log(`${this.name} is barking.`); }; const myDog = new Dog('Buddy', 'Golden Retriever'); myDog.breathe(); // Buddy is breathing. myDog.bark(); // Buddy is barking.


Temel klasik kalıtım oluşturduk, ama bu neden önemli? Kaynak kodunu ele aldığımızı sanıyordum!


Evet, doğru, ancak prototipler, kalıtımın ötesinde, verimli ve esnek modüller oluşturmanın çekirdeğidir. Basit, iyi yazılmış modüller bile prototip nesneleriyle doludur. Biz sadece temelleri atıyoruz.


Kalıtımın alternatifi, iki veya daha fazla nesneyi alıp birleştirerek "üst" bir nesne oluşturan nesne kompozisyonudur.


Mixin'ler nesnelerin miras kullanmadan diğer nesnelerden metot ödünç almasına izin verir. İlgisiz nesneler arasında davranışı paylaşmak için kullanışlıdırlar.


İlk araştırmamızın yaptığı şey de bu: İlk önce ele almayı vaat ettiğimiz merge-descriptors kütüphanesi.

Birleştirme Tanımlayıcıları Modülü

Express'te nerede ve nasıl kullanıldığını zaten gördük. Şimdi bunu nesne kompozisyonu için biliyoruz.


17. satırda ilk kütüphanemiz şu şekilde:


 var mixin = require('merge-descriptors');


Kullanım 42. ve 43. satırlardadır:


 mixin(app, EventEmitter.prototype, false); mixin(app, proto, false);


Öğrendiklerimize göre mixin EventEmitter.prototype ve proto birleştirerek app adında bir nesne oluşturduğunu söyleyebiliriz.


Express'ten bahsederken app geçeceğiz.


merge-descriptors tüm kaynak kodu şudur:


 'use strict'; function mergeDescriptors(destination, source, overwrite = true) { if (!destination) { throw new TypeError('The `destination` argument is required.'); } if (!source) { throw new TypeError('The `source` argument is required.'); } for (const name of Object.getOwnPropertyNames(source)) { if (!overwrite && Object.hasOwn(destination, name)) { // Skip descriptor continue; } // Copy descriptor const descriptor = Object.getOwnPropertyDescriptor(source, name); Object.defineProperty(destination, name, descriptor); } return destination; } module.exports = mergeDescriptors;


Başlangıçta her zaman fonksiyonun nasıl kullanıldığına ve aldığı parametrelere bakın:


 // definition mergeDescriptors(destination, source, overwrite = true) // usage var mixin = require('merge-descriptors'); mixin(app, EventEmitter.prototype, false); mixin(app, proto, false);


App hedefimizdir. mixin nesne kompozisyonu anlamına geldiğini biliyoruz. Kabaca, bu paketin yaptığı şey kaynak nesneyi hedef nesneye birleştirmek ve üzerine yazma seçeneği sunmaktır.


Üzerine yazma, varsayıma göre, app (hedefin) kaynağın sahip olduğu tam bir özelliğe sahip olması durumunda gerçekleşir; true üzerine yazmada, aksi takdirde o özellik olduğu gibi bırakılır ve atlanır.


Nesnelerin aynı özelliğe iki kez sahip olamayacağını biliyoruz. Anahtar-değer çiftlerinde (nesnelerde), anahtarlar benzersiz olmalıdır.

Express ile üzerine yazma işlemi false olur.


Aşağıda temel ev idaresi yer almaktadır, beklenen hataları her zaman ele alın:


 if (!destination) { throw new TypeError('The `destination` argument is required.'); } if (!source) { throw new TypeError('The `source` argument is required.'); }


İşte burada ilginçleşiyor: 12. satır.


 for (const name of Object.getOwnPropertyNames(source)) {


Yukarıda OwnProperty ne anlama geldiğini biliyoruz, dolayısıyla getOwnPropertyNames açıkça kendi özelliklerinin anahtarlarını almak anlamına geliyor.


 const person = { // data name: "Jane", age: 0, // behavior grow() { this.age += 1; } }; Object.getOwnPropertyNames(person); // [ 'name', 'age', 'grow' ]


Anahtarları bir dizi olarak döndürüyor ve aşağıdaki örnekte bu anahtarlar üzerinde döngü oluşturuyoruz:


 for (const name of Object.getOwnPropertyNames(source)) {


Aşağıdaki, hedef ve kaynağın şu anda üzerinde döngü kurduğumuz aynı anahtara sahip olup olmadığını kontrol ediyor:


 if (!overwrite && Object.hasOwn(destination, name)) { // Skip descriptor continue; }


Eğer overwrite false ise, bu özelliği atlayın; üzerine yazmayın. continue yaptığı şey budur—döngüyü bir sonraki yinelemeye iter ve alttaki kodu çalıştırmaz, bu da şu koddur:


 // Copy descriptor const descriptor = Object.getOwnPropertyDescriptor(source, name); Object.defineProperty(destination, name, descriptor);


getOwnProperty ne anlama geldiğini zaten biliyoruz. Yeni kelime descriptor . Bu fonksiyonu kendi person nesnemizde test edelim:


 const person = { // data name: "Jane", age: 0, // behavior grow() { this.age += 1; } }; Object.getOwnPropertyDescriptor(person, "grow"); // { // value: [Function: grow], // writable: true, // enumerable: true, // configurable: true // }


Bu, grow fonksiyonumuzu değer olarak döndürür ve bir sonraki satır kendini açıklar:


 Object.defineProperty(destination, name, descriptor);


Tanımlayıcımızı kaynaktan alıp hedefe yazıyor. Kaynağın kendi özelliklerini hedef nesnemize kendi özellikleri olarak kopyalıyor.


person nesnemizde bir örnek yapalım:


 const val = { value: function isAlien() { return false; }, enumerable: true, writable: true, configurable: true, }; Object.defineProperty(person, "isAlien", val);


Artık person isAlien özelliği tanımlanmış olmalı.


Özetle, çok indirilen bu modül, kaynak nesnenin kendi özelliklerini, üzerine yazma seçeneğiyle birlikte hedefe kopyalar.


Bu kaynak kod katmanındaki ilk modülümüzü başarıyla tamamladık, daha heyecan verici şeyler de yolda.


Bu bir giriş niteliğindeydi. Modülü anlamak için gereken temelleri ele alarak başladık ve bir yan ürün olarak, çoğu modüldeki kalıpları, yani nesne kompozisyonunu ve kalıtımı anladık. Son olarak, merge-descriptors modülünde gezindik.


Bu kalıp çoğu makalede yaygın olacaktır. Ele alınması gereken temellerin olduğunu düşünürsem, bunları ilk bölümde ele alacağız ve ardından kaynak kodunu ele alacağız.


Neyse ki, merge-descriptors Express'te kullanılıyor ve bu bizim başlangıç kaynak kodu çalışmamızdaki odak noktamız. Bu yüzden Express'in yeterince iyi bir çalışmasını elde ettiğimizi hissedene kadar daha fazla Express.js kaynak kodu makalesi bekleyin, ardından Node.js gibi başka bir modüle veya araca geçin.


Bu arada bir meydan okuma olarak yapabileceğiniz şey, birleştirme tanımlayıcılarındaki test dosyasına gitmek, tüm dosyayı okumaktır. Kaynağı kendi başınıza okumak önemlidir, ne yaptığını ve ne test ettiğini anlamaya çalışın, sonra bozun, evet ve tekrar düzeltin veya daha fazla test ekleyin!


Programlama becerilerinizi geliştirmek için daha özel, pratik ve uzun içeriklerle ilgileniyorsanız, Ko-fi'de daha fazlasını bulabilirsiniz.