clean-code-javascript-tr
Icindekiler
- Giris
- Degiskenler
- Fonksiyonlar
- Nesneler ve Veri Yapilari
- Siniflar
- SOLID
- Test
- Eszamanlilik
- Hata Yakalama
- Yazim Sekli
- Yorumlar
- Ceviriler
Giris
Yazilim muhendisligi prensipleri, Robert C. Martin'in Clean Code isimli kitabindan alinmis, JavaScript icin uyarlanmistir. Bu bir stil rehber degildir. Bu JavaScript ile readable, reusable, ve refactorable yazilimlar uretmek icin bir rehberdir..
Burada yazilan prensiplere siki bir sekilde uymaniz gerekmez belki bazi kurallar herkesce kabul edilecektir. Burada yazanlar kurallar ve daha fazlasi degil. Ancak bu kurallar Clean Code yazarlarinin uzun yillara dayanan deneyimleri sonucu ortaya ciktigi icin dikkate almaniz iyi olabilir.
Yazilim muhendisligi konusundaki calismalarimiz 50 yilin biraz uzerinde ve hala cok fazla sey ogreniyoruz. Yazilim mimarisi, mimarligin kendisi kadar eski oldugunda belki de uyulmasi gereken daha zor kurallara sahip olacagiz. Simdilik, bu kilavuzlarin sizin ve ekibinizin urettigi JavaScript kodunun kalitesini degerlendirmek icin bir mihenk tasi olarak hizmet etmesine izin verin.
Son bir sey daha: Bunlari biliyor olmak sizi hemen mukemmel bir yazilim gelistirici yapmaz ve bu kurallari bilerek gecirdiginiz yillar hata yapmayacaginiz anlamina da gelmez. Her kod parcasi bir taslak olarak baslar, tipki islak bir kilin son halini almasi gibi de devam eder. Son olarak takim arkadaslarimizla incelemeler yaparken kotu gorunen kisimlari yok eder. Gelisime ihtiyaci olan kodun ilk hali icin kendinize kizmayin. Bunun yerine kodu dovun :)
Degiskenler
Anlamli ve belirli degisken isimleri kullanin
Kotu:
Iyi:
en basa don
Ayni turde degiskenler icin ayni kelimeleri kullanin
Kotu:
musteriVerisiGetir();
musteriKayitlariGetir();
Iyi:
en basa don
Aranabilir isimler kullanin
Yazacagimizdan daha fazla kod okuyacagiz. Bu yazdigimiz kodun okunabilir ve aranabilir olmasi acisindan onemlidir. Degiskenleri kotu bir sekilde adlandirmayarak programimizi anlamaya calisan kod okuyucularina zarar vermeyiz. Isimleri aranabilir yapin. buddy.js ve ESLint gibi araclar tanimlanmamis sabit degerleri constant olarak tanimlamaniza yardimci olabilir.
Kotu:
setTimeout(havalandirmayiCalistir, 86400000);
Iyi:
const BIR_GUNDEKI_MILISANIYELER = 86400000;
setTimeout(havalandirmayiCalistir, BIR_GUNDEKI_MILISANIYELER);
en basa don
Aciklayici degiskenler kullanin
Kotu:
const sehirPostaKoduRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
sehirPostaKodunuKaydet(adres.match(sehirPostaKoduRegex)[1], adres.match(sehirPostaKoduRegex)[2]);
Iyi:
const sehirPostaKoduRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, sehir, postaKodu] = adres.match(sehirPostaKoduRegex) || [];
sehirPostaKodunuKaydet(sehir, postaKodu);
en basa don
Zihinsel Haritalamadan Kacinin
Acik olan kapali olandan daha iyidir
Kotu:
lokasyonlar.forEach((l) => {
birSeylerYap();
baskaBirSeyYap();
// ...
// ...
// ...
// Bekle bi dk.. Bu l nedir?
oneCikar(l);
});
Iyi:
lokasyonlar.forEach((lokasyon) => {
birSeylerYap();
baskaBirSeyYap();
// ...
// ...
// ...
oneCikar(lokasyon);
});
en basa don
Gereksiz icerik eklemeyin
Eger sinif ya da nesne adindan ne yaptigi anlasiliyorsa, tekrar olarak degiskenler icinde onu anlatan isimlendirmeler yapmayin.
Kotu:
arabaUret: 'Honda',
arabaModeli: 'Accord',
arabaRengi: 'Mavi'
};
function arabayiBoya(araba) {
araba.arabaRengi = 'Kirmizi';
}
Iyi:
uret: 'Honda',
model: 'Accord',
renk: 'Mavi'
};
function arabayiBoya(araba) {
araba.renk = 'Kirmizi';
}
en basa don
Kisa Mantiksal Ifadeler ya da Kosullar Yerine Varsayilan Argumanlar Kullanin
Varsayilan argumanlar cogunlukla kisa mantiksa ifadelerden daha temiz bir kullanima sahiptir. Varsayilan argumanlarin sadece undefined argumanlar gecerliyse calisacagini unutmayin. Diger falsy olarak kabul edilen degerler varsayilan argumani degistirecektir. Bunlar '', "", false, null, 0, ve NaN olarak gosterilebilir.
Kotu:
const fabrikaAdi = isim || 'Onceki Yazilimci AS';
// ...
}
Iyi:
// ...
}
en basa don
Fonksiyonlar
Fonksiyon Argumanlari (Ideal olani 2 ya da daha az)
Fonksiyonlarin aldigi argumanlari sinirlandirmak fonksiyonun test edilebilirligi acisindan oldukca onemlidir. Ucten fazla argumana sahip bir fonksiyonu test etmeniz gerektiginde, her bir durumu her bir argumanla ayri ayri test edeceginizden dolayi tonlarca teste maruz kalabilirsiniz.
Bir veya iki arguman normal olan durumdur, mumkun oldugunca ucuncuden kacinilmadilir. Bundan daha fazla olanlar iyilestirilmelidir. Eger fonksiyonunuz ikiden fazla arguman aliyorsa, muhtemelen yapmasi gerekenden fazla isi yapmaya calisiyordur. Daha fazla argumana ihtiyaciniz oldugu durumlarda daha kapsamli bir nesne kullanmak yeterli olacaktir.
Javascript size aninda nesne olusturma kabiliyetini verdiginden dolayi, daha fazla argumana ihtiyac duydugunuz durumlarda; herhangi bir sinif uretmeye gerek kalmadan nesneler icerisinde argumanlarinizi gonderebilirsiniz.
Fonksiyonun bekledigi argumanlari garantilemek icin ES2015/ES6 ile gelen yikim islemi (destructuring) sozdizimini kullanabilirsiniz. Bunun birkac avantaji var:
- Disaridan birisi fonksiyon iskeletine baktigi zaman, fonksiyonun disaridan aldigi ozellikleri kolayca anlayabilir.
- Yikim islemi (destructuring) ayni zamanda nesne icerisinde gonderilen ilkel degerleri klonlar. Bu yan etkilerin engellenmesinde yardimci olur. Not: Arguman nesneleri tarafindan yikima ugratilmis (destruct edilmis) nesne ve dizi degerleri klonlanmaz.
- Linterlar sizi kullanilmayan degerler icin uyarabilir, ki bu durumu yikim ("destruct") islemi olmadan yapmaniz mumkun degildir.
Kotu:
// ...
}
Iyi:
// ...
}
menuOlustur({
baslik: 'Takip Et',
icerik: 'Kullanici takip edilsin mi?',
butonIcerik: 'TAKIP ET',
iptalEdilebilir: true
});
en basa don
Fonksiyonlar Tek Bir Sey Yapmali
Bu yazilim muhendisliginde en onemli kuraldir. Fonksiyonlar birden fazla is yaptiginda, onlari duzenlemek, test etmek ve hakkinda fikir sahibi olmak oldukca zorlasir. Bir fonksiyonu izole ettiginizde, daha kolay refactor edilebilir ve daha temiz, okunabilir bir kod haline gelir. Bu kilavuzdan aldiginiz tek bilgi bu olsa bile bircok gelistiricinin onunde olacaksiniz.
Kotu:
musteriler.forEach((musteri) => {
const musteriKaydi = database.sorgula(musteri);
if (musteriKaydi.aktifMi()) {
email(musteri);
}
});
}
Iyi:
musteriler
.filter(aktifMusteriMi)
.forEach(email);
}
function aktifMusteriMi(musteri) {
const musteriKaydi = database.sorgula(musteri);
return musteriKaydi.aktifMi();
}
en basa don
Fonksiyon Isimleri Ne Yaptiklarini Soylemeli
Kotu:
// ...
}
const tarih = new Date();
// Fonkisyon adina bakarak neyin eklendigini anlamak zor
tariheEkle(tarih, 1);
Iyi:
// ...
}
const tarih = new Date();
tariheAyEkle(1, tarih);
en basa don
Fonksiyonlar bir seviye soyutlastirilmalidir
Fonkiyonunuz bir seviyeden fazla soyutlasmis ise, gereginden fazla is yapiyor demektir. Fonksiyonlarinizi gorevlerine gore kucuk parcalara bolmek geri kullanilabilirlik ve kolay test edilebilirlik acisindan onemlidir.
Kotu:
const REGEXLER = [
// ...
];
const kodParcaciklari = kod.split(' ');
const simgeler = [];
REGEXLER.forEach((REGEX) => {
kodParcaciklari.forEach((kodParcacigi) => {
// ...
});
});
const ast = [];
simgeler.forEach((simge) => {
// lex...
});
ast.forEach((node) => {
// donustur...
});
}
Iyi:
const simgeler = simgelestir(kod);
const ast = analizEt(simgeler);
ast.forEach((node) => {
// donustur...
});
}
function simgelestir(kod) {
const REGEXLER = [
// ...
];
const kodParcaciklari = kod.split(' ');
const simgeler = [];
REGEXLER.forEach((REGEX) => {
kodParcaciklari.forEach((kodParcacigi) => {
simgeler.push( /* ... */ );
});
});
return simgeler;
}
function analizEt(simgeler) {
const ast = [];
simgeler.forEach((simge) => {
ast.push( /* ... */ );
});
return ast;
}
en basa don
Yinelenen kodu kaldirin
Yinelenen kodu kaldirmak icin elinizden gelenin en iyisini yapin. Tekrarlanan kodun kotu olma nedeni, kodunuzda mantiksal bir durumu degistirmeye calistiginizda bunu birden fazla yerde yapmanizi gerektirmesidir. Bu da oldukca hataya elverisli bir durumdur.
Bir restoran islettiginizi ve icinde domates, sogan, biber, sarimsak vs. olan bir deponuz oldugunu ve deponuzu takip ettiginizi dusunun. Eger bu is icin birden fazla liste tutarsaniz, en ufak bir servisinizde tum listeleri yeniden guncellemeniz gerekecektir. Eger tek bir listeniz olursa tek bir noktadan tum listeyi yonetebilirsiniz
Cogu zaman kod tekrarina dusersiniz. Cunku iki veya daha fazla kucuk farkliligi olan ama cogunlukla ayni ozellikleri tasiyan iki fonksiyon sizi bu kucuk nedenlerden dolayi cogunlukla ayni ozelliklere sahip olan ve temelde ayni isi yapan iki farkli fonksiyon yazmaya zorlar. Tekrarlayan kodu kaldirmak demek; bu farkliliklari farkli bir yerde yerine getirebilecek soyut fonksiyonlar, moduller, siniflar yazmak demektir.
Soyutlamayi dogru yapmak cok kritikdir. Bu yuzden devam eden kisimlardan Siniflar kismindaki KATI kurallari takip etmelisiniz. Kotu soyutlamalar kod tekrarindan da kotudur. Bu yuzden dikkatli olmalisiniz. Iyi bir soyutlama yapabilirim diyorsaniz bunu yapin. Kendinizi tekrar etmeyin, aksi takdirde kendinizi birden fazla yeri guncellerken bulacaksiniz.
Kotu:
gelistiriciler.forEach((gelistirici) => {
const beklenenMaas = gelistirici.beklenenMaasiHesapla();
const deneyim = gelistirici.deneyimiGetir();
const githubLink = gelistirici.githubLink();
const veri = {
beklenenMaas,
deneyim,
githubLink
};
yazdir(veri);
});
}
function yoneticiListesiniGoster(yoneticiler) {
yoneticiler.forEach((yonetici) => {
const beklenenMaas = yonetici.beklenenMaasiHesapla();
const deneyim = yonetici.deneyimiGetir();
const portfolio = yonetici.projeleriniGetir();
const veri = {
beklenenMaas,
deneyim,
portfolio
};
yazdir(veri);
});
}
Iyi:
personeller.forEach((personel) => {
const beklenenMaas = personel.beklenenMaasiHesapla();
const deneyim = personel.deneyimiGetir();
const veri = {
beklenenMaas,
deneyim
};
switch (personel.tip) {
case 'yonetici':
veri.portfolio = personel.projeleriniGetir();
break;
case 'developer':
veri.githubLink = personel.githubLink();
break;
}
yazdir(veri);
});
}
en basa don
Varsayilan Nesneleri Object.assign ile Atayin!
Kotu:
baslik: null,
icerik: 'Deneme',
butonYazisi: null,
iptalEdilebilir: true
};
function menuOlustur(ayar) {
ayar.baslik = ayar.baslik || 'Bir Baslik';
ayar.icerik = ayar.icerik || 'Deneme';
ayar.butonYazisi = ayar.butonYazisi || 'Kaydet';
ayar.iptalEdilebilir = ayar.iptalEdilebilir !== undefined ? ayar.iptalEdilebilir : true;
}
menuOlustur(menuAyari);
Iyi:
baslik: 'Bir Baslik',
// Gelistirici 'icerik' key'ini burada belirtmedi
butonYazisi: 'Kaydet',
iptalEdilebilir: true
};
function menuOlustur(ayar) {
ayar = Object.assign({
baslik: 'Bir Baslik',
icerik: 'Deneme',
butonYazisi: 'Kaydet',
iptalEdilebilir: true
}, ayar);
// ayar simdi: {baslik: "Bir Baslik", icerik: "Deneme", butonYazisi: "Kaydet", iptalEdilebilir: true}
// ...
}
menuOlustur(menuAyari);
en basa don
Bayraklari Fonksiyon Argumanlari Olarak Kullanmayin
Bayraklar gelistiriciye fonksiyonun birden fazla sey yaptigini soyler. Fonksiyonlar sadece bir is yapmalidir. Eger fonksiyonlariniz boolean degere dayali olarak farkli kodlar calistiriyorsa onlari bolun.
Kotu:
if (gecici) {
fs.create(`./gecici/${isim}`);
} else {
fs.create(isim);
}
}
Iyi:
fs.create(isim);
}
function geciciDosyaOlustur(isim) {
dosyaOlustur(`./temp/${isim}`);
}
en basa don
Yan Etkilerden Kacinin (Kisim 1)
Bir fonksiyon, deger alip baska deger veya degerler dondurmek disinda bir sey yapiyorsa yan etki olusturur. Bu yan etki, dosyalara bir seyler yazmak, bazi global degiskenleri degistirmek, guncellemek veya yanlislikla butun paranizi bir yabanciya aktarmak olabilir.
Zaman zaman yazdiginiz programda yan etkilerin olmasi gerekir. Mesela, bir onceki ornekte islenildigi gibi dosyalara bir seyler yazmaniz gerekebilir. Yapmaniz gereken sey ise yan etki olusturan islemleri yaptiginiz yeri merkezilestirmektir. Ornegin belirli bir dosya uzerinde islem yapan birkac fonksiyon veya sinifiniz olmasin. Sadece bir servis bunu yapsin. Evet, sadece bir servis.
Buradaki ana fikir, herhangi bir yapiya sahip olmayan nesneler arasindaki stateleri paylasmak, herhangi bir sey tarafindan degistirilebilir veri tiplerini kullanmak veya yan etkilerin olustugu yerleri merkezilestirmemek gibi yaygin hatalardan kacinmaktir. Eger bunlari yapabilirseniz, diger programcilarin buyuk bir cogunlugundan daha mutlu olacaksiniz.
Kotu:
// If we had another function that used this name, now it'd be an array and it could break it.
let isim = 'Ali Veli';
function isimVeSoyismiAyir() {
isim = isim.split(' ');
}
isimVeSoyismiAyir();
console.log(isim); // ['Ali', 'Veli'];
Iyi:
return isim.split(' ');
}
const isim = 'Ali Veli';
const yeniIsim = isimVeSoyismiAyir(isim);
console.log(isim); // 'Ali Veli';
console.log(yeniIsim); // ['Ali', 'Veli'];
en basa don
Avoid Side Effects (part 2)
In JavaScript, primitives are passed by value and objects/arrays are passed by
reference. In the case of objects and arrays, if your function makes a change
in a shopping cart array, for example, by adding an item to purchase,
then any other function that uses that cart array will be affected by this
addition. That may be great, however it can be bad too. Let's imagine a bad
situation:
The user clicks the "Purchase", button which calls a purchase function that
spawns a network request and sends the cart array to the server. Because
of a bad network connection, the purchase function has to keep retrying the
request. Now, what if in the meantime the user accidentally clicks "Add to Cart"
button on an item they don't actually want before the network request begins?
If that happens and the network request begins, then that purchase function
will send the accidentally added item because it has a reference to a shopping
cart array that the addItemToCart function modified by adding an unwanted
item.
A great solution would be for the addItemToCart to always clone the cart,
edit it, and return the clone. This ensures that no other functions that are
holding onto a reference of the shopping cart will be affected by any changes.
Two caveats to mention to this approach:
-
There might be cases where you actually want to modify the input object, but when you adopt this programming practice you will find that those cases are pretty rare. Most things can be refactored to have no side effects!
-
Cloning big objects can be very expensive in terms of performance. Luckily, this isn't a big issue in practice because there are great libraries that allow this kind of programming approach to be fast and not as memory intensive as it would be for you to manually clone objects and arrays.
Kotu:
cart.push({ item, date: Date.now() });
};
Iyi:
return [...cart, { item, date: Date.now() }];
};
en basa don
Global fonksiyonlar yazma.
Javascript'te globalleri kirletmek kotu bir uygulamadir cunku diger bir kutuphaneyle cakisabilirsiniz ve API kullaniciniz uygulamayi canli ortama sundugunda alacagi bir hataya kadar bu durumdan haberdar olmayabilir. Bir ornek uzerinden dusunelim: eger JavaScript'in sahip oldugu Array metodunu, iki dizi arasindaki farki gosteren bir diff metoduna sahip olacak sekilde genisletmek isteseydik? Array.prototype a yeni bir fonksiyon yazabilirsin, ama bu, ayni seyi yapmaya calisan baska bir kutuphane ile cakisabilir. Ya baska bir kutuphane diff metodunu, bir dizinin ilk elemani ile son elemani arasindaki farki bulmak icin kullaniyor olsaydi? Bu yuzden ES2015/ES6 siniflarini kullanmak ve basitce Array i kalitimla almak cok daha iyi olacaktir.
Kotu:
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
Iyi:
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
en basa don
Emirli programlama yerine Fonksiyonel programlamayi tercih edin
JavaScript, Haskell gibi fonksiyonel bir dil degil ama fonksiyonel yonleri de var. Fonksiyonel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiginiz zaman bu programlama stilini tercih edin.
Kotu:
{
name: 'Uncle Bobby',
kodSatirlari: 500
}, {
name: 'Suzie Q',
kodSatirlari: 1500
}, {
name: 'Jimmy Gosling',
kodSatirlari: 150
}, {
name: 'Gracie Hopper',
kodSatirlari: 1000
}
];
let toplamCikti = 0;
for (let i = 0; i < programciCiktisi.length; i++) {
toplamCikti += programciCiktisi[i].kodSatirlari;
}
Iyi:
{
name: 'Uncle Bobby',
kodSatirlari: 500
}, {
name: 'Suzie Q',
kodSatirlari: 1500
}, {
name: 'Jimmy Gosling',
kodSatirlari: 150
}, {
name: 'Gracie Hopper',
kodSatirlari: 1000
}
];
const toplamCikti = programciCiktisi
.map(cikti => cikti.kodSatirlari)
.reduce((toplamSatirlar, satirlar) => toplamSatirlar + satirlar);
en basa don
Encapsulate conditionals
Kotu:
// ...
}
Iyi:
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
en basa don
Negatif Kosullardan Kacinin
Kotu:
// ...
}
if (!domYaratilmadi(node)) {
// ...
}
Iyi:
// ...
}
if (domYaratildi(node)) {
// ...
}
en basa don
Kosullardan Kacinin
Bu imkansiz bir is gibi guzukuyor. Cogu insan bunu ilk duydugu ana kadar, " if ifadesi olmadan nasil bir sey yapabilirim? " diyor. Bunun cevabi, bircok durumda ayni isi yapmak icin polymorphism kullanabilirsiniz. Genellikle ikinci soru, "iyi guzel ama neden bunu yapmayi isteyeyim ki?" Bunun cevabi ise ogrendigimiz onceki temiz kod konsepti olan: bir fonksiyon yalnizca bir sey yapmalidir. if ifadesine sahip olan siniflariniz ve fonksiyonlariniz oldugunda, kullanicilariniza fonksiyonunuzun birden fazla sey yaptigini soyluyorsunuz. Hatirla, sadece bir sey yap.
Kotu:
// ...
seyirYuksekliginiGetir() {
switch (this.type) {
case '777':
return this.maxYuksekligiGetir() - this.yolcuSayisiniGetir();
case 'Air Force One':
return this.maxYuksekligiGetir();
case 'Cessna':
return this.maxYuksekligiGetir() - this.yakitHarcamasiniGetir();
}
}
}
Iyi:
// ...
}
class Boeing777 extends Ucak {
// ...
seyirYuksekliginiGetir() {
return this.maxYuksekligiGetir() - this.yolcuSayisiniGetir();
}
}
class AirForceOne extends Ucak {
// ...
seyirYuksekliginiGetir() {
return this.maxYuksekligiGetir();
}
}
class Cessna extends Ucak {
// ...
seyirYuksekliginiGetir() {
return this.maxYuksekligiGetir() - this.yakitHarcamasiniGetir();
}
}
en basa don
Tip Kontrolunden Kacinin (Bolum 1)
JavaScript tip guvensiz bir dildir, yani fonksiyonlariniz herhangi bir tipte arguman alabilir. Bazen bu ozgurluk can yakici olabiliyor haliyle fonksiyonlarinizda tip kontrolu yapmak cazip hale gelebiliyor. Bundan kacinmanin bircok yolu var. Dikkate alinmasi gereken ilk sey tutarli API'lar yazmaniz.
Kotu:
if (arac instanceof Bisiklet) {
arac.pedaliCevir(this.mevcutLokasyon, new Lokasyon('nigde'));
} else if (arac instanceof Araba) {
arac.sur(this.mevcutLokasyon, new Lokasyon('nigde'));
}
}
Iyi:
arac.hareketEt(this.mevcutLokasyon, new Lokasyon('nigde'));
}
en basa don
Tip Kontrolunden Kacinin (Bolum 2)
Eger strginler ve integerlar gibi temel ilkel degerlerle calisiyorsaniz ve polymorphism kullanamiyorsaniz ama hala tip kontrolu yapmaniz gerekiyormus gibi hissediyorsaniz TypeScript kullanmayi dusunmelisiniz. TypeScript, normal JavaScript'in mukkemel bir alternatifi, standart Javascript soz diziminin uzerine statik yazmanizi saglar. Normal JavaScript'te manuel sekilde tip kontrolu yapmanin problemi, tip kontrlunu iyi yapmak ekstra kod kalabaligini gerektiriyor. Yapmaya calistigimiz sahte "tip kontrolu" kaybolan okunabilirligi telafi etmiyor. JavaScript kodlarinizi temiz tutun, iyi testler yazin ve rahat kod incelemelerine sahip olun. Ayrica, hepsini yap ama TypeScript ile yap (dedigim gibi, harika bir alternatif).
Kotu:
if (typeof deger1 === 'number' && typeof deger2 === 'number' ||
typeof deger1 === 'string' && typeof deger2 === 'string') {
return val1 + val2;
}
throw new Error('String veya Number tipinde olmalidir!');
}
Iyi:
return deger1 + deger2;
}
en basa don
Asiri Optimizasyon Yapmayin
Modern tarayicilar calisma aninda arkaplanda cok fazla optimizasyon yaparlar. Cogu zaman yaptiginiz optimizasyon, bosa zaman harcamaktir. Optimzasyonun nerede eksik oldugunu gormek icin bu kaynaklar iyidir.
Kotu:
// Cunku `list.length` her defasinda yeniden sayilir. Modern tarayicilarda bu optimize edilmistir.
for (let i = 0, len = liste.length; i < len; i++) {
// ...
}
Iyi:
// ...
}
en basa don
Olu Kodlari Silin
Olu kod da tekrarli kodlar kadar kotudur. Kodlarinizda olu kod saklamaniz icin herhangi bir neden yoktur. Eger herhangi bir yerde cagrilmiyorlarsa onlardan kurtulun. Ihtiyaciniz oldugunda, versiyon kontrol sisteminde bulabilirsiniz.
Kotu:
// ...
}
function yeniHttpRequestModulu(url) {
// ...
}
const istek = yeniHttpRequestModulu;
envanterTakibi('elmalar', istek, 'www.envantertakibi.com');
Iyi:
// ...
}
const istek = yeniHttpRequestModulu;
envanterTakibi('elmalar', istek, 'www.envantertakibi.com');
en basa don
Nesneler ve Veri Yapilari
Setter ve Getter kullanin
Nesnelerdeki verilere erismek icin Setter ve Getter kullanmak sadece bir nesnedeki ozellikleri aramaktan daha iyi olabilir. "Neden?" diye soracaksiniz. Peki, iste burada sebeplerinin bir listesi var:
- Bir nesne ozelligini elde etmekten daha fazla sey yapmak istediginizde kod tabaninizdaki her erisimciyi aramaniz ve degistirmeniz gerekmez.
setislemi yaparken dogrulama eklemeyi kolaylastirir.- Ic temsili kapsuller.
- Set ve Get islemlerini gerceklestirirken kayit tutmayi (log) ve hata yakalamayi eklemek kolaydir.
- Nesnenizin ozelliklerini sunucudan alirken Lazy Load kullanabilirsiniz.
Kotu:
// ...
return {
bakiye: 0,
// ...
};
}
const hesap = bankaHesabiOlustur();
hesap.bakiye = 100;
Iyi:
// bu private
let bakiye = 0;
//Getter asagida dondurulen nesne araciligiyla public hale getirildi
function getBakiye() {
return bakiye;
}
// Setter asagida dondurulen nesne araciligiyla public hale getirildi
function setBakiye(deger) {
// ... bakiyeyi guncellemeden once onaylar
bakiye = deger;
}
return {
// ...
getBakiye,
setBakiye,
};
}
const hesap = bankaHesabiOlustur();
hesap.setBakiye(100);
en basa don
Nesnelerin private uyelere sahip olmasini saglayin.
Bu, kapamalarla(closures) gerceklestirilebilir. (ES5 ve alti icin).
Kotu:
this.isim = isim;
};
Calisan.prototype.getIsim = function getIsim() {
return this.isim;
};
const calisan = new calisan('John Doe');
console.log(`Calisanin ismi: ${calisan.getIsim()}`); // Calisanin ismi: John Doe
delete calisan.isim;
console.log(`Calisanin ismi: ${calisan.getIsim()}`); // Calisanin ismi: undefined
Iyi:
return {
getIsim() {
return isim;
},
};
}
const calisan = calisanOlustur('John Doe');
console.log(`Calisanin ismi: ${calisan.getIsim()}`); // Calisanin ismi: John Doe
delete calisan.isim;
console.log(`Calisanin ismi: ${calisan.getIsim()}`); // Calisanin ismi: John Doe
en basa don
Siniflar
Yalin ES5 fonksiyonlari yerine ES2015/ES6 siniflarini tercih edin
Klasik ES5 siniflari icin okunabilir sinif kalitimlari, construction ve metod tanimlarini almak cok zordur. Eger kalitima ihtiyaciniz varsa (ihtiyacinizin olmayabileceginin farkinda olun), o zaman ES2015/ES6 siniflarini tercih edin. Ancak, daha buyuk ve karmasik nesnelerle ugrasana kadar siniflar yerine kucuk fonksiyonlari kullanin.
Kotu:
if (!(this instanceof Animal)) {
throw new Error('Instantiate Animal with `new`');
}
this.age = age;
};
Animal.prototype.move = function move() {};
const Mammal = function(age, furColor) {
if (!(this instanceof Mammal)) {
throw new Error('Instantiate Mammal with `new`');
}
Animal.call(this, age);
this.furColor = furColor;
};
Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};
const Human = function(age, furColor, languageSpoken) {
if (!(this instanceof Human)) {
throw new Error('Instantiate Human with `new`');
}
Mammal.call(this, age, furColor);
this.languageSpoken = languageSpoken;
};
Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};
Iyi:
constructor(age) {
this.age = age;
}
move() { /* ... */ }
}
class Mammal extends Animal {
constructor(age, furColor) {
super(age);
this.furColor = furColor;
}
liveBirth() { /* ... */ }
}
class Human extends Mammal {
constructor(age, furColor, languageSpoken) {
super(age, furColor);
this.languageSpoken = languageSpoken;
}
speak() { /* ... */ }
}
en basa don
Metod zincirleme yontemini kullanin
Bu yontem JavaScript'te cok kullanislidir ve bunu jQuery ve Lodash gibi bircok kutuphanede gorebilirsiniz.
Kodunuzun daha anlamli ve daha az detayli olmasini saglar.
Bu nedenle, metod zincirleme yontemini bir kez kullanin ve kodunuzun ne kadar temiz olacagina bir goz atin derim.
Sinif fonksiyonlarinda basitce her fonksiyon sonunda this dondurun,
boylece daha fazla sinif metodu zincirleyebilirsiniz.
Kotu:
constructor(marka, model, renk) {
this.marka = marka;
this.model = model;
this.renk = renk;
}
setMarka(marka) {
this.marka = marka;
}
setModel(model) {
this.model = model;
}
setRenk(renk) {
this.renk = renk;
}
kaydet() {
console.log(this.marka, this.model, this.renk);
}
}
const araba = new Araba('Ford','F-150','kirmizi');
araba.setRenk('pembe');
araba.kaydet();
Iyi:
constructor(marka, model, renk) {
this.marka = marka;
this.model = model;
this.renk = renk;
}
setMarka(marka) {
this.marka = marka;
// NOT: Zincirleme icin 'this' donduruluyor
return this;
}
setModel(model) {
this.model = model;
// NOT: Zincirleme icin 'this' donduruluyor
return this;
}
setRenk(renk) {
this.renk = renk;
// NOT: Zincirleme icin 'this' donduruluyor
return this;
}
kaydet() {
console.log(this.marka, this.model, this.renk);
// NOT: Zincirleme icin 'this' donduruluyor
return this;
}
}
const araba = new Araba('Ford','F-150','kirmizi')
.setRenk('pembe')
.kaydet();
en basa don
Miras(Kalitim) yerine Kompozisyonu tercih edin
Gang of Four tarafindan Design Patterns da unlu olarak belirtildigi gibi, yapabildiginiz yerlerde miras(kalitim) yerine kompozisyonu tercih etmelisiniz. Miras(kalitim)i kullanmak icin bircok iyi sebep oldugu gibi kompozisyonu kullanmak icinde bircok iyi sebep var. Bu kural icin asil nokta, eger akliniz icgudusel olarak miras(kalitim)i tercih ediyorsa, kompozisyonun, probleminizi daha iyi modelleyebilecegini dusunmeye calisin. Bazi durumlarda bu olabilir.
"Miras(kalitim)i ne zaman kullanmaliyim?" diye merak ediyor olabilirsiniz. Bu durum elinizdeki soruna bagli ama bu, ne zaman miras(kalitim)in kompozisyondan daha anlamli oldugunun kabul edilebilir bir listesi.
- Miras(kalitim)iniz, "-dir, -dir" iliskisini sagliyor ve "sahiplik" iliskisinin saglamiyor. (Insan->Hayvan vs. Kullanici->KullaniciDetaylari)
- Kodu temel siniflardan yeniden kullanabilirsiniz. (Insanlar, tum hayvanlar gibi hareket edebilir)
- Bir temel sinifi degistirerek turetilmis siniflarda genel degisiklikler yapmak istiyorsunuz. (Hayvanlarin hareket ettiginde harcadigi kaloriyi degistirmek)
Kotu:
constructor(isim, mail) {
this.isim = isim;
this.mail = mail;
}
// ...
}
// Kotu cunku Personeller vergi verisine "sahip". PersonelVergiVerileri, bir Personel turu degil.
class PersonelVergiVerileri extends Personel {
constructor(ssn, maas) {
super();
this.ssn = ssn; // sosyal guvenlik numarasi
this.maas = maas;
}
// ...
}
Iyi:
constructor(ssn, maas) {
this.ssn = ssn; // sosyal guvenlik numarasi
this.maas = maas;
}
// ...
}
class Personel {
constructor(isim, mail) {
this.isim = isim;
this.mail = mail;
}
vergiVerisiniBelirle(ssn, maas) {
this.vergiVerisi = new PersonelVergiVerileri(ssn, maas);
}
// ...
}
en basa don
SOLID
Tek Sorumluluk Prensibi (TSP)
Temiz Kod'da belirtildigi gibi, "Bir sinifin degisebilmesi icin asla birden fazla sebep olmamalidir". Bircok islevsellikle birlikte bir sinifi sikistirmak cezbedicidir, tipki bir ucusda yalnizca bir valiz almak gibi. Bununla ilgili mesele, sinifinizin kavramsal olarak uyum saglamayacagi ve degismesi icin bircok neden verecegidir. Bir sinifi degistirmeniz icin gereken sureyi en aza indirgemek onemlidir. Cunku cok fazla islevsellik bir sinifta bulunuyorsa ve siz bir kismini degistirirseniz, bu degisikligin kod tabaninizdaki diger bagimli modulleri nasil etkileyecegini anlamaniz zor olabilir.
Kotu:
constructor(user) {
this.user = user;
}
changeSettings(settings) {
if (this.verifyCredentials()) {
// ...
}
}
verifyCredentials() {
// ...
}
}
Iyi:
constructor(user) {
this.user = user;
}
verifyCredentials() {
// ...
}
}
class UserSettings {
constructor(user) {
this.user = user;
this.auth = new UserAuth(user);
}
changeSettings(settings) {
if (this.auth.verifyCredentials()) {
// ...
}
}
}
en basa don
Acik/Kapali Prensibi
Bertrand Meyer tarafindan belirtildigi gibi, "yazilim varliklari (classlar, moduller, fonksiyonlar vs.) gelisime acik, degisime kapali olmalidir." Peki bu ne anlamaya geliyor? Bu ilke temel olarak, kullanicilarin varolan kodu degistirmeden yeni islevler ekleyebilmesini saglamamiz gerektigini belirtir.
Kotu:
constructor() {
super();
this.name = 'ajaxAdapter';
}
}
class NodeAdapter extends Adapter {
constructor() {
super();
this.name = 'nodeAdapter';
}
}
class HttpRequester {
constructor(adapter) {
this.adapter = adapter;
}
fetch(url) {
if (this.adapter.name === 'ajaxAdapter') {
return makeAjaxCall(url).then((response) => {
// transform response and return
});
} else if (this.adapter.name === 'httpNodeAdapter') {
return makeHttpCall(url).then((response) => {
// transform response and return
});
}
}
}
function makeAjaxCall(url) {
// request and return promise
}
function makeHttpCall(url) {
// request and return promise
}
Iyi:
constructor() {
super();
this.name = 'ajaxAdapter';
}
request(url) {
// request and return promise
}
}
class NodeAdapter extends Adapter {
constructor() {
super();
this.name = 'nodeAdapter';
}
request(url) {
// request and return promise
}
}
class HttpRequester {
constructor(adapter) {
this.adapter = adapter;
}
fetch(url) {
return this.adapter.request(url).then((response) => {
// transform response and return
});
}
}
en basa don
Liskov Yer Degistirme Prensibi (LSP)
Bu terim bu kadar basit bir konsept icin korkutucu. Resmi olarak tanimi soyle: "Eger S, T'nin alt turu ise, programin istenilen ozelliklerinden herhangi birini degistirmeden(dogrulugunu, yaptigi isi vb.) T tipindeki nesneler S tipindeki nesneler ile yer degistirebilir (yani, S tipindeki nesneler T tipindeki nesnelerin yerine gecebilir). Bu daha da korkutucu bir tanim.
Bunun icin en iyi aciklama: eger bir ebeveyn sinifiniz ve alt sinifiniz varsa, temel sinif ve alt sinif, yanlis sonuclar ortaya koymadan birbirlerinin yerine kullanilabilir. Hala kafa karistirici olabilir, o zaman hadi klasik Kare-Dikdortgen ornegine goz atalim. Matematiksel olarak kare bir dikdortgendir ama eger bunu kalitim yoluyla, "is-a" iliskisi kullanarak modellerseniz basinizin belaya girmesi cok gecikmeyecektir.
Kotu:
constructor() {
this.genislik = 0;
this.yukseklik = 0;
}
renginiBelirle(renk) {
// ...
}
olustur(alan) {
// ...
}
genisligiBelirle(genislik) {
this.genislik = genislik;
}
yuksekligiBelirle(yukseklik) {
this.yukseklik = yukseklik;
}
alanHesapla() {
return this.genislik * this.yukseklik;
}
}
class Kare extends Dikdortgen {
genisligiBelirle(genislik) {
this.genislik = genislik;
this.yukseklik = genislik;
}
yuksekligiBelirle(yukseklik) {
this.genislik = yukseklik;
this.yukseklik = yukseklik;
}
}
function genisDikdortgenlerOlustur(dikdortgenler) {
dikdortgenler.forEach((dikdortgen) => {
dikdortgen.genisligiBelirle(4);
dikdortgen.yuksekligiBelirle(5);
const alan = dikdortgen.alanHesapla(); // KOTU: Kare icin 25 doner. 20 olmaliydi.
dikdortgen.olustur(alan);
});
}
const dikdortgenler = [new Dikdortgen(), new Dikdortgen(), new Kare()];
genisDikdortgenlerOlustur(dikdortgenler);
Iyi:
renginiAyarla(renk) {
// ...
}
olustur(alan) {
// ...
}
}
class Dikdortgen extends Sekil {
constructor(genislik, yukseklik) {
super();
this.genislik = genislik;
this.yukseklik = yukseklik;
}
alanHesapla() {
return this.genislik * this.yukseklik;
}
}
class Kare extends Sekil {
constructor(uzunluk) {
super();
this.uzunluk = uzunluk;
}
alanHesapla() {
return this.uzunluk * this.uzunluk;
}
}
function genisSekillerOlustur(sekiller) {
sekiller.forEach((sekil) => {
const alan = sekil.alanHesapla();
sekil.olustur(alan);
});
}
const sekiller = [new Dikdortgen(4, 5), new Dikdortgen(4, 5), new Kare(5)];
genisSekillerOlustur(sekiller);
en basa don
Interface Segregation Principle (ISP)
JavaScript doesn't have interfaces so this principle doesn't apply as strictly as others. However, it's important and relevant even with JavaScript's lack of type system.
ISP states that "Clients should not be forced to depend upon interfaces that they do not use." Interfaces are implicit contracts in JavaScript because of duck typing.
A good example to look at that demonstrates this principle in JavaScript is for classes that require large settings objects. Not requiring clients to setup huge amounts of options is beneficial, because most of the time they won't need all of the settings. Making them optional helps prevent having a "fat interface".
Kotu:
constructor(settings) {
this.settings = settings;
this.setup();
}
setup() {
this.rootNode = this.settings.rootNode;
this.animationModule.setup();
}
traverse() {
// ...
}
}
const $ = new DOMTraverser({
rootNode: document.getElementsByTagName('body'),
animationModule() {} // Most of the time, we won't need to animate when traversing.
// ...
});
Iyi:
constructor(settings) {
this.settings = settings;
this.options = settings.options;
this.setup();
}
setup() {
this.rootNode = this.settings.rootNode;
this.setupOptions();
}
setupOptions() {
if (this.options.animationModule) {
// ...
}
}
traverse() {
// ...
}
}
const $ = new DOMTraverser({
rootNode: document.getElementsByTagName('body'),
options: {
animationModule() {}
}
});
en basa don
Dependency Inversion Principle (DIP)
This principle states two essential things:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend upon details. Details should depend on abstractions.
This can be hard to understand at first, but if you've worked with AngularJS, you've seen an implementation of this principle in the form of Dependency Injection (DI). While they are not identical concepts, DIP keeps high-level modules from knowing the details of its low-level modules and setting them up. It can accomplish this through DI. A huge benefit of this is that it reduces the coupling between modules. Coupling is a very bad development pattern because it makes your code hard to refactor.
As stated previously, JavaScript doesn't have interfaces so the abstractions
that are depended upon are implicit contracts. That is to say, the methods
and properties that an object/class exposes to another object/class. In the
example below, the implicit contract is that any Request module for an
InventoryTracker will have a requestItems method.
Kotu:
constructor() {
this.REQ_METHODS = ['HTTP'];
}
requestItem(item) {
// ...
}
}
class InventoryTracker {
constructor(items) {
this.items = items;
// BAD: We have created a dependency on a specific request implementation.
// We should just have requestItems depend on a request method: `request`
this.requester = new InventoryRequester();
}
requestItems() {
this.items.forEach((item) => {
this.requester.requestItem(item);
});
}
}
const inventoryTracker = new InventoryTracker(['apples', 'bananas']);
inventoryTracker.requestItems();
Iyi:
constructor(items, requester) {
this.items = items;
this.requester = requester;
}
requestItems() {
this.items.forEach((item) => {
this.requester.requestItem(item);
});
}
}
class InventoryRequesterV1 {
constructor() {
this.REQ_METHODS = ['HTTP'];
}
requestItem(item) {
// ...
}
}
class InventoryRequesterV2 {
constructor() {
this.REQ_METHODS = ['WS'];
}
requestItem(item) {
// ...
}
}
// By constructing our dependencies externally and injecting them, we can easily
// substitute our request module for a fancy new one that uses WebSockets.
const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2());
inventoryTracker.requestItems();
en basa don
Testing
Testing is more important than shipping. If you have no tests or an inadequate amount, then every time you ship code you won't be sure that you didn't break anything. Deciding on what constitutes an adequate amount is up to your team, but having 100% coverage (all statements and branches) is how you achieve very high confidence and developer peace of mind. This means that in addition to having a great testing framework, you also need to use a good coverage tool.
There's no excuse to not write tests. There are plenty of good JS test frameworks, so find one that your team prefers. When you find one that works for your team, then aim to always write tests for every new feature/module you introduce. If your preferred method is Test Driven Development (TDD), that is great, but the main point is to just make sure you are reaching your coverage goals before launching any feature, or refactoring an existing one.
Single concept per test
Kotu:
describe('MakeMomentJSGreatAgain', () => {
it('handles date boundaries', () => {
let date;
date = new MakeMomentJSGreatAgain('1/1/2015');
date.addDays(30);
assert.equal('1/31/2015', date);
date = new MakeMomentJSGreatAgain('2/1/2016');
date.addDays(28);
assert.equal('02/29/2016', date);
date = new MakeMomentJSGreatAgain('2/1/2015');
date.addDays(28);
assert.equal('03/01/2015', date);
});
});
Iyi:
describe('MakeMomentJSGreatAgain', () => {
it('handles 30-day months', () => {
const date = new MakeMomentJSGreatAgain('1/1/2015');
date.addDays(30);
assert.equal('1/31/2015', date);
});
it('handles leap year', () => {
const date = new MakeMomentJSGreatAgain('2/1/2016');
date.addDays(28);
assert.equal('02/29/2016', date);
});
it('handles non-leap year', () => {
const date = new MakeMomentJSGreatAgain('2/1/2015');
date.addDays(28);
assert.equal('03/01/2015', date);
});
});
en basa don
Eszamanlilik
Promiseleri kullan,Callbackleri degil.
Callbackler kusursuz degildir , ve asiri miktarda ic ice gecmeye neden olurlar. ES2015/ES6 ile birlikte Promiseler bir yerlesik evrensel tiptir. Onlari kullan!
Kotu:
import { writeFile } from 'fs';
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (istekHatasi, cevap) => {
if (requestErr) {
console.error(istekHatasi);
} else {
writeFile('makale.html', cevap.body, (yazmaHatasi) => {
if (yazmaHatasi) {
console.error(yazmaHatasi);
} else {
console.log('Dosya yazildi');
}
});
}
});
Iyi:
import { writeFile } from 'fs';
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
.then((cevap) => {
return writeFile('makale.html', cevap);
})
.then(() => {
console.log('Dosya yazildi');
})
.catch((hata) => {
console.error(hata);
});
en basa don
Async/Await ,Promise'den daha temizdir.
Promiseler Callbacklere nazaran daha temizdir, fakat ES2017/ES8 daha
temiz bir cozum sunan async await'i getirdi. Tek ihtiyacin async onekine sahip bir fonksiyon,
ve sonrasinda thenli fonksiyonlar zincirini kullanmaksizin
mantigini zorunlu olarak yazabilirsin. ES2017 / ES8 ozelliklerinden yararlanabiliyorsaniz bunu
bugun kullanin!.
Kotu:
import { writeFile } from 'fs-promise';
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
.then((cevap) => {
return writeFile('makale.html', cevap);
})
.then(() => {
console.log('Dosya yazildi');
})
.catch((hata) => {
console.error(hata);
});
Iyi:
import { writeFile } from 'fs-promise';
async function temizKodMakalesiniAl() {
try {
const cevap = await get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
await writeFile('makale.html', cevap);
console.log('Dosya yazildi');
} catch(hata) {
console.error(hata);
}
}
en basa don
Hata Yakalama
Hatalar olusturmak iyi bir seydir. Hatalar size programinizda bir seylerin yolunda olmadigini soylemenin en iyi yoludur. Calisan bir kod parcacigi ya da calismayi durduran bir fonksiyonun, process'in neden durduguna dair konsol ekraninda sizi bilgilendirirler.
Yakalanan Hatalari Gormezden Gelmeyin
Yakalanan bir hata ile hicbir sey gerceklestirmemek, size o hatayi tamamen fixlemis olma imkani sunmaz.
Hatalari (console.log) ile gostermek, tipki suya yazi yazmak gibidir. Cogu zaman yetersizdir.
Eger kod bolumlerini try/catch bloklari ile olusturuyorsaniz o bolumde bir hatanin olusabilecegini dusunuyorsunuzdur.
Bu durumlar icin bir planiniz olmali ya da bu durumlari yonetebileceginiz ayri kod yapilariniz olmali.
Kotu:
hataFirlatabilecekFonksiyon();
} catch (hata) {
console.log(hata);
}
Iyi:
hataFirlatabilecekFonksiyon();
} catch (hata) {
// Ilk secenek (console.log'dan daha cok bilgilendirici):
console.error(hata);
// Diger Secenek:
kullaniciyaHataGoster(hata);
// Diger Secenek:
hatayiServiseBildir(hata);
// Ya da ucunu de yapabilirsiniz!!
}
Promise Hatalarini Gormezden Gelmeyin
Ayni sebepten dolayi try/catch'ten kaynaklanan hatalari gozardi etmemelisiniz.
Kotu:
.then((veri) => {
fonksiyonHataFirlatabilir(veri);
})
.catch((hata) => {
console.log(hata);
});
Iyi:
.then((veri) => {
fonksiyonHataFirlatabilir(veri);
})
.catch((hata) => {
// Ilk secenek (console.log'dan daha cok bilgilendirici):
console.error(hata);
// Diger Secenek:
kullaniciyaHataGoster(hata);
// Diger Secenek:
hatayiServiseBildir(hata);
// Ya da ucunu de yapabilirsiniz!!
});
en basa don
Yazim Sekli
Yazim sekli ozneldir. Buradaki bircok kural gibi, uymaniz gereken zor ve siki bir kural yoktur. Yazim sekli uzerinde TARTISMAYIN. Bunlari otomatiklestirmek icin binlerce arac vardir. Birini kullanin! Muhendisler icin, yazim sekli uzerinde tartismak zaman ve para kaybidir.
Otomatik formatlama kapsamina girmeyen seyler (girintileme, tab veya bosluk, cift veya tek tirnak vb.) hakkinda rehberlik icin buraya bakin.
Buyuk harf kullaniminiz tutarli olsun
JavaScript'in bir yazim kurali yoktur, bu yuzden buyuk harf kullanimi size degiskenler, fonksiyonlar vb. seyler hakkinda bircok bilgi verir. Bu kurallar ozneldir, yani ekibiniz istedigini secebilir. Onemli olan neyi sectiginiz degildir, sectiginizde tutarli olmanizdir.
Kotu:
const ayinGunSayisi = 30;
const sarkilar = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
const Sanatcilar = ['ACDC', 'Led Zeppelin', 'The Beatles'];
function veritabaniniSil() {}
function veritabanini_kurtar() {}
class hayvan {}
class Alpaka {}
Iyi:
const AYIN_GUN_SAYISI = 30;
const SARKILAR = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
const SANATCILAR = ['ACDC', 'Led Zeppelin', 'The Beatles'];
function veritabaniniSil() {}
function veritabaniniKurtar() {}
class Hayvan {}
class Alpaka {}
en basa don
Cagirilan ve cagiran fonksiyonlar birbirine yakin olmalidir.
Eger bir fonksiyon digerini cagiriyorsa, kodda onlari dikey olarak birbirine yakin tutun. Ideal olarak, cagiran fonksiyonu cagirilanin hemen uzerinde tutun. Kodlari tipki gazete okur gibi yukaridan asagiya dogru okuruz. Bu nedenle, kodunuzun bu yolda okunabilmesini saglayin.
Kotu:
constructor(calisan) {
this.calisan = calisan;
}
benzerleriniGetir() {
return db.lookup(this.calisan, 'benzer');
}
mudurleriGetir() {
return db.lookup(this.calisan, 'mudur');
}
benzerDegerlendirmeler() {
const benzerler = this.benzerleriniGetir();
// ...
}
performansDegerlendirmesi() {
this.benzerDegerlendirmeler();
this.mudurDegerlendirmeleri();
this.kendiDegerlendirmeleriGetir();
}
mudurDegerlendirmeleri() {
const mudur = this.mudurleriGetir();
}
kendiDegerlendirmeleriGetir() {
// ...
}
}
const deegrlendirme = new PerformansDegerlendirmesi(calisan);
deegrlendirme.performansDegerlendirmesi();
Iyi:
constructor(calisan) {
this.calisan = calisan;
}
performansDegerlendirmesi() {
this.benzerDegerlendirmeler();
this.mudurDegerlendirmeleri();
this.kendiDegerlendirmeleriGetir();
}
benzerDegerlendirmeler() {
const benzer = this.benzerleriniGetir();
// ...
}
benzerleriniGetir() {
return db.lookup(this.calisan, 'benzer');
}
mudurDegerlendirmeleri() {
const mudur = this.mudurleriGetir();
}
mudurleriGetir() {
return db.lookup(this.calisan, 'mudur');
}
kendiDegerlendirmeleriGetir() {
// ...
}
}
const degerlendirme = new PerformansDegerlendirmesi(calisan);
degerlendirme.performansDegerlendirmesi();
en basa don
Javascript Dokumantasyon Kurallarina uyulmali.
DocBlock sayesinde uygulamalarimizdaki fonksiyonlar, methodlar, siniflar, modeller ve kontrollerin islevlerini, degiskenlerini, geri donus degerlerini hatirlamamizi ve/veya kullandigimiz IDElerde fonksiyonlar hakkinda hizli bilgilendirmeler almak icin mutlaka oncelik verilmesi gerekiyor. Ayrica yorum satirlarinda yapilmasi gereken notlarimizi TODO: seklinde tanimlamalar yaparsak en son eksik kalan yerleri hizli hatirlamamiza yardimci olabilir.
Kotu:
faiz=((Anapara*Gun*YillikOran)/36500)*(1-(StopajOrani/100));
return faiz;
}
function FonGetiri(Anapara,Gun,FonOrani){
//var Gun=((sonTarih-ilkTarih)/(1000*60*60*24)); hucre icerisinde "01.01.2019" olunca calismiyor
// tarih olan hucre secildiginde bu calismaktadir. ilerleyen gunlerde google sheets script
// arastirma yapilacak
fon=(Anapara*(Gun)*(FonOrani/100));
return fon;
}
Iyi:
* Nakit Akisi Sinifi.
*
* @fileOverview Nakit Akisinda Kullanilan Ozel Fonksiyonlar Mevcuttur.
* @author Sezgin BULUT
* @version 1.0.1
*/
/**
* Vadeli Hesap Net Faiz Hesaplama
*
* @link https://www.yapikredi.com.tr/bireysel-bankacilik/mevduat-urunleri/mevduat-stopaj-oranlari
*
* Bu formul parametre olarak iletilen tutarin Net Faizini geri dondurur
* @param {100.000,00} Anapara Anapara Tutari Yazilacak
* @param {5} Gun Gun Yazilacak
* @param {17,25} YillikOran Yillik Faiz Orani Yazilacak
* @param {15} StopajOrani Mevduat Stopaj Orani Yazilacak
* @return
* @customfunction
*/
function FaizHesapla(Anapara,Gun,YillikOran,StopajOrani){
faiz=((Anapara*Gun*YillikOran)/36500)*(1-(StopajOrani/100));
return faiz;
}
/**
* Fon Hesaplama
*
* @link https://www.yapikredi.com.tr/yatirimci-kosesi/fon-bilgileri
*
* Bu formul parametre olarak iletilen tutarin Net Fon Gelirini geri dondurur
* @param {1.000,00} Anapara Anapara Tutari Yazilacak
* @param {5} Gun Gun Yazilacak
* @param {0,04} FonOrani Gunluk Fon Getiri Orani Yazilacak
* @return
* @customfunction
*/
function FonGetiri(Anapara,Gun,FonOrani){
// TODO: var Gun=((sonTarih-ilkTarih)/(1000*60*60*24)); hucre icerisinde "01.01.2019" olunca calismiyor
// TODO: tarih olan hucre secildiginde bu calismaktadir. ilerleyen gunlerde google sheets script arastirma yapilacak
fon=(Anapara*(Gun)*(FonOrani/100));
return fon;
}
en basa don
Yorumlar
Sadece is mantiginin karmasik oldugu durumlarda yorumlari kullanin.
Yorumlar lukstur, zorunlu degildir. Iyi kod cogunlukla kendini belli eder.
Kotu:
// Ozet
let ozet = 0;
// data degiskeninin uzunlugu
const uzunluk = veri.length;
// veri degiskeninin her karakterini donguye sok
for (let i = 0; i < uzunluk; i++) {
// Karakter kodunu getir
const karakter = veri.charCodeAt(i);
// Ozetini cikar
ozet = ((ozet << 5) - ozet) + karakter;
// 32-bit'lik sayiya cevir
ozet &= ozet;
}
}
Iyi:
let ozet = 0;
const uzunluk = veri.length;
for (let i = 0; i < uzunluk; i++) {
const karakter = veri.charCodeAt(i);
ozet = ((ozet << 5) - ozet) + karakter;
// 32-bit'lik sayiya cevir
ozet &= ozet;
}
}
en basa don
Kod tabaninizda yorum satirina alinmis kod birakmayin.
Surum kontrol sistemleri bu nedenle var. Eski kodu gecmisinizde birakin.
Kotu:
// baskaBirSeyYap();
// birazDahaBirSeyYap();
// dahaFazlaBirSeyYap();
Iyi:
en basa don
Yorum satirini gunluge cevirmeyin
Surum kontrol sistemlerini kullanmaniz gerektigini hatirlayin! Olu koda, yorum satirina alinmis koda ve
ozellikle gunluge cevrilmis yorum satirina gerek yok. Onceki yapilanlari almak icin git log komutunu kullanin!
Kotu:
* 2016-12-20: Monadlari kaldirdim, onlari anlamadim (RM)
* 2016-10-01: Ozel monadlari kullanarak gelistirdim (JP)
* 2016-02-03: Tip denetimini kaldirdim (LI)
* 2015-03-14: Topla fonksiyonunu ekledim (JR)
*/
function topla(a, b) {
return a + b;
}
Iyi:
return a + b;
}
en basa don
Konum isaretleyicilerini kullanmaktan kacinin
Onlar sadece kuru gurultuden ibaret. Fonksiyonlar ve degiskenlerin uygun girintilemeler, yoluyla kodunuza gorsel seklini vermesine izin verin.
Kotu:
// Scope Model Ornegi
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
menu: 'foo',
nav: 'bar'
};
////////////////////////////////////////////////////////////////////////////////
// Eylem tanimlanmasi
////////////////////////////////////////////////////////////////////////////////
const eylemler = function() {
// ...
};
Iyi:
menu: 'foo',
nav: 'bar'
};
const eylemler = function() {
// ...
};
en basa don
Translation
This is also available in other languages:
- Brazilian Portuguese: fesnt/clean-code-javascript
- Spanish: andersontr15/clean-code-javascript
- Chinese:
- German: marcbruederlin/clean-code-javascript
- Korean: qkraudghgh/clean-code-javascript-ko
- Polish: greg-dev/clean-code-javascript-pl
- Russian:
- Vietnamese: hienvd/clean-code-javascript/
- Japanese: mitsuruog/clean-code-javascript/
- Indonesia: andirkh/clean-code-javascript/
- Italian: frappacchio/clean-code-javascript/
en basa don