- Giriş
- Değişkenler
- Fonksiyonlar
- Nesneler Ve Veri Yapıları
- Sınıflar
- SOLID
- Test etme
- Eşzamanlılık
- Hata Yakalama
- Yazım Şekli
- Yorumlar
- Çeviri
Yazlım mühendisliği prensipleri, Robert C. Martin'in kitabından Clean Code, JavaScript için uyarlandı. Bu belge bir kod yazma semantik rehberi değildir. Bu belge JavaScript ile okunabilir, yeniden kullanılabilir ve elden geçirilebilir yazılım üretebilmek için kullanılabilecek bir rehber oluşturmak için yazıldı.
Buradaki her ilkeye kesinlikle uyulması gerekmiyor ve hatta bir çoğu evrensel olarak kabul edilmeyebilir. Bunlar yönergelerdir, daha fazlası değil, ama uzun yıllar boyunca edindikleri toplu tecrübe ile Clean Code kitabı yazarlarınca derlenmiş olanlardır.
Yazılım mühendisliği zanaatı 50 yaşın biraz üzerinde ve hala çok şey öğreniyoruz. Yazılım mimarisi mimarlığın kendisi kadar eskidiğinde, belki o zaman uyması gereken daha sağlam kurallar olacaktır. Şimdilik, bu kuralların sizin ve ekibinizin ürettiği JavaScript kodunun kalitesini değerlendirmek için bir mihenk taşı olarak hizmet etmesine izin verin.
Bir şey daha var: bunları bilmek sizi hemen daha iyi bir yazılım geliştiricisi yapmaz ve bunlarla yıllarca çalışmış olmak hiç hata yapmayacağınız anlamına da gelmez. Her kod parçası, şekillendirilip son haline çevirilen ıslak kilin gibi ilk taslak olarak başlar. Son olarak, akranlarımızla birlikte gözden geçirdiğimiz zaman kusurları gideririz. İyileştirilmesi gereken ilk taslaklar için kendinize eziyet etmeyin. Bunun yerine kodu yorun!
Yanlış:
const yyyymmdstr = moment().format("YYYY/MM/DD");
Doğru:
const currentDate = moment().format("YYYY/MM/DD");
Yanlış:
getUserInfo();
getClientData();
getCustomerRecord();
Doğru:
getUser();
Yazdığımızdan daha çok kod satırı okuruz. Dolayısıyla yazdığımız kodun okunabilir ve aranabilir olması önemlidir. Değişkenlerimize programımızın ne yapmaya çalıştığını anlatacak anlamlı isimler vermezsek kodumuzu okumaya çalışanlar çok üzülecektir. Verdiğiniz isimlerin kolayca aranabilir olmasını sağlayın. Bunun için buddy.js ve ESLint gibi araçlar size isimlendirme hatalarınızı gösterek yardımcı olabilir.
Yanlış:
// Bu 86400000 ne anlama geliyor?
setTimeout(blastOff, 86400000);
Doğru:
// Büyük harfli constant olarak tanımlayın.
const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000; //86400000;
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
Yanlış:
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
address.match(cityZipCodeRegex)[1],
address.match(cityZipCodeRegex)[2]
);
Doğru:
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
Açık olmak kapalı olmaktan daha iyidir.
Yanlış:
const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(l => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Dur, bu l ne demekti?
dispatch(l);
});
Doğru:
const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(location => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch(location);
});
Eğer sınıf ya da nesne ne yaptığını söylüyprsa, değişken isminde tekrar belirtmenize gerek yok.
Yanlış:
const Car = {
carMake: "Honda",
carModel: "Accord",
carColor: "Blue"
};
function paintCar(car) {
car.carColor = "Red";
}
Doğru:
const Car = {
make: "Honda",
model: "Accord",
color: "Blue"
};
function paintCar(car) {
car.color = "Red";
}
Ön tanımlı parametre kullanmak kısa kontrol yapılarında genellikle daha temiz kod oluşturur. Bu şekilde kullandığınızda, kodunuzun sadece undefined
olan parametreler için ön tanımlı değer sağlayacağını unutmayın. ''
, ""
, false
, null
, 0
, ve NaN
gibi "hatalı" değerler ön tanımlı değer ile değiştirilmeyecektir.
Yanlış:
function createMicrobrewery(name) {
const breweryName = name || "Hipster Brew Co.";
// ...
}
Doğru:
function createMicrobrewery(name = "Hipster Brew Co.") {
// ...
}
Fonksiyon parametrelerinin sayısının sınırlandırılması, fonksiyonunuzun test edilmesini kolaylaştırdığı için inanılmaz derecede önemlidir. Üçten fazlaya sahip olmak, her bir ayrı argümanla tonlarca farklı durumu test etmeniz gereken bir kombinasyon patlamasına yol açar.
Bir veya iki argüman ideal durumdur ve mümkünse üçten kaçınılmalıdır. Bundan fazlası tekrar düşünülmelidir. Çoğunlukla, 2 den fazla parametreye sahip bir fonksiyonunuz varsa, yapması gerektiğinden fazla iş yapıyordur. Gerçekten gerekli olduğu durumda, çoğu zaman bir üst seviye nesne argüman olarak yeterli olacaktır.
JavaScript, çok fazla sınıf tanımlamadan anında nesneler oluşturmanıza izin verdiğinden, çok fazla argümana ihtiyaç duyduğunuzu tespit ediyorsanız, anlık oluşturduğunuz bir nesneyi kullanabilirsiniz..
Fonksiyonun hangi özellikleri beklediğini açıkça belirtmek için, ES2015/ES6 "destructuring" sözdizimini kullanabilirsiniz. Bunun bazı avantajları vardır:
- Fonksiyonun tanımına bakıldığında, nelerin kullanıldığı oldukça açıktır.
- Adlandırılmış parametreleri simüle etmek için kullanılabilir.
- İmha yöntemi, fonksiyona geçilen parametre nesnesinin belirtilen ilkel değerlerini de klonlar. Bu, yan etkilerin önlenmesine yardımcı olabilir. Not: parametre nesnesinden imha edilen nesneler ve diziler klonlanmaz.
- Linter kontrolleri kullanılmayan özellikler hakkında sizi uyarır, ki bu destructuring yöntemi olmadan mümkün değildir.
Yanlış:
function createMenu(title, body, buttonText, cancellable) {
// ...
}
Doğru:
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
createMenu({
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
});
Bu, yazılım mühendisliğinde belki de en önemli kuraldır. Eğer bir fonksiyon birden fazla iş yapıyorsa, bu fonksiyonu oluşturmaki test etmek ve anlamlandırmak zordur. Eğer bir fonksiyonu sadece bir işe yapacak şekilde sınırlandırırsanız, kolayca elden geçirilebilir ve kodunuz daha okunaklı olur. Bu rehberden birtek bunu alsanız bile, birçok geliştiriciden önde olacaksınız.
Yanlış:
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
Doğru:
function emailActiveClients(clients) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
Yanlış:
function addToDate(date, month) {
// ...
}
const date = new Date();
// Fonksiyon isminden neyin eklendiği anlaşılmıyor
addToDate(date, 1);
Doğru:
function addMonthToDate(month, date) {
// ...
}
const date = new Date();
addMonthToDate(1, date);
Birden fazla soyutlama seviyesine sahipseniz, fonksiyon genellikle çok fazla şey yapar. Fonksiyonları bölmek yeniden kullanılabilirliğe ve daha kolay testlere neden olur.
Yanlış:
function parseBetterJSAlternative(code) {
const REGEXES = [
// ...
];
const statements = code.split(" ");
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
// ...
});
});
const ast = [];
tokens.forEach(token => {
// lex...
});
ast.forEach(node => {
// parse...
});
}
Doğru:
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const syntaxTree = parse(tokens);
syntaxTree.forEach(node => {
// parse...
});
}
function tokenize(code) {
const REGEXES = [
// ...
];
const statements = code.split(" ");
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
tokens.push(/* ... */);
});
});
return tokens;
}
function parse(tokens) {
const syntaxTree = [];
tokens.forEach(token => {
syntaxTree.push(/* ... */);
});
return syntaxTree;
}
Kod çoklanmasını engellemek için elinizden geleni yapın. Çoklanmış kod kötüdür çünkü bir sorun olduğunda ya da değiştirilmesi gerektiğinde ilgilenilmesi gereken birden fazla yer var demektir.
Bir restoran işlettiğinizi ve envanterinizi takip ettiğinizi düşünün: tüm domatesleriniz, soğanlarınız, sarımsaklarınız, baharatlarınız vb. Eğer birden fazla listeniz varsa, bir yemek yaptığınızda hepsini güncellemeniz gerekecektir. Yalnızca bir listeniz varsa, güncellenecek tek bir yer vardır!
Çoğunlukla, yinelenen kod yapılarınız vardır. Çünkü bir ortak işlemi paylaşan iki veya daha fazla farklı fonksiyonunuz olabilir. Ancak bazı ufak farklılıklar sizi aynı şeyleri yapan iki veya daha fazla ayrı fonksiyona sahip olmaya zorlar. Çift kodun kaldırılması, bu farklı şeyleri tek bir işlev/modül/sınıfla işleyebilecek bir soyutlama oluşturmak anlamına gelir.
Soyutlamayı doğru yapmak çok önemlidir, bu yüzden Sınıflar bölümünde belirtilen SOLID ilkelerine uymalısınız. Kötü soyutlamalar yinelenen kodlardan daha da kötü olabilir, bu yüzden dikkatli olun! Bunu söyledikten sonra, iyi bir soyutlama yapabilirseniz yapın! Kendinizi tekrar etmeyin, aksi halde, bir şeyi değiştirmek istediğinizde kendinizi birden çok yeri güncellerken bulacaksınız.
Yanlış:
function showDeveloperList(developers) {
developers.forEach(developer => {
const expectedSalary = developer.calculateExpectedSalary();
const experience = developer.getExperience();
const githubLink = developer.getGithubLink();
const data = {
expectedSalary,
experience,
githubLink
};
render(data);
});
}
function showManagerList(managers) {
managers.forEach(manager => {
const expectedSalary = manager.calculateExpectedSalary();
const experience = manager.getExperience();
const portfolio = manager.getMBAProjects();
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}
Doğru:
function showEmployeeList(employees) {
employees.forEach(employee => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
const data = {
expectedSalary,
experience
};
switch (employee.type) {
case "manager":
data.portfolio = employee.getMBAProjects();
break;
case "developer":
data.githubLink = employee.getGithubLink();
break;
}
render(data);
});
}
Yanlış:
const menuConfig = {
title: null,
body: "Bar",
buttonText: null,
cancellable: true
};
function createMenu(config) {
config.title = config.title || "Foo";
config.body = config.body || "Bar";
config.buttonText = config.buttonText || "Baz";
config.cancellable =
config.cancellable !== undefined ? config.cancellable : true;
}
createMenu(menuConfig);
Doğru:
const menuConfig = {
title: "Order",
// Kullanıcı 'body' değerini atamamış
buttonText: "Send",
cancellable: true
};
function createMenu(config) {
config = Object.assign(
{
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
},
config
);
// config artık bu şekilde: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
Karar verici parametreler bir fonksiyonun birden fazla iş yaptığını gösterir. Fonksiyonlar tek iş yapmalılar. Boolean bir değer üzerinden yapacağı işe karar veren fonksiyonları birden fazla fonksiyona bölün.
Yanlış:
function createFile(name, temp) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
Doğru:
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile(`./temp/${name}`);
}
Bir fonksiyon, bir değeri almak ve başka bir değer veya değerler döndürmekten başka bir şey yaparsa, bir yan etki oluşturur. Bu yan etki bir dosyaya yazmak, bazı global değişkenleri değiştirmek veya yanlışlıkla tüm paranızı bir yabancıya bağlamak olabilir.
Zaman zaman bir programda yan etkilere ihtiyacınız olur. Önceki örnekte olduğu gibi, bir dosyaya yazmanız gerekebilir. Yapmak istediğiniz şey, bunu yaptığınız yeri merkezileştirmektir. Belirli bir dosyaya yazan çok sayıda fonksiyon ve sınıfa sahip olmayın. Bunu yapan bir servis oluşturun. Sadece bir tane.
Ana nokta, herhangi bir yapıya sahip olmadan nesneler arasında durum paylaşımı, herhangi bir şey tarafından yazılabilen değişken yanları kullanmak ve yan etkilerin ortaya çıktığı yerleri merkezileştirmemek gibi çok yapılan hatalarda kaçınmaktır. Bunu yapabilirseniz, diğer programcıların büyük çoğunluğundan daha mutlu olursunuz.
Yanlış:
// Global değişken aşağıdaki fonksiyon tarafındna kullanılıyor.
// Eğer başka bir fonksiyon da bunu kullanıyorsa, bu artık array olduğu için sorun çıkarabilir..
let name = "Ryan McDermott";
function splitIntoFirstAndLastName() {
name = name.split(" ");
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];
Doğru:
function splitIntoFirstAndLastName(name) {
return name.split(" ");
}
const name = "Ryan McDermott";
const newName = splitIntoFirstAndLastName(name);
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
JavaScript'te, ilkel değerler değer olarak ve nesneler/diziler referans olarak iletilir. Nesneler ve diziler söz konusu olduğunda, işleviniz bir
alışveriş sepeti dizisinde bir değişiklik yaparsa, örneğin satın almak için bir öğe eklerse, bu cart
dizisini kullanan diğer işlevler bu eklemeden etkilenir. Bu iyi olabilir, ancak kötü de olabilir. Bunu anlamak için aşağıdaki örneği düşünelim:
Kullanıcı, bir ağ isteğini başlatan ve cart
dizisini sunucuya gönderen bir satın alma
fonksiyonunu çağıran "Purchase" düğmesini tıklar. Kötü bir ağ bağlantısı nedeniyle, satın alma
işlevinin isteği yeniden denemeye devam etmesi gerekir. Şimdi, bu arada kullanıcı yanlışlıkla ağ isteği başlamadan önce istemediği bir öğe üzerinde "Sepete Ekle" düğmesini yanlışlıkla tıklarsa ne olur? Bu gerçekleşirse ve ağ isteği başlarsa, o zaman satın alma işlevi yanlışlıkla eklenen öğeyi gönderir, çünkü istenmeyen bir öğe eklenerek değiştirilmiş bir addItemToCart
işlevinin değiştirdiği bir alışveriş sepeti dizisine bir başvuru yapar.
AddItemToCart
için her zaman cart
'ı klonlamak, düzenlemek ve klonu geri göndermek
için harika bir çözüm olacaktır. Bu, alışveriş sepetinin referansını tutan başka hiçbir
fonksiyonun herhangi bir değişiklikten etkilenmemesini sağlar.
Bu yaklaşımda hatırlanması gereken iki uyarı:
-
Giriş nesnesini gerçekten değiştirmek istediğiniz durumlar olabilir, ancak bu programlama yaklaşımını uyguladığınızda, bu durumların oldukça nadir olduğunu göreceksiniz. Çoğu şey yan etkisi olmayacak şekilde yeniden yapılandırılabilir!
-
Büyük nesneleri klonlamak, performans açısından çok pahalı olabilir. Neyse ki, bu uygulamada büyük bir sorun değil çünkü bu tür bir programlama yaklaşımının hızlı ve hafıza yoğun olmamasına izin veren ve nesneleri ve dizileri elle klonlamanızdan daha verimli yapan kütüphaneler var.
Yanlış:
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
Doğru:
const addItemToCart = (cart, item) => {
return [...cart, { item, date: Date.now() }];
};
Global kapsamı kirletmek JavaScript'te kötü bir uygulamadır çünkü başka bir kütüphaneyle çakışabilirsiniz ve API'nizin kullanıcısı canlıda bir istisna ile karşılaşana bundan haberdar olmaz. Bir örnek düşünelim: ya JavaScript'in yerel Array
yöntemini iki dizi arasındaki farkı gösterebilecek bir diff
yöntemine sahip olarak genişletmek istiyorsanız? Yeni işlevinizi Array.prototype dosyasına yazabilirsiniz, ancak aynı şeyi yapmaya çalışan başka bir kitaplıkla çakışabilir. Ya bu diğer kütüphane dizinin ilk ve son elemanları arasındaki farkı bulmak için sadece diff
kullanıyorsa? Bu yüzden sadece ES2015/ES6 sınıflarını kullanmak ve sadece Array
sınıfını genişletmek daha iyi olur.
Yanlış:
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
Doğru:
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
JavaScript, Haskell'in olduğu gibi fonksiyonel bir dil değildir, ancak fonksiyonel bir tadı vardır. Fonksiyonel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiğiniz zaman bu programlama stilini tercih edin.
Yanlış:
const programmerOutput = [
{
name: "Uncle Bobby",
linesOfCode: 500
},
{
name: "Suzie Q",
linesOfCode: 1500
},
{
name: "Jimmy Gosling",
linesOfCode: 150
},
{
name: "Gracie Hopper",
linesOfCode: 1000
}
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
Doğru:
const programmerOutput = [
{
name: "Uncle Bobby",
linesOfCode: 500
},
{
name: "Suzie Q",
linesOfCode: 1500
},
{
name: "Jimmy Gosling",
linesOfCode: 150
},
{
name: "Gracie Hopper",
linesOfCode: 1000
}
];
const totalOutput = programmerOutput.reduce(
(totalLines, output) => totalLines + output.linesOfCode,
0
);
Yanlış:
if (fsm.state === "fetching" && isEmpty(listNode)) {
// ...
}
Doğru:
function shouldShowSpinner(fsm, listNode) {
return fsm.state === "fetching" && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
Yanlış:
function isDOMNodeNotPresent(node) {
// ...
}
if (!isDOMNodeNotPresent(node)) {
// ...
}
Doğru:
function isDOMNodePresent(node) {
// ...
}
if (isDOMNodePresent(node)) {
// ...
}
Bu imkansız bir iş gibi görünür. Bunu ilk duyduklarında, çoğu insan "bir if ifadesi olmadan nasıl bir şey yapabilirim?" der. Cevap, birçok
durumda aynı görevi gerçekleştirmek için polimorfizmi kullanabileceğinizdir. İkinci soru genellikle, "peki bu harika ama neden bunu yapmak isteyeyim?", bu cevap ise daha önce öğrendiğimiz bir temiz kod kavramıdır: bir işlev yalnızca bir şey yapmalıdır. İf
deyimine sahip sınıf ve işlevleriniz olduğunda, kullanıcınıza işlevinizin birden fazla şey yaptığını söylersiniz. Unutmayın, sadece bir şey yapın.
Yanlış:
class Airplane {
// ...
getCruisingAltitude() {
switch (this.type) {
case "777":
return this.getMaxAltitude() - this.getPassengerCount();
case "Air Force One":
return this.getMaxAltitude();
case "Cessna":
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
}
Doğru:
class Airplane {
// ...
}
class Boeing777 extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getPassengerCount();
}
}
class AirForceOne extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude();
}
}
class Cessna extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
JavaScript'te veri tiplerini tanımlamak zorunlu değildir, yani işlevleriniz herhangi bir tipte parametre alabilir. Bazen bu özgürlükten sorun çıkarabiliyor ve fonksiyonlarınızda tip kontrolü yapmak cazip hale gelebiliyor. Bunu yapmaktan kaçınmanın birçok yolu vardır. Dikkate alınacak ilk şey tutarlı API'ler.
Yanlış:
function travelToTexas(vehicle) {
if (vehicle instanceof Bicycle) {
vehicle.pedal(this.currentLocation, new Location("texas"));
} else if (vehicle instanceof Car) {
vehicle.drive(this.currentLocation, new Location("texas"));
}
}
Doğru:
function travelToTexas(vehicle) {
vehicle.move(this.currentLocation, new Location("texas"));
}
Dizeler ve tam sayılar gibi temel ilkel değerlerle çalışıyorsanız,ve polimorfizmi kullanamıyorsanız ancak hala tip kontrolü yapma ihtiyacı hissederseniz, TypeScript kullanmayı düşünmelisiniz. Standart JavaScript sözdiziminin üstüne statik yazma özelliği sağladığından normal JavaScript'e mükemmel bir alternatiftir. Normal JavaScript'i manuel olarak kontrol etmekdeki sorun, bunu iyi yapmanın, aldığınız sahte "tür güvenliği" nin kayda değer okunabilirlik için telafi etmeyecek kadar fazladan ek yük gerektirmesidir. JavaScript'inizi temiz tutun, iyi testler yazın ve iyi kod incelemeleri yapın. Aksi takdirde, hepsini yapın ama TypeScript ile (dediğim gibi, harika bir alternatif!).
Yanlış:
function combine(val1, val2) {
if (
(typeof val1 === "number" && typeof val2 === "number") ||
(typeof val1 === "string" && typeof val2 === "string")
) {
return val1 + val2;
}
throw new Error("Must be of type String or Number");
}
Doğru:
function combine(val1, val2) {
return val1 + val2;
}
Modern tarayıcılar çalışma zamanında bir çok optimizasyon ön tanımlı olarak yaparlar. Çoğu zaman, kendiniz optimize etmeye çalışıyorsanız, zamanınızı boşa harcıyorsunuz demektir. Nerelerde optimizasyonun eksik olduğunu görmek için iyi kaynaklar var. Buradaki sorunlara çözülene kadar optimizasyon yapmayı hedefleyin.
Yanlış:
// Eski tarayıcılarda döngünün her turunda `list.length` ön belleğe alınmadığı için pahalıydı
// çünkü her defa `list.length` tekrardan hesaplanıyordu. Modern tarayıcılarda bu sorun giderildi.
for (let i = 0, len = list.length; i < len; i++) {
// ...
}
Doğru:
for (let i = 0; i < list.length; i++) {
// ...
}
Ölü kod çoklanmış kod kadar kötüdür. Kod yığınınızda tutulmaları için bir sebep yoktur. Eğer kullanılmıyorsa, ondan kurtulun! Sürüm kontrol sistemlerinde geriye dönebildiğiniz için lazım olduğunda bulabilirsiniz.
Yanlış:
function oldRequestModule(url) {
// ...
}
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker("apples", req, "www.inventory-awesome.io");
Doğru:
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker("apples", req, "www.inventory-awesome.io");
Nesnelerdeki verilere erişmek için getter ve setter kullanmak, yalnızca bir nesnede özelliğe direk erişmekten daha iyi olabilir. "Neden?" diye sorabilirsin. Peki, işte dağınık bir sebepler listesi:
- Bir nesne özelliği elde etmenin ötesinde daha fazla şey yapmak istediğinizde, kod tabanınızdaki her erişimi aramanız ve değiştirmeniz gerekmez.
- Atama (
set
) yaparken doğrulama yapmanızı kolaylaştırır. - İç gösterimi koruyabilirsiniz.
- Değer alırken ya da atama yaparken log ve hata kaydı oluşturmak kolaylaşır.
- Nesnenizin özelliklerini tembel yaklaşımla yükleyebilirsiniz, örneğin bir sunucudan veri almayı düşünelim.
Yanlış:
function makeBankAccount() {
// ...
return {
balance: 0
// ...
};
}
const account = makeBankAccount();
account.balance = 100;
Doğru:
function makeBankAccount() {
// this one is private
let balance = 0;
// "getter", her yerden erişilebilir olmalı
function getBalance() {
return balance;
}
// "setter", her yerden erişilebilir olmalı
function setBalance(amount) {
// ... güncellemeden önce doğrulama yapabilirsiniz
balance = amount;
}
return {
// ...
getBalance,
setBalance
};
}
const account = makeBankAccount();
account.setBalance(100);
Bu closer vasıtasıyla sağlanabilir (ES5 ve daha eski sürümler için).
Yanlış:
const Employee = function(name) {
this.name = name;
};
Employee.prototype.getName = function getName() {
return this.name;
};
const employee = new Employee("John Doe");
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
Doğru:
function makeEmployee(name) {
return {
getName() {
return name;
}
};
}
const employee = makeEmployee("John Doe");
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
Klasik ES5 sınıfları için okunabilir sınıf mirası, construction ve yöntem tanımları elde etmek çok zor. Kalıtıma ihtiyacınız varsa (ve olmaması gerektiğinin farkında olun), o zaman ES2015/ES6 sınıflarını tercih edin. Bununla birlikte, kendinizi daha büyük ve daha karmaşık nesnelere ihtiyaç duyana kadar sınıflara göre küçük fonksiyonlar tercih edin.
Yanlış:
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() {};
Doğru:
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 kalıp JavaScript'te çok kullanışlıdır ve bu kullanımı jQuery ve Lodash gibi birçok kütüphanede görürsünüz. Kodunuzun anlamlı ve daha az ayrıntılı olmasını sağlar. Bu nedenle diyelim ki, yöntem zincirleme kullanın ve kodunuzun ne kadar temiz olacağına bakın. Sınıf tanımlamalarınızda, basitçe her yöntemin sonuna this
döndürün ve bunun üzerine daha fazla sınıf yöntemi zincirleyebilirsiniz.
Yanlış:
class Car {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}
setMake(make) {
this.make = make;
}
setModel(model) {
this.model = model;
}
setColor(color) {
this.color = color;
}
save() {
console.log(this.make, this.model, this.color);
}
}
const car = new Car("Ford", "F-150", "red");
car.setColor("pink");
car.save();
Doğru:
class Car {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}
setMake(make) {
this.make = make;
// NOTE: Returning this for chaining
return this;
}
setModel(model) {
this.model = model;
// NOTE: Returning this for chaining
return this;
}
setColor(color) {
this.color = color;
// NOTE: Returning this for chaining
return this;
}
save() {
console.log(this.make, this.model, this.color);
// NOTE: Returning this for chaining
return this;
}
}
const car = new Car("Ford", "F-150", "red").setColor("pink").save();
Dörtlü Çetenin Design Patterns makalesinde ünlü bir şekilde belirtildiği gibi, kalıtım yapabileceğiniz yerde kompozisyonu tercih etmelisiniz. Kalıtım kullanmak için birçok neden ve kompozisyon kullanmak için de birçok neden vardır. Bu en üst noktadaki ana nokta, eğer zihniniz içgüdüsel olarak kalıtım kullanmaya meylederse, kompozisyonun probleminizi daha iyi modelleyebileceğini düşünmeye çalışmanızdır. Bu bazı durumlarda doğrudur.
"Kalıtımı ne zaman kullanmalıyım?" diye merak ediyor olabilirsiniz. Elinizdeki probleminize bağlıdır, işte size kalıtımın kompozisyondan daha mantıklı olduğu durumların uygun bir liste:
- Kalıtım, "is-a" ilişkisini temsil eder, "has-a" ilişkisini temsil etmez (Human->Animal doğru, User->UserDetail yanlıştır.).
- Ana sınıftan gelen kodu kullanabildiğinizde (İnsanlar hayvanlar gibi hareket edebilirler).
- Bir temel sınıfı değiştirerek türetilmiş sınıflarda genel değişiklikler yapmak istiyorsanız. (Hareket halindeyken tüm hayvanların kalori harcamalarını güncellemek).
Yanlış:
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
// ...
}
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
constructor(ssn, salary) {
super();
this.ssn = ssn;
this.salary = salary;
}
// ...
}
Doğru:
class EmployeeTaxData {
constructor(ssn, salary) {
this.ssn = ssn;
this.salary = salary;
}
// ...
}
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
setTaxData(ssn, salary) {
this.taxData = new EmployeeTaxData(ssn, salary);
}
// ...
}
Clean Code kitabında söylendiği gibi, "Bir sınıfın değişmesi için hiçbir zaman birden fazla sebep olmamalıdır". Uçuşunuzda yalnızca bir valiz alabildiğiniz zamanki gibi bir çok işlevi olan bir sınıfı sıkıştırarak çıkarmak cazip gelir. Bunun sorun olmasının sebebi, bu durumda sınıfınızın kavramsal olarak uyumlu olmaması ve değişmesi için birçok nedene sahip olmasıdır. Bir sınıfı gerektiğinde değiştirmek için ihtiyacınız duyulan zamanı azaltmanız önemlidir. Bu önemlidir, çünkü çok fazla işlevsellik bir sınıftaysa ve bir parçasını değiştirirseniz, bunun kod tabanınızdaki diğer bağımlı modülleri nasıl etkileyeceğini anlamak zor olabilir.
Yanlış:
class UserSettings {
constructor(user) {
this.user = user;
}
changeSettings(settings) {
if (this.verifyCredentials()) {
// ...
}
}
verifyCredentials() {
// ...
}
}
Doğru:
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ı (sınıflar, modüller, fonksiyonlar, vb.) genişlemek için açık, ancak değişiklik için kapalı olmalıdır". Bu ne anlama geliyor? Bu ilke, temel olarak, kullanıcıların mevcut kodu değiştirmeden yeni işlevler eklemelerine izin vermeniz gerektiğini belirtir.
Yanlış:
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 === "nodeAdapter") {
return makeHttpCall(url).then(response => {
// transform response and return
});
}
}
}
function makeAjaxCall(url) {
// request and return promise
}
function makeHttpCall(url) {
// request and return promise
}
Doğru:
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 çok basit bir kavram için karmaşık bir terimdir. Resmen "S, T'nin bir alt tipi ise, o zaman T tipi olan nesneler, bu programın istenen özelliklerinden herhangi birini değiştirmeden, S tipi olan nesnelerle (yani, S tipi objelerin yerini alabilir), T tipi objelerin yerini alabilir. (doğruluk, yapılan görev vb.) Bu daha da karmaşık bir tanım.
Bunun için en iyi açıklama bir ana sınıfınız ve bir alt sınıfınız varsa, temel sınıf ve alt sınıf yanlış sonuçlar alınmadan birbirlerinin yerine kullanılabilir. Bu hala kafa karıştırıcı olabilir, o yüzden klasik kare dikdörtgen örneğine bakalım. Matematiksel olarak, bir kare bir dikdörtgendir, ancak kalıtsallık yoluyla "is-a" ilişkisini kullanarak modellerseniz, derhal başınız derde girer.
Yanlış:
class Rectangle {
constructor() {
this.width = 0;
this.height = 0;
}
setColor(color) {
// ...
}
render(area) {
// ...
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
setWidth(width) {
this.width = width;
this.height = width;
}
setHeight(height) {
this.width = height;
this.height = height;
}
}
function renderLargeRectangles(rectangles) {
rectangles.forEach(rectangle => {
rectangle.setWidth(4);
rectangle.setHeight(5);
const area = rectangle.getArea(); // BAD: Kare için 25 döner. 20 olmalı.
rectangle.render(area);
});
}
const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);
Doğru:
class Shape {
setColor(color) {
// ...
}
render(area) {
// ...
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Shape {
constructor(length) {
super();
this.length = length;
}
getArea() {
return this.length * this.length;
}
}
function renderLargeShapes(shapes) {
shapes.forEach(shape => {
const area = shape.getArea();
shape.render(area);
});
}
const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
renderLargeShapes(shapes);
JavaScript'te interface yoktur, bu nedenle bu ilke diğerleri kadar uygulanabilir değildir. Bununla birlikte, bu kavramJavaScript’in tip sistem eksikliğinde bile faydalı ve önemlidir.
Arayüz Ayrım Prensibi (ISP) "İstemcilerin kullanmadıkları arayüzlere bağlı kalmaması gerektiğini" belirtmektedir. Arayüzler, kendi zayıf yapısı nedeniyle JavaScript'te gizli sözleşmelerdir.
Bu prensibi JavaScript'te gösteren iyi bir örnek, büyük ayar nesneleri gerektiren sınıflardır. İstemciden büyük miktarda seçenek ayarlamalarını istememek faydalıdır, çünkü çoğu zaman tüm ayarlara ihtiyaç duymazlar. Bunları isteğe bağlı yapmak, "şişman interface" sahip olmamaya yardımcı olur.
Yanlış:
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() {} // Çoğu zaman gezinirken animate kullanma ihtiyacımız olmaz.
// ...
});
Doğru:
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() {}
}
});
Bu prensip ik temel şeyi belirtir:
- Üst seviye modüller alt seviye modüllere bağımlı olmamalı. İkisi birden ortak soyutlamalara bağımlı olmalı.
- Soyutlamalar ayrıntılara bağlı olmamalıdır. Ayrıntıkar soyutlamaya bağlı olmalıdır.
İlk başta bunu anlamak zor olabilir, ancak AngularJS ile çalıştıysanız, bu ilkenin Bağımlılık Enjeksiyonu (DI) şeklinde uygulandığını gördünüz. Aynı kavramlar olmasalar da, DIP, yüksek seviye modüllerinin düşük seviye modüllerinin ayrıntılarını bilmesini ve kurmasını önler. Bunu DI ile başarabilir. Bunun en büyük yararı modüller arasındaki eşleşmeyi azaltmasıdır. İkileme çok kötü bir gelişme şeklidir çünkü kodunuzu elden geçirilmesini zorlaştırır.
Daha önce de belirtildiği gibi, JavaScript'in ara yüzleri bulunmadığından bağımlı olan soyutlamalar gizli sözleşmelerdir. Başka bir deyişle, bir nesnenin / sınıfın başka bir nesneye / sınıfa açtığı yöntem ve özellikler. Aşağıdaki örnekte, gizli sözleşme, bir InventoryTracker
için herhangi bir Request modülünün bir requestItems
yöntemine sahip olmasıdır.
Yanlış:
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();
Doğru:
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();
Test, sonucu oluşturmaktan daha önemlidir. Eğer hiç testiniz yoksa veya yetersiz miktarda ise, sonra kodu her gönderdiğinizde hiçbir şeyi bozmadığınızdan emin olmayacaksınız. Neyin uygun bir miktar oluşturduğuna karar vermek ekibinize bağlıdır, ancak %100 kapsama sahip olmak (tüm özellik ve branşlar) çok yüksek bir güven ve geliştirici gönül rahatlığı elde etmenizdir. Bu, harika bir test çerçevesine ek olarak, aynı zamanda biriyi kapsam aracı sahip olmanız demektir.
Test yazmamak için mazeret yok. Bol miktarda JS test çatısı var, bu yüzden ekibinizin tercih ettiği bir tane bulun. Takımınız için uygun olanı bulduğunuzda, tanıttığınız her yeni özellik / modül için her zaman testler yazmayı hedefleyin. Tercih ettiğiniz yöntem Test Tahrikli Geliştirme (TDD) ise, bu harika, ancak asıl mesele, herhangi bir özelliği başlatmadan veya mevcut olanı yeniden düzenlemeden önce kapsama hedeflerinize ulaştığınızdan emin olmaktır.
Yanlış:
import assert from "assert";
describe("MomentJS", () => {
it("handles date boundaries", () => {
let date;
date = new MomentJS("1/1/2015");
date.addDays(30);
assert.equal("1/31/2015", date);
date = new MomentJS("2/1/2016");
date.addDays(28);
assert.equal("02/29/2016", date);
date = new MomentJS("2/1/2015");
date.addDays(28);
assert.equal("03/01/2015", date);
});
});
Doğru:
import assert from "assert";
describe("MomentJS", () => {
it("handles 30-day months", () => {
const date = new MomentJS("1/1/2015");
date.addDays(30);
assert.equal("1/31/2015", date);
});
it("handles leap year", () => {
const date = new MomentJS("2/1/2016");
date.addDays(28);
assert.equal("02/29/2016", date);
});
it("handles non-leap year", () => {
const date = new MomentJS("2/1/2015");
date.addDays(28);
assert.equal("03/01/2015", date);
});
});
Callback'ler temiz değil ve aşırı miktarda dallanmaya neden oluyorlar. ES2015/ES6 ile, Promise artık yerleşik bir küresel tiptir. Mutlaka kullanın!
Yanlış:
import { get } from "request";
import { writeFile } from "fs";
get(
"https://en.wikipedia.org/wiki/Robert_Cecil_Martin",
(requestErr, response) => {
if (requestErr) {
console.error(requestErr);
} else {
writeFile("article.html", response.body, writeErr => {
if (writeErr) {
console.error(writeErr);
} else {
console.log("File written");
}
});
}
}
);
Doğru:
import { get } from "request";
import { writeFile } from "fs";
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
.then(response => {
return writeFile("article.html", response);
})
.then(() => {
console.log("File written");
})
.catch(err => {
console.error(err);
});
Promise'ler geri Callback'lere göre çok temiz bir alternatif, ancak ES2017/ES8 daha temiz bir çözüm olan async ve wait yönteminide sağlıyor. İhtiyacınız olan tek şey bir async
anahtar kelimesinde önceden belirlenmiş bir fonksiyondur ve ardından yapmak istediğiniz then
fonksiyonlar zinciri olmadan zorunlu olarak yazabilirsiniz. Bugün ES2017/ES8 özelliklerinden yararlanabiliyorsanız bunu kullanın!
Yanlış:
import { get } from "request-promise";
import { writeFile } from "fs-promise";
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
.then(response => {
return writeFile("article.html", response);
})
.then(() => {
console.log("File written");
})
.catch(err => {
console.error(err);
});
Doğru:
import { get } from "request-promise";
import { writeFile } from "fs-promise";
async function getCleanCodeArticle() {
try {
const response = await get(
"https://en.wikipedia.org/wiki/Robert_Cecil_Martin"
);
await writeFile("article.html", response);
console.log("File written");
} catch (err) {
console.error(err);
}
}
Fırlatılmış hatalar iyi bir şeydir! Programınızdaki bir şey ters gittiğinde çalışma zamanının başarıyla tanımlandığı ve mevcut yığında işlev yürütmeyi durdurarak, işlemi (o anki düğümde) öldürerek ve konsolda bir yığın izlemesi ile size bildirerek sizi bilgilendirmesini sağlar.
Yakalanan bir hata ile hiçbir şey yapmamak, size söylenen hatayı gidermek veya hiç olumsuz cevap verebilmeniz için yardımcı olmaz. Hatayı sadece konsola kaydetmek (console.log
), konsola yazdırılan hatalardan oluşan bir denizde kaybolabildiği için çoğunlukla faydalı değildir. Herhangi bir kod parçasını bir try/catch
içine alırsanız, orada bir hatanın olabileceğini düşünüyorsunuz ve bu nedenle gerçekleştiği zaman için bir planınız olmalı veya bir kod oluşturmalısınız.
Yanlış:
try {
functionThatMightThrow();
} catch (error) {
console.log(error);
}
Doğru:
try {
functionThatMightThrow();
} catch (error) {
// One option (more noisy than console.log):
console.error(error);
// Another option:
notifyUserOfError(error);
// Another option:
reportErrorToService(error);
// OR do all three!
}
try/catch
bloğu içindeki hataları gözardı etmemenizin sebebbi olan gerekçeler bunun için de geçerlidir.
Yanlış:
getdata()
.then(data => {
functionThatMightThrow(data);
})
.catch(error => {
console.log(error);
});
Doğru:
getdata()
.then(data => {
functionThatMightThrow(data);
})
.catch(error => {
// One option (more noisy than console.log):
console.error(error);
// Another option:
notifyUserOfError(error);
// Another option:
reportErrorToService(error);
// OR do all three!
});
Kod yazım şekli kişisel bir konudur. Buradaki diğer kurallar gibi, takip edebileceğiniz etkili ve hızlı bir kural yoktur. dikkat etmeniz gereken şey bu konu hakkında TARTIŞMALARA YOL AÇMAMAKTIR. Bunu otomatikleştirebileceğiniz bir sürü araç var. Birini kullanın! Kodlama şekli üzerine insanların tartışması para ve kaynak israfıdır.
Otomatik formatlama (girintiler, sekmeler ve boşluklar, çift ve tek tırnaklar, vb.) kapsamına girmeyen şeyler için aşağıdaki bazı yönergelere bakın.
JavaScript tip zorunlu bir dil değildir, bu yüzden büyük harf size değişkenleriniz, fonksiyonlarını vb. hakkında çok şey söyler. Bu kurallar tartışmaya açıktır, ekibiniz ne isterse seçebilir. Mesele şu ki, neyi seçerseniz seçin, mutlaka tutarlı olun.
Yanlış:
const DAYS_IN_WEEK = 7;
const daysInMonth = 30;
const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
const Artists = ["ACDC", "Led Zeppelin", "The Beatles"];
function eraseDatabase() {}
function restore_database() {}
class animal {}
class Alpaca {}
Doğru:
const DAYS_IN_WEEK = 7;
const DAYS_IN_MONTH = 30;
const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"];
function eraseDatabase() {}
function restoreDatabase() {}
class Animal {}
class Alpaca {}
Bir fonksiyon diğerini çağırıyorsa, bu fonksiyonları kaynak dosyada dikey olarak birbirine yakın tanımlayın. İdeal olarak, arayanı aranan fonksiyonun hemen üstünde tutun. Bir gazete gibi, yukarıdan aşağıya kod okuma eğilimindeyiz. Bu nedenle, kodunuzu bu şekilde okunmasını sağlayın.
Yanlış:
class PerformanceReview {
constructor(employee) {
this.employee = employee;
}
lookupPeers() {
return db.lookup(this.employee, "peers");
}
lookupManager() {
return db.lookup(this.employee, "manager");
}
getPeerReviews() {
const peers = this.lookupPeers();
// ...
}
perfReview() {
this.getPeerReviews();
this.getManagerReview();
this.getSelfReview();
}
getManagerReview() {
const manager = this.lookupManager();
}
getSelfReview() {
// ...
}
}
const review = new PerformanceReview(employee);
review.perfReview();
Doğru:
class PerformanceReview {
constructor(employee) {
this.employee = employee;
}
perfReview() {
this.getPeerReviews();
this.getManagerReview();
this.getSelfReview();
}
getPeerReviews() {
const peers = this.lookupPeers();
// ...
}
lookupPeers() {
return db.lookup(this.employee, "peers");
}
getManagerReview() {
const manager = this.lookupManager();
}
lookupManager() {
return db.lookup(this.employee, "manager");
}
getSelfReview() {
// ...
}
}
const review = new PerformanceReview(employee);
review.perfReview();
Yorumlar bir özür değil, bir gerekliliktir. İyi kod çoğunlukla kendini belgeler.
Yanlış:
function hashIt(data) {
// The hash
let hash = 0;
// Length of string
const length = data.length;
// Loop through every character in data
for (let i = 0; i < length; i++) {
// Get character code.
const char = data.charCodeAt(i);
// Make the hash
hash = (hash << 5) - hash + char;
// Convert to 32-bit integer
hash &= hash;
}
}
Doğru:
function hashIt(data) {
let hash = 0;
const length = data.length;
for (let i = 0; i < length; i++) {
const char = data.charCodeAt(i);
hash = (hash << 5) - hash + char;
// Convert to 32-bit integer
hash &= hash;
}
}
Sürüm kontrolü sistemlerinin bir amacı var. Eski kodu saklama işini sürüm geçmişinde bırak.
Yanlış:
doStuff();
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();
Doğru:
doStuff();
Unutma, sürüm kontrol sistemini kullan! Ölü kod, yorum içine alınmış kod ve özellikle hikaye şeklinde yorumlara gerek yoktur. Geçmişi bulmak için git log
kullanın!
Yanlış:
/**
* 2016-12-20: Removed monads, didn't understand them (RM)
* 2016-10-01: Improved using special monads (JP)
* 2016-02-03: Removed type-checking (LI)
* 2015-03-14: Added combine with type-checking (JR)
*/
function combine(a, b) {
return a + b;
}
Doğru:
function combine(a, b) {
return a + b;
}
Genellikle sadece görüntü kirliliği eklerler. Fonksiyonların ve değişken adlarının uygun girinti ve biçimlendirmeyle birlikte görsel yapıyı kodunuza vermesini sağlayın.
Yanlış:
////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
menu: "foo",
nav: "bar"
};
////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
const actions = function() {
// ...
};
Doğru:
$scope.model = {
menu: "foo",
nav: "bar"
};
const actions = function() {
// ...
};
Bu belge aşağıdaki dillere çevirilmiştir:
- Fransızca: GavBaros/clean-code-javascript-fr
- Brezilya Portekizcesi: fesnt/clean-code-javascript
- İspanyolca: andersontr15/clean-code-javascript
- İspanyolca: tureey/clean-code-javascript
- Çince:
- Almanca: marcbruederlin/clean-code-javascript
- Korece: qkraudghgh/clean-code-javascript-ko
- Polonyaca: greg-dev/clean-code-javascript-pl
- Rusça:
- Vietnamce: hienvd/clean-code-javascript/
- Japonca: mitsuruog/clean-code-javascript/
- Endonezya dili: andirkh/clean-code-javascript/
- İtalyanca: frappacchio/clean-code-javascript/