- Giriş
- Değişkenler
- Fonksiyonlar
- Nesneler ve Veri Yapıları
- Sınıflar
- SOLID
- Test
- Eşzamanlılık
- Hata Yakalama
- Yazım Şekli
- Yorumlar
- Çeviriler
Yazılım mühendisliği prensipleri, Robert C. Martin'in Clean Code isimli kitabından alınmış, JavaScript için uyarlanmıştır. Bu bir stil rehber değildir. Bu JavaScript ile readable, reusable, ve refactorable yazılımlar üretmek için bir rehberdir..
Burada yazılan prensiplere sıkı bir şekilde uymanız gerekmez belki bazı kurallar herkesçe kabul edilecektir. Burada yazanlar kurallar ve daha fazlası değil. Ancak bu kurallar Clean Code yazarlarının uzun yıllara dayanan deneyimleri sonucu ortaya çıktığı için dikkate almanız iyi olabilir.
Yazılım mühendisliği konusundaki çalışmalarımız 50 yılın biraz üzerinde ve hala çok fazla şey öğreniyoruz. Yazılım mimarisi, mimarlığın kendisi kadar eski olduğunda belki de uyulması gereken daha zor kurallara sahip olacağız. Şimdilik, bu kılavuzların sizin ve ekibinizin ürettiği JavaScript kodunun kalitesini değerlendirmek için bir mihenk taşı olarak hizmet etmesine izin verin.
Son bir şey daha: Bunları biliyor olmak sizi hemen mükemmel bir yazılım geliştirici yapmaz ve bu kuralları bilerek geçirdiğiniz yıllar hata yapmayacağınız anlamına da gelmez. Her kod parçası bir taslak olarak başlar, tıpkı ıslak bir kilin son halini alması gibi de devam eder. Son olarak takım arkadaşlarımızla incelemeler yaparken kötü görünen kısımları yok eder. Gelişime ihtiyacı olan kodun ilk hali için kendinize kızmayın. Bunun yerine kodu dövün :)
Kötü:
const yyyymmdstr = moment().format('YYYY/MM/DD');
İyi:
const mevcutTarih = moment().format('YYYY/MM/DD');
Kötü:
kullaniciBilgisiGetir();
musteriVerisiGetir();
musteriKayitlariGetir();
İyi:
kullaniciGetir();
Yazacağımızdan daha fazla kod okuyacağız. Bu yazdığımız kodun okunabilir ve aranabilir olması açısından önemlidir. Değişkenleri kötü bir şekilde adlandırmayarak programımızı anlamaya çalışan kod okuyucularına zarar vermeyiz. İsimleri aranabilir yapın. buddy.js ve ESLint gibi araçlar tanımlanmamış sabit değerleri constant olarak tanımlamanıza yardımcı olabilir.
Kötü:
// Bu 86400000 ne olabilir??
setTimeout(havalandirmayiCalistir, 86400000);
İyi:
// Bu türden tanımlamaları büyük harfler ve alt çizgiler içerecek şekilde belirtin.
const BIR_GUNDEKI_MILISANIYELER = 86400000;
setTimeout(havalandirmayiCalistir, BIR_GUNDEKI_MILISANIYELER);
Kötü:
const adres = 'Kireç Burnu Sahili, Sarıyer 34400';
const sehirPostaKoduRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
sehirPostaKodunuKaydet(adres.match(sehirPostaKoduRegex)[1], adres.match(sehirPostaKoduRegex)[2]);
İyi:
const adres = 'Kireç Burnu Sahili, Sarıyer 34400';
const sehirPostaKoduRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, sehir, postaKodu] = adres.match(sehirPostaKoduRegex) || [];
sehirPostaKodunuKaydet(sehir, postaKodu);
Açık olan kapalı olandan daha iyidir
Kötü:
const lokasyonlar = ['Ankara', 'İstanbul', 'İzmir'];
lokasyonlar.forEach((l) => {
birSeylerYap();
baskaBirSeyYap();
// ...
// ...
// ...
// Bekle bi dk.. Bu l nedir?
oneCikar(l);
});
İyi:
const lokasyonlar = ['Ankara', 'İstanbul', 'İzmir'];
lokasyonlar.forEach((lokasyon) => {
birSeylerYap();
baskaBirSeyYap();
// ...
// ...
// ...
oneCikar(lokasyon);
});
Eğer sınıf ya da nesne adından ne yaptığı anlaşılıyorsa, tekrar olarak değişkenler içinde onu anlatan isimlendirmeler yapmayın.
Kötü:
const Araba = {
arabaUret: 'Honda',
arabaModeli: 'Accord',
arabaRengi: 'Mavi'
};
function arabayiBoya(araba) {
araba.arabaRengi = 'Kırmızı';
}
İyi:
const Araba = {
uret: 'Honda',
model: 'Accord',
renk: 'Mavi'
};
function arabayiBoya(araba) {
araba.renk = 'Kırmızı';
}
Varsayılan argümanlar çoğunlukla kısa mantıksa ifadelerden daha temiz bir kullanıma sahiptir. Varsayılan argümanların sadece undefined argümanlar geçerliyse çalışacağını unutmayın. Diğer falsy olarak kabul edilen değerler varsayılan argümanı değiştirecektir. Bunlar ''
, ""
, false
, null
, 0
, ve NaN
olarak gösterilebilir.
Kötü:
function fabrikaOlustur(isim) {
const fabrikaAdi = isim || 'Önceki Yazılımcı AŞ';
// ...
}
İyi:
function fabrikaOlustur(isim = 'Önceki Yazılımcı AŞ') {
// ...
}
Fonksiyonların aldığı argümanları sınırlandırmak fonksiyonun test edilebilirliği açısından oldukça önemlidir. Üçten fazla argümana sahip bir fonksiyonu test etmeniz gerektiğinde, her bir durumu her bir argümanla ayrı ayrı test edeceğinizden dolayı tonlarca teste maruz kalabilirsiniz.
Bir veya iki argüman normal olan durumdur, mümkün olduğunca üçüncüden kaçınılmadılır. Bundan daha fazla olanlar iyileştirilmelidir. Eğer fonksiyonunuz ikiden fazla argüman alıyorsa, muhtemelen yapması gerekenden fazla işi yapmaya çalışıyordur. Daha fazla argümana ihtiyacınız olduğunuz durumlarda daha kapsamlı bir nesne kullanmak yeterli olacaktır.
Javascript size anında nesne oluşturma kabiliyetini verdiğinden dolayı, daha fazla argümana ihtiyaç duyduğunuz durumlarda; herhangi bir sınıf üretmeye gerek kalmadan nesneler içerisinde argümanlarınızı gönderebilirsiniz.
Fonksiyonun beklediği argümanları garantilemek için ES2015/ES6 ile gelen yıkım işlemi (destructuring) sözdizimini kullanabilirsiniz. Bunun birkaç avantajı var:
- Dışarıdan birisi fonksiyon iskeletine baktığı zaman, fonksiyonun dışarıdan aldığı özellikleri kolayca anlayabilir.
- Yıkım işlemi (destructuring) aynı zamanda nesne içerisinde gönderilen ilkel değerleri klonlar. Bu yan etkilerin engellenmesinde yardımcı olur. Not: Argüman nesneleri tarafından yıkıma uğratılmış (destruct edilmiş) nesne ve dizi değerleri klonlanmaz.
- Linterlar sizi kullanılmayan değerler için uyarabilir, ki bu durumu yıkım ("destruct") işlemi olmadan yapmanız mümkün değildir.
Kötü:
function menuOlustur(baslik, icerik, butonIcerik, iptalEdilebilir) {
// ...
}
İyi:
function menuOlustur({ baslik, icerik, butonIcerik, iptalEdilebilir }) {
// ...
}
menuOlustur({
baslik: 'Takip Et',
icerik: 'Kullanıcı takip edilsin mi?',
butonIcerik: 'TAKİP ET',
iptalEdilebilir: true
});
Bu yazılım mühendisliğinde en önemli kuraldır. Fonksiyonlar birden fazla iş yaptığında, onları düzenlemek, test etmek ve hakkında fikir sahibi olmak oldukça zorlaşır. Bir fonksiyonu izole ettiğinizde, daha kolay refactor edilebilir ve daha temiz, okunabilir bir kod haline gelir. Bu kılavuzdan aldığınız tek bilgi bu olsa bile birçok geliştiricinin önde olacaksınız.
Kötü:
function musterilereMailYolla(musteriler) {
musteriler.forEach((musteri) => {
const musteriKaydi = database.sorgula(musteri);
if (musteriKaydi.aktifMi()) {
email(musteri);
}
});
}
İyi:
function aktifMusterilereEmailGonder(musteriler) {
musteriler
.filter(aktifMusteriMi)
.forEach(email);
}
function aktifMusteriMi(musteri) {
const musteriKaydi = database.sorgula(musteri);
return musteriKaydi.aktifMi();
}
Kötü:
function tariheEkle(tarih, ay) {
// ...
}
const tarih = new Date();
// Fonkisyon adına bakarak neyin eklendiğini anlamak zor
tariheEkle(tarih, 1);
İyi:
function tariheAyEkle(ay, tarih) {
// ...
}
const tarih = new Date();
tariheAyEkle(1, tarih);
Fonkiyonunuz bir seviyeden fazla soyutlaşmış ise, gereğinden fazla iş yapıyor demektir. Fonksiyonlarınızı görevlerine göre küçük parçalara bölmek geri kullanılabilirlik ve kolay test edilebilirlik açısından önemlidir.
Kötü:
function dahaIyiJSAlternatifineDonustur(kod) {
const REGEXLER = [
// ...
];
const kodParcaciklari = kod.split(' ');
const simgeler = [];
REGEXLER.forEach((REGEX) => {
kodParcaciklari.forEach((kodParcacigi) => {
// ...
});
});
const ast = [];
simgeler.forEach((simge) => {
// lex...
});
ast.forEach((node) => {
// dönüstür...
});
}
İyi:
function dahaIyiJSAlternatifineDonustur(kod) {
const simgeler = simgelestir(kod);
const ast = analizEt(simgeler);
ast.forEach((node) => {
// dönüstür...
});
}
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;
}
Yinelenen kodu kaldırmak için elinizden gelenin en iyisini yapın. Tekrarlanan kodun kötü olma nedeni, kodunuzda mantıksal bir durumu değiştirmeye çalıştığınızda bunu birden fazla yerde yapmanızı gerektirmesidir. Bu da oldukça hataya elverişli bir durumdur.
Bir restoran işlettiğinizi ve içinde domates, soğan, biber, sarımsak vs. olan bir deponuz olduğunu ve deponuzu takip ettiğinizi düşünün. Eğer bu iş için birden fazla liste tutarsanız, en ufak bir servisinizde tüm listeleri yeniden güncellemeniz gerekecektir. Eğer tek bir listeniz olursa tek bir noktadan tüm listeyi yönetebilirsiniz
Çoğu zaman kod tekrarına düşersiniz. Çünkü iki veya daha fazla küçük farklılığı olan ama çoğunlukla aynı özellikleri taşıyan iki fonksiyon sizi bu küçük nedenlerden dolayı çoğunlukla aynı özelliklere sahip olan ve temelde aynı işi yapan iki farklı fonksiyon yazmaya zorlar. Tekrarlayan kodu kaldırmak demek; bu farklılıkları farklı bir yerde yerine getirebilecek soyut fonksiyonlar, modüller, sınıflar yazmak demektir.
Soyutlamayı doğru yapmak çok kritikdir. Bu yüzden devam eden kısımlardan Sınıflar kısmındaki KATI kuralları takip etmelisiniz. Kötü soyutlamalar kod tekrarından da kötüdür. Bu yüzden dikkatli olmalısınız. İyi bir soyutlama yapabilirim diyorsanız bunu yapın. Kendinizi tekrar etmeyin, aksi takdirde kendinizi birden fazla yeri güncellerken bulacaksınız.
Kötü:
function gelistiriciListesiniGoster(gelistiriciler) {
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);
});
}
İyi:
function personelListesiniGoster(personeller) {
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);
});
}
Kötü:
const menuAyari = {
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);
İyi:
const menuAyari = {
baslik: 'Bir Baslik',
// Geliştirici '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);
Bayraklar geliştiriciye fonksiyonun birden fazla şey yaptığını söyler. Fonksiyonlar sadece bir iş yapmalıdır. Eğer fonksiyonlarınız boolean değere dayalı olarak farklı kodlar çalıştırıyorsa onları bölün.
Kötü:
function dosyaOlustur(isim, gecici) {
if (gecici) {
fs.create(`./gecici/${isim}`);
} else {
fs.create(isim);
}
}
İyi:
function dosyaOlustur(isim) {
fs.create(isim);
}
function geciciDosyaOlustur(isim) {
dosyaOlustur(`./temp/${isim}`);
}
Bir fonksiyon, değer alıp başka değer veya değerler döndürmek dışında bir şey yapıyorsa yan etki oluşturur. Bu yan etki, dosyalara bir şeyler yazmak, bazı global değişkenleri değiştirmek, güncellemek veya yanlışlıkla bütün paranızı bir yabancıya aktarmak olabilir.
Zaman zaman yazdığınız programda yan etkilerin olması gerekir. Mesela, bir önceki örnekte işlenildiği gibi dosyalara bir şeyler yazmanız gerekebilir. Yapmanız gereken şey ise yan etki oluşturan işlemleri yaptığınız yeri merkezileştirmektir. Örneğin belirli bir dosya üzerinde işlem yapan birkaç fonksiyon veya sınıfınız olmasın. Sadece bir servis bunu yapsın. Evet, sadece bir servis.
Buradaki ana fikir, herhangi bir yapıya sahip olmayan nesneler arasındaki stateleri paylaşmak, herhangi bir şey tarafından değiştirilebilir veri tiplerini kullanmak veya yan etkilerin oluştuğu yerleri merkezileştirmemek gibi yaygın hatalardan kaçınmaktır. Eğer bunları yapabilirseniz, diğer programcıların büyük bir çoğunluğundan daha mutlu olacaksınız.
Kötü:
// Global variable referenced by following function.
// 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'];
İyi:
function isimVeSoyismiAyir(isim) {
return isim.split(' ');
}
const isim = 'Ali Veli';
const yeniIsim = isimVeSoyismiAyir(isim);
console.log(isim); // 'Ali Veli';
console.log(yeniIsim); // ['Ali', 'Veli'];
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.
Kötü:
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
İyi:
const addItemToCart = (cart, item) => {
return [...cart, { item, date: Date.now() }];
};
Javascript'te globalleri kirletmek kötü bir uygulamadır çünkü diğer bir kütüphaneyle çakışabilirsiniz ve API kullanıcınız uygulamayı canlı ortama sunduğunda alacağı bir hataya kadar bu durumdan haberdar olmayabilir. Bir örnek üzerinden düşünelim: eğer JavaScript'in sahip olduğu Array metodunu, iki dizi arasındaki farkı gösteren bir diff
metoduna sahip olacak şekilde genişletmek isteseydik? Array.prototype
a yeni bir fonksiyon yazabilirsin, ama bu, aynı şeyi yapmaya çalışan başka bir kütüphane ile çakışabilir. Ya başka bir kütüphane diff
metodunu, bir dizinin ilk elemanı ile son elemanı arasındaki farkı bulmak için kullanıyor olsaydı? Bu yüzden ES2015/ES6 sınıflarını kullanmak ve basitçe Array
i kalıtımla almak çok daha iyi olacaktır.
Kötü:
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
İyi:
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
JavaScript, Haskell gibi fonksiyonel bir dil değil ama fonksiyonel yönleri de var. Fonksiyonel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiğiniz zaman bu programlama stilini tercih edin.
Kötü:
const programciCiktisi = [
{
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;
}
İyi:
const programciCiktisi = [
{
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);
Kötü:
if (fsm.state === 'fetching' && isEmpty(listNode)) {
// ...
}
İyi:
function shouldShowSpinner(fsm, listNode) {
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
Kötü:
function domYaratilmadi(node) {
// ...
}
if (!domYaratilmadi(node)) {
// ...
}
İyi:
function domYaratildi(node) {
// ...
}
if (domYaratildi(node)) {
// ...
}
Bu imkansız bir iş gibi güzüküyor. Çoğu insan bunu ilk duyduğu ana kadar, " if
ifadesi olmadan nasıl bir şey yapabilirim? " diyor. Bunun cevabı, birçok durumda aynı işi yapmak için polymorphism kullanabilirsiniz. Genellikle ikinci soru, "iyi güzel ama neden bunu yapmayı isteyeyim ki?" Bunun cevabı ise öğrendiğimiz önceki temiz kod konsepti olan: bir fonksiyon yalnızca bir şey yapmalıdır. if
ifadesine sahip olan sınıflarınız ve fonksiyonlarınız olduğunda, kullanıcılarınıza fonksiyonunuzun birden fazla şey yaptığını söylüyorsunuz. Hatırla, sadece bir şey yap.
Kötü:
class Ucak {
// ...
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();
}
}
}
İyi:
class Ucak {
// ...
}
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();
}
}
JavaScript tip güvensiz bir dildir, yani fonksiyonlarınız herhangi bir tipte argüman alabilir. Bazen bu özgürlük can yakıcı olabiliyor haliyle fonksiyonlarınızda tip kontrolü yapmak cazip hale gelebiliyor. Bundan kaçınmanın birçok yolu var. Dikkate alınması gereken ilk şey tutarlı API'lar yazmanız.
Kötü:
function nigdeyeZiyaret(arac) {
if (arac instanceof Bisiklet) {
arac.pedaliCevir(this.mevcutLokasyon, new Lokasyon('nigde'));
} else if (arac instanceof Araba) {
arac.sur(this.mevcutLokasyon, new Lokasyon('nigde'));
}
}
İyi:
function nigdeyeZiyaret(arac) {
arac.hareketEt(this.mevcutLokasyon, new Lokasyon('nigde'));
}
Eğer strginler ve integerlar gibi temel ilkel değerlerle çalışıyorsanız ve polymorphism kullanamıyorsanız ama hala tip kontrolü yapmanız gerekiyormuş gibi hissediyorsanız TypeScript kullanmayı düşünmelisiniz. TypeScript, normal JavaScript'in mükkemel bir alternatifi, standart Javascript söz diziminin üzerine statik yazmanızı sağlar. Normal JavaScript'te manuel şekilde tip kontrolü yapmanın problemi, tip kontrlünü iyi yapmak ekstra kod kalabalığını gerektiriyor. Yapmaya çalıştığımız sahte "tip kontrolü" kaybolan okunabilirliği telafi etmiyor. JavaScript kodlarınızı temiz tutun, iyi testler yazın ve rahat kod incelemelerine sahip olun. Ayrıca, hepsini yap ama TypeScript ile yap (dediğim gibi, harika bir alternatif).
Kötü:
function kombin(deger1, deger2) {
if (typeof deger1 === 'number' && typeof deger2 === 'number' ||
typeof deger1 === 'string' && typeof deger2 === 'string') {
return val1 + val2;
}
throw new Error('String veya Number tipinde olmalıdır!');
}
İyi:
function kombin(deger1, deger2) {
return deger1 + deger2;
}
Modern tarayıcılar çalışma anında arkaplanda çok fazla optimizasyon yaparlar. Çoğu zaman yaptığınız optimizasyon, boşa zaman harcamaktır. Optimzasyonun nerede eksik olduğunu görmek için bu kaynaklar iyidir.
Kötü:
// Eski tarayıcılarda `list.length` için önbelleğe alınmamış her yineleme maliyetlidir.
// Çünkü `list.length` her defasında yeniden sayılır. Modern tarayıcılarda bu optimize edilmiştir.
for (let i = 0, len = liste.length; i < len; i++) {
// ...
}
İyi:
for (let i = 0; i < liste.length; i++) {
// ...
}
Ölü kod da tekrarlı kodlar kadar kötüdür. Kodlarınızda ölü kod saklamanız için herhangi bir neden yoktur. Eğer herhangi bir yerde çağrılmıyorlarsa onlardan kurtulun. İhtiyacınız olduğunda, versiyon kontrol sisteminde bulabilirsiniz.
Kötü:
function eskiHttpRequestModulu(url) {
// ...
}
function yeniHttpRequestModulu(url) {
// ...
}
const istek = yeniHttpRequestModulu;
envanterTakibi('elmalar', istek, 'www.envantertakibi.com');
İyi:
function yeniHttpRequestModulu(url) {
// ...
}
const istek = yeniHttpRequestModulu;
envanterTakibi('elmalar', istek, 'www.envantertakibi.com');
Nesnelerdeki verilere erişmek için Setter ve Getter kullanmak sadece bir nesnedeki özellikleri aramaktan daha iyi olabilir. "Neden?" diye soracaksınız. Peki, işte burada sebeplerinin bir listesi var:
- Bir nesne özelliğini elde etmekten daha fazla şey yapmak istediğinizde kod tabanınızdaki her erişimciyi aramanız ve değiştirmeniz gerekmez.
set
işlemi yaparken doğrulama eklemeyi kolaylaştırır.- İç temsili kapsüller.
- Set ve Get işlemlerini gerçekleştirirken kayıt tutmayı (log) ve hata yakalamayı eklemek kolaydır.
- Nesnenizin özelliklerini sunucudan alırken Lazy Load kullanabilirsiniz.
Kötü:
function bankaHesabiOlustur() {
// ...
return {
bakiye: 0,
// ...
};
}
const hesap = bankaHesabiOlustur();
hesap.bakiye = 100;
İyi:
function bankaHesabiOlustur() {
// bu private
let bakiye = 0;
//Getter aşağıda döndürülen nesne aracılığıyla public hale getirildi
function getBakiye() {
return bakiye;
}
// Setter aşağıda döndürülen nesne aracılığıyla public hale getirildi
function setBakiye(deger) {
// ... bakiyeyi güncellemeden önce onaylar
bakiye = deger;
}
return {
// ...
getBakiye,
setBakiye,
};
}
const hesap = bankaHesabiOlustur();
hesap.setBakiye(100);
Bu, kapamalarla(closures) gerçekleştirilebilir. (ES5 ve altı için).
Kötü:
const Calisan = function(isim) {
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
İyi:
function calisanOlustur(isim) {
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
Klasik ES5 sınıfları için okunabilir sınıf kalıtımları, construction ve metod tanımlarını almak çok zordur. Eğer kalıtıma ihtiyacınız varsa (ihtiyacınızın olmayabileceğinin farkında olun), o zaman ES2015/ES6 sınıflarını tercih edin. Ancak, daha büyük ve karmaşık nesnelerle uğraşana kadar sınıflar yerine küçük fonksiyonları kullanın.
Kötü:
const Animal = function(age) {
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() {};
İyi:
class Animal {
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() { /* ... */ }
}
Bu yöntem JavaScript'te çok kullanışlıdır ve bunu jQuery ve Lodash gibi birçok kütüphanede görebilirsiniz.
Kodunuzun daha anlamlı ve daha az detaylı olmasını sağlar.
Bu nedenle, metod zincirleme yöntemini bir kez kullanın ve kodunuzun ne kadar temiz olacağına bir göz atın derim.
Sınıf fonksiyonlarında basitçe her fonksiyon sonunda this
döndürün,
böylece daha fazla sınıf metodu zincirleyebilirsiniz.
Kötü:
class Araba {
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();
İyi:
class Araba {
constructor(marka, model, renk) {
this.marka = marka;
this.model = model;
this.renk = renk;
}
setMarka(marka) {
this.marka = marka;
// NOT: Zincirleme için 'this' döndürülüyor
return this;
}
setModel(model) {
this.model = model;
// NOT: Zincirleme için 'this' döndürülüyor
return this;
}
setRenk(renk) {
this.renk = renk;
// NOT: Zincirleme için 'this' döndürülüyor
return this;
}
kaydet() {
console.log(this.marka, this.model, this.renk);
// NOT: Zincirleme için 'this' döndürülüyor
return this;
}
}
const araba = new Araba('Ford','F-150','kirmizi')
.setRenk('pembe')
.kaydet();
Gang of Four tarafından Design Patterns da ünlü olarak belirtildiği gibi, yapabildiğiniz yerlerde miras(kalıtım) yerine kompozisyonu tercih etmelisiniz. Miras(kalıtım)ı kullanmak için birçok iyi sebep olduğu gibi kompozisyonu kullanmak içinde birçok iyi sebep var. Bu kural için asıl nokta, eğer aklınız içgüdüsel olarak miras(kalıtım)ı tercih ediyorsa, kompozisyonun, probleminizi daha iyi modelleyebileceğini düşünmeye çalışın. Bazı durumlarda bu olabilir.
"Miras(kalıtım)ı ne zaman kullanmalıyım?" diye merak ediyor olabilirsiniz. Bu durum elinizdeki soruna bağlı ama bu, ne zaman miras(kalıtım)ın kompozisyondan daha anlamlı olduğunun kabul edilebilir bir listesi.
- Miras(kalıtım)ınız, "-dır, -dir" ilişkisini sağlıyor ve "sahiplik" ilişkisinin sağlamıyor. (İnsan->Hayvan vs. Kullanıcı->KullanıcıDetayları)
- Kodu temel sınıflardan yeniden kullanabilirsiniz. (İnsanlar, tüm hayvanlar gibi hareket edebilir)
- Bir temel sınıfı değiştirerek türetilmiş sınıflarda genel değişiklikler yapmak istiyorsunuz. (Hayvanların hareket ettiğinde harcadığı kaloriyi değiştirmek)
Kötü:
class Personel {
constructor(isim, mail) {
this.isim = isim;
this.mail = mail;
}
// ...
}
// Kötü çünkü Personeller vergi verisine "sahip". PersonelVergiVerileri, bir Personel türü değil.
class PersonelVergiVerileri extends Personel {
constructor(ssn, maas) {
super();
this.ssn = ssn; // sosyal güvenlik numarası
this.maas = maas;
}
// ...
}
İyi:
class PersonelVergiVerileri {
constructor(ssn, maas) {
this.ssn = ssn; // sosyal güvenlik numarası
this.maas = maas;
}
// ...
}
class Personel {
constructor(isim, mail) {
this.isim = isim;
this.mail = mail;
}
vergiVerisiniBelirle(ssn, maas) {
this.vergiVerisi = new PersonelVergiVerileri(ssn, maas);
}
// ...
}
Temiz Kod'da belirtildiği gibi, "Bir sınıfın değişebilmesi için asla birden fazla sebep olmamalıdır". Birçok işlevsellikle birlikte bir sınıfı sıkıştırmak cezbedicidir, tıpkı bir uçuşda yalnızca bir valiz almak gibi. Bununla ilgili mesele, sınıfınızın kavramsal olarak uyum sağlamayacağı ve değişmesi için birçok neden vereceğidir. Bir sınıfı değiştirmeniz için gereken süreyi en aza indirgemek önemlidir. Çünkü çok fazla işlevsellik bir sınıfta bulunuyorsa ve siz bir kısmını değiştirirseniz, bu değişikliğin kod tabanınızdaki diğer bağımlı modülleri nasıl etkileyeceğini anlamanız zor olabilir.
Kötü:
class UserSettings {
constructor(user) {
this.user = user;
}
changeSettings(settings) {
if (this.verifyCredentials()) {
// ...
}
}
verifyCredentials() {
// ...
}
}
İyi:
class UserAuth {
constructor(user) {
this.user = user;
}
verifyCredentials() {
// ...
}
}
class UserSettings {
constructor(user) {
this.user = user;
this.auth = new UserAuth(user);
}
changeSettings(settings) {
if (this.auth.verifyCredentials()) {
// ...
}
}
}
Bertrand Meyer tarafından belirtildiği gibi, "yazılım varlıkları (classlar, modüller, fonksiyonlar vs.) gelişime açık, değişime kapalı olmalıdır." Peki bu ne anlamaya geliyor? Bu ilke temel olarak, kullanıcıların varolan kodu değiştirmeden yeni işlevler ekleyebilmesini sağlamamız gerektiğini belirtir.
Kötü:
class AjaxAdapter extends Adapter {
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
}
İyi:
class AjaxAdapter extends Adapter {
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
});
}
}
Bu terim bu kadar basit bir konsept için korkutucu. Resmi olarak tanımı şöyle: "Eğer S, T'nin alt türü ise, programın istenilen özelliklerinden herhangi birini değiştirmeden(doğruluğunu, yaptığı işi vb.) T tipindeki nesneler S tipindeki nesneler ile yer değiştirebilir (yani, S tipindeki nesneler T tipindeki nesnelerin yerine geçebilir). Bu daha da korkutucu bir tanım.
Bunun için en iyi açıklama: eğer bir ebeveyn sınıfınız ve alt sınıfınız varsa, temel sınıf ve alt sınıf, yanlış sonuçlar ortaya koymadan birbirlerinin yerine kullanılabilir. Hala kafa karıştırıcı olabilir, o zaman hadi klasik Kare-Dikdörtgen örneğine göz atalım. Matematiksel olarak kare bir dikdörtgendir ama eğer bunu kalıtım yoluyla, "is-a" ilişkisi kullanarak modellerseniz başınızın belaya girmesi çok gecikmeyecektir.
Kötü:
class Dikdortgen {
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(); // KÖTÜ: Kare için 25 döner. 20 olmalıydı.
dikdortgen.olustur(alan);
});
}
const dikdortgenler = [new Dikdortgen(), new Dikdortgen(), new Kare()];
genisDikdortgenlerOlustur(dikdortgenler);
İyi:
class Sekil {
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);
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".
Kötü:
class DOMTraverser {
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.
// ...
});
İyi:
class DOMTraverser {
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() {}
}
});
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.
Kötü:
class InventoryRequester {
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();
İyi:
class InventoryTracker {
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();
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.
Kötü:
import assert from 'assert';
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);
});
});
İyi:
import assert from 'assert';
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);
});
});
Callbackler kusursuz değildir , ve aşırı miktarda iç içe geçmeye neden olurlar. ES2015/ES6 ile birlikte Promiseler bir yerleşik evrensel tiptir. Onları kullan!
Kötü:
import { get } from 'request';
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');
}
});
}
});
İyi:
import { get } from 'request';
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);
});
Promiseler Callbacklere nazaran daha temizdir, fakat ES2017/ES8 daha
temiz bir çözüm sunan async await'i getirdi. Tek ihtiyacın async
önekine sahip bir fonksiyon,
ve sonrasında then
li fonksiyonlar zincirini kullanmaksızın
mantığını zorunlu olarak yazabilirsin. ES2017 / ES8 özelliklerinden yararlanabiliyorsanız bunu
bugün kullanın!.
Kötü:
import { get } from 'request-promise';
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);
});
İyi:
import { get } from 'request-promise';
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);
}
}
Hatalar oluşturmak iyi bir şeydir. Hatalar size programınızda bir şeylerin yolunda olmadığını söylemenin en iyi yoludur. Çalışan bir kod parçacığı ya da çalışmayı durduran bir fonksiyonun, process'in neden durduğuna dair konsol ekranında sizi bilgilendirirler.
Yakalanan bir hata ile hiçbir şey gerçekleştirmemek, size o hatayı tamamen fixlemiş olma imkanı sunmaz.
Hataları (console.log
) ile göstermek, tıpkı suya yazı yazmak gibidir. Çoğu zaman yetersizdir.
Eğer kod bölümlerini try/catch
blokları ile oluşturuyorsanız o bölümde bir hatanın oluşabileceğini düşünüyorsunuzdur.
Bu durumlar için bir planınız olmalı ya da bu durumları yönetebileceğiniz ayrı kod yapılarınız olmalı.
Kötü:
try {
hataFirlatabilecekFonksiyon();
} catch (hata) {
console.log(hata);
}
İyi:
try {
hataFirlatabilecekFonksiyon();
} catch (hata) {
// İlk seçenek (console.log'dan daha çok bilgilendirici):
console.error(hata);
// Diğer Seçenek:
kullaniciyaHataGoster(hata);
// Diğer Seçenek:
hatayiServiseBildir(hata);
// Ya da üçünü de yapabilirsiniz!!
}
Aynı sebepten dolayı try/catch
'ten kaynaklanan hataları gözardı etmemelisiniz.
Kötü:
verileriGetir()
.then((veri) => {
fonksiyonHataFirlatabilir(veri);
})
.catch((hata) => {
console.log(hata);
});
İyi:
verileriGetir()
.then((veri) => {
fonksiyonHataFirlatabilir(veri);
})
.catch((hata) => {
// İlk seçenek (console.log'dan daha çok bilgilendirici):
console.error(hata);
// Diğer Seçenek:
kullaniciyaHataGoster(hata);
// Diğer Seçenek:
hatayiServiseBildir(hata);
// Ya da üçünü de yapabilirsiniz!!
});
Yazım şekli özneldir. Buradaki birçok kural gibi, uymanız gereken zor ve sıkı bir kural yoktur. Yazım şekli üzerinde TARTIŞMAYIN. Bunları otomatikleştirmek için binlerce araç vardır. Birini kullanın! Mühendisler için, yazım şekli üzerinde tartışmak zaman ve para kaybıdır.
Otomatik formatlama kapsamına girmeyen şeyler (girintileme, tab veya boşluk, çift veya tek tırnak vb.) hakkında rehberlik için buraya bakın.
JavaScript'in bir yazım kuralı yoktur, bu yüzden büyük harf kullanımı size değişkenler, fonksiyonlar vb. şeyler hakkında birçok bilgi verir. Bu kurallar özneldir, yani ekibiniz istediğini seçebilir. Önemli olan neyi seçtiğiniz değildir, seçtiğinizde tutarlı olmanızdır.
Kötü:
const HAFTANIN_GUN_SAYISI = 7;
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 {}
İyi:
const HAFTANIN_GUN_SAYISI = 7;
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 {}
Eğer bir fonksiyon diğerini çağırıyorsa, kodda onları dikey olarak birbirine yakın tutun. İdeal olarak, çağıran fonksiyonu çağırılanın hemen üzerinde tutun. Kodları tıpkı gazete okur gibi yukarıdan aşağıya doğru okuruz. Bu nedenle, kodunuzun bu yolda okunabilmesini sağlayın.
Kötü:
class PerformansDegerlendirmesi {
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();
İyi:
class PerformansDegerlendirmesi {
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();
DocBlock sayesinde uygulamalarımızdaki fonksiyonlar, methodlar, sınıflar, modeller ve kontrollerin işlevlerini, değişkenlerini, geri dönüş değerlerini hatırlamamızı ve/veya kullandığımız IDElerde fonksiyonlar hakkında hızlı bilgilendirmeler almak için mutlaka öncelik verilmesi gerekiyor. Ayrıca yorum satırlarında yapılması gereken notlarımızı TODO: şeklinde tanımlamalar yaparsak en son eksik kalan yerleri hızlı hatırlamamıza yardımcı olabilir.
Kötü:
function FaizHesapla(Anapara,Gun,YillikOran,StopajOrani){
faiz=((Anapara*Gun*YillikOran)/36500)*(1-(StopajOrani/100));
return faiz;
}
function FonGetiri(Anapara,Gun,FonOrani){
//var Gun=((sonTarih-ilkTarih)/(1000*60*60*24)); hücre içerisinde "01.01.2019" olunca çalışmıyor
// tarih olan hücre seçildiğinde bu çalışmaktadır. ilerleyen günlerde google sheets script
// araştırma yapılacak
fon=(Anapara*(Gun)*(FonOrani/100));
return fon;
}
İyi:
/**
* Nakit Akışı Sınıfı.
*
* @fileOverview Nakit Akışında Kullanılan Özel 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 formül parametre olarak iletilen tutarın Net Faizini geri döndürür
* @param {100.000,00} Anapara Anapara Tutarı Yazılacak
* @param {5} Gun Gün Yazılacak
* @param {17,25} YillikOran Yıllık Faiz Oranı Yazılacak
* @param {15} StopajOrani Mevduat Stopaj Oranı Yazılacak
* @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 formül parametre olarak iletilen tutarın Net Fon Gelirini geri döndürür
* @param {1.000,00} Anapara Anapara Tutarı Yazılacak
* @param {5} Gun Gün Yazılacak
* @param {0,04} FonOrani Günlük Fon Getiri Oranı Yazılacak
* @return
* @customfunction
*/
function FonGetiri(Anapara,Gun,FonOrani){
// TODO: var Gun=((sonTarih-ilkTarih)/(1000*60*60*24)); hücre içerisinde "01.01.2019" olunca çalışmıyor
// TODO: tarih olan hücre seçildiğinde bu çalışmaktadır. ilerleyen günlerde google sheets script araştırma yapılacak
fon=(Anapara*(Gun)*(FonOrani/100));
return fon;
}
Yorumlar lükstür, zorunlu değildir. İyi kod çoğunlukla kendini belli eder.
Kötü:
function ozetCikar(veri) {
// Özet
let ozet = 0;
// data değişkeninin uzunluğu
const uzunluk = veri.length;
// veri değişkeninin her karakterini döngüye sok
for (let i = 0; i < uzunluk; i++) {
// Karakter kodunu getir
const karakter = veri.charCodeAt(i);
// Özetini çıkar
ozet = ((ozet << 5) - ozet) + karakter;
// 32-bit'lik sayıya çevir
ozet &= ozet;
}
}
İyi:
function ozetCikar(veri) {
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 sayıya çevir
ozet &= ozet;
}
}
Sürüm kontrol sistemleri bu nedenle var. Eski kodu geçmişinizde bırakın.
Kötü:
birSeyYap();
// baskaBirSeyYap();
// birazDahaBirSeyYap();
// dahaFazlaBirSeyYap();
İyi:
birSeyYap();
Sürüm kontrol sistemlerini kullanmanız gerektiğini hatırlayın! Ölü koda, yorum satırına alınmış koda ve
özellikle günlüğe çevrilmiş yorum satırına gerek yok. Önceki yapılanları almak için git log
komutunu kullanın!
Kötü:
/**
* 2016-12-20: Monadları kaldırdım, onları anlamadım (RM)
* 2016-10-01: Özel monadları kullanarak geliştirdim (JP)
* 2016-02-03: Tip denetimini kaldırdım (LI)
* 2015-03-14: Topla fonksiyonunu ekledim (JR)
*/
function topla(a, b) {
return a + b;
}
İyi:
function topla(a, b) {
return a + b;
}
Onlar sadece kuru gürültüden ibaret. Fonksiyonlar ve değişkenlerin uygun girintilemeler, yoluyla kodunuza görsel şeklini vermesine izin verin.
Kötü:
////////////////////////////////////////////////////////////////////////////////
// Scope Model Örneği
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
menu: 'foo',
nav: 'bar'
};
////////////////////////////////////////////////////////////////////////////////
// Eylem tanımlanması
////////////////////////////////////////////////////////////////////////////////
const eylemler = function() {
// ...
};
İyi:
$scope.model = {
menu: 'foo',
nav: 'bar'
};
const eylemler = function() {
// ...
};
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/