Repositori original : ryanmcdermott/clean-code-javascript
- Kata Pengantar
- Variabel
- Fungsi
- Objek dan Struktur Data
- Class
- SOLID
- Pengujian
- Concurrency
- Penanganan Kesalahan
- Menyusun Format
- Komentar
- Alih Bahasa
Prinsip Rekayasa Perangkat Lunak, oleh Robert C. Buku Clean Code oleh Martin, diadaptasi untuk JavaScript. Perlu diingat bahwa panduan ini bukan tentang gaya penulisan kode. Panduan ini lebih tentang membuat perangkat lunak yg mudah dibaca, dapat digunakan kembali, dan mudah direfaktor kembali dalam bahasa Javascript.
Tidak semua prinsip disini harus diikuti secara ketat, dan bahkan lebih sedikit akan lebih mudah disepakati bersama. Ini hanyalah panduan-panduan saja dan tidak lebih, tetapi panduan-panduan ini dikodifikiasi selama bertahun-tahun dari pengalaman kolektif penulis buku Clean Code.
Rekayasa-rekayasa perangkat lunak kita baru berumur 50 tahun lebih sedikit. dan kita masih belajar banyak. Ketika arsitektur perangkat lunak setua arsitektur itu sendiri, mungkin kita memiliki aturan yang lebih ketat untuk diikuti. Untuk saat ini, biarkan panduan ini berfungsi sebagai batu ujian (touchstone) untuk mengukur kualitas kode JavaScript yang kamu dan tim kamu buat.
Satu hal lagi: mengetahui hal ini bukan berarti membuat kamu menjadi seorang pengembang perangkat lunak (developer) yg baik secara tiba-tiba, dan bekerja dengannya bukan berarti kamu tidak akan membuat kesalahan-kesalahan. Setiap potongan kode dimulai sebagai draft pertama, seperti tanah liat basah yg terus dibentuk menjadi bentuk terakhirnya. Akhirnya, kita memahat ketidaksempurnaan ketika kita me-*-nya kembali dengan teman dalam tim kita. Apabila draft pertamamu butuh perbaikan, jangan pukul dirimu sendiri. Pukul Kodenya !
Buruk:
constyyyymmdstr=moment().format('YYYY/MM/DD');Baik:
constcurrentDate=moment().format('YYYY/MM/DD');Buruk:
getUserInfo();getClientData();getCustomerRecord();Baik:
getUser();Kita akan lebih banyak membaca kode daripada menulisnya. Maka penting hukumnya menulis kode untuk mudah dibaca dan mudah dicari. Dengan tidak memberi nama variabel yang bermakna untuk memahami program kita, kita menyakiti pembaca kode kita. Buat nama variabel lebih mudah dicari. Alat seperti
buddy.js dan ESLint dapat membantu mengidentifikasi konstanta yg tidak diberi nama.
Buruk:
// Nah kan, 86400000 itu untuk apa?setTimeout(blastOff,86400000);Baik:
// tulis `const` dalam huruf kapital dan secara globalconstMILLISECONDS_IN_A_DAY=86400000;setTimeout(blastOff,MILLISECONDS_IN_A_DAY);Buruk:
constaddress='One Infinite Loop, Cupertino 95014';constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;saveCityZipCode(address.match(cityZipCodeRegex)[1],address.match(cityZipCodeRegex)[2]);Baik:
constaddress='One Infinite Loop, Cupertino 95014';constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;const[,city,zipCode]=address.match(cityZipCodeRegex)||[];saveCityZipCode(city,zipCode);Explisit lebih baik daripada implisit.
Buruk:
constlocations=['Austin','New York','San Francisco'];locations.forEach((l)=>{doStuff();doSomeOtherStuff();// ...// ...// ...// Sebentar, `l` itu untuk apa lagi?dispatch(l);});Baik:
constlocations=['Austin','New York','San Francisco'];locations.forEach((location)=>{doStuff();doSomeOtherStuff();// ...// ...// ...dispatch(location);});Jika nama class/objek kamu memiliki arti tertentu, jangan mengulanginya di dalam nama variabel.
Buruk:
constCar={carMake: 'Honda',carModel: 'Accord',carColor: 'Blue'};functionpaintCar(car){car.carColor='Red';}Baik:
constCar={make: 'Honda',model: 'Accord',color: 'Blue'};functionpaintCar(car){car.color='Red';}Argumen default lebih bersih daripada shoot circuiting. Sadarilah jika kamu
menggunakannya, fungsimu hanya akan memberikan nilai default untuk argumen undefined.
Selain nilai "falsy" seperti '', "", false, null, 0, dan NaN, tidak
digantikan oleh nilai default.
Buruk:
functioncreateMicrobrewery(name){constbreweryName=name||'Hipster Brew Co.';// ...}Baik:
functioncreateMicrobrewery(breweryName='Hipster Brew Co.'){// ...}Membatasi jumlah parameter dari fungsi sangatlah penting karena membuat pengujian fungsi menjadi lebih mudah. Memiliki lebih dari 3 parameter dapat mengakibatkan ledakan kombinasional dimana kamu harus menguji banyak sekali kasus-kasus yg berbeda di tiap-tiap argumen secara terpisah.
Satu atau dua argumen adalah kasus yg ideal, jika tiga maka sebaiknya dihindari. Apapun lebih dari itu harus dikonsolidasikan. Biasanya, jika kamu memiliki lebih dari dua argumen maka fungsimu melakukan hal yg terlalu banyak. Di beberapa kasus dimana hal itu tidak terjadi, seringkali objek tingkat-tinggi sudah cukup untuk dijadikan sebuah argumen.
JavaScript memperbolehkanmu untuk membuat objek secara langsung, tanpa membuat banyak class terlebih dahulu. kamu dapat menggunakan sebuah objek jika kamu mendapati bahwa kamu memerlukan banyak argumen.
Lebih jelasnya, kamu dapat menggunakan destructuring syntax pada ES2015/ES6. Hal tersebut memiliki beberapa kelebihan:
- Ketika seseorang melihat fungsi, fungsi tersebut secara langsung menjelaskan properti-properti apa saja yg digunakan.
- Merestrukturisasi juga mengkloning nilai primitif yang ditentukan dari objek argumen yang dilewatkan ke fungsi. Hal ini dapat membantu mencegah efek samping. Sebagai Catatan: objek dan array yg telah didestrukturisasi dari argumen objek tidak di kloning.
- Linters dapat memberi peringatan kepadamu tentang properti yg tidak digunakan, yg mana akan lebih tidak mungkin tanpa merestrukturisasi.
Buruk:
functioncreateMenu(title,body,buttonText,cancellable){// ...}Baik:
functioncreateMenu({ title, body, buttonText, cancellable }){// ...}createMenu({title: 'Foo',body: 'Bar',buttonText: 'Baz',cancellable: true});Ini adalah aturan terpenting dalam rekayasa perangkat lunak. Ketika fungsi melakukan lebih dari satu hal, mereka sulit untuk dibuat, diuji dan dipikirkan. Ketika kamu dapat mengisolasi sebuah fungsi untuk melakukan satu action saja, mereka dapat difaktorkan secara mudah dan kodemu akan jauh lebih bersih. Jika kamu tidak mengambil apapun dari panduan ini selain hal ini, maka kamu akan ada di depan banyak pengembang.
Buruk:
functionemailClients(clients){clients.forEach((client)=>{constclientRecord=database.lookup(client);if(clientRecord.isActive()){email(client);}});}Baik:
functionemailActiveClients(clients){clients.filter(isActiveClient).forEach(email);}functionisActiveClient(client){constclientRecord=database.lookup(client);returnclientRecord.isActive();}Buruk:
functionaddToDate(date,month){// ...}constdate=newDate();// sulit untuk mengatakan dari nama fungsi yg ditambahkanaddToDate(date,1);Baik:
functionaddMonthToDate(month,date){// ...}constdate=newDate();addMonthToDate(1,date);Ketika kamu memiliki lebih dari satu tingkat abstraksi, fungsimu biasanya melakukan hal yg terlalu banyak. Memisah-misah fungsi dapat meningkatkan reusabilitas dan memudahkan pengujian.
Buruk:
functionparseBetterJSAlternative(code){constREGEXES=[// ...];conststatements=code.split(' ');consttokens=[];REGEXES.forEach((REGEX)=>{statements.forEach((statement)=>{// ...});});constast=[];tokens.forEach((token)=>{// lex...});ast.forEach((node)=>{// parse...});}Baik:
functiontokenize(code){constREGEXES=[// ...];conststatements=code.split(' ');consttokens=[];REGEXES.forEach((REGEX)=>{statements.forEach((statement)=>{tokens.push(/* ... */);});});returntokens;}functionlexer(tokens){constast=[];tokens.forEach((token)=>{ast.push(/* ... */);});returnast;}functionparseBetterJSAlternative(code){consttokens=tokenize(code);constast=lexer(tokens);ast.forEach((node)=>{// parse...});}Lakukan yg terbaik untuk menghindari kode yg duplikat. Kode yg duplikat itu Buruk karena berarti terdapat lebih dari satu tempat untuk diubah jika kamu perlu mengubah beberapa logic.
Bayangkan jika kamu memiliki sebuah restoran dan kamu harus terus melacak inventarismu: tomat, bawang, cabe, dan lain-lain. Nah, Jika kamu memiliki beberapa daftar yg kamu simpan, maka kamu harus mengubah itu semua ketika kamu menyajikan hidangan yg ada tomatnya. Jika kamu hanya memiliki satu daftar saja, maka kamu hanya perlu mengubahnya di satu tempat saja.
Seringkali kamu memiliki kode yg duplikat karena kamu memiliki dua atau lebih hal-hal yg sedikit berbeda, memiliki banyak hal yg sama, namun perbedaan itu memaksamu untuk memisahkan dua atau lebih fungsi yg berbeda namun kebanyakan melakukan hal yg sama. Membuang kode yg duplikat berarti membuat sebuah abstraksi yg dapat menangani hal yg berbeda dengan satu fungsi/modul/class.
Mendapatkan abstraksi yg benar sangat penting, itulah kenapa kamu harus mengikuti prinsip SOLID yg ada setelah bagian Class. Abstraksi yg buruk dapat lebih buruk daripada kode yg duplikat, jadi berhati-hatilah! Setelah diberitahu hal ini, kamu dapat membuat abstraksi yg baik, lakukan! Jangan membuat dirimu mengulang (DRY), Jika tidak, kamu akan mendapati dirimu memperbarui hal dibeberapa tempat setiap kali kamu ingin mengubah satu hal.
Buruk:
functionshowDeveloperList(developers){developers.forEach((developer)=>{constexpectedSalary=developer.calculateExpectedSalary();constexperience=developer.getExperience();constgithubLink=developer.getGithubLink();constdata={
expectedSalary,
experience,
githubLink
};render(data);});}functionshowManagerList(managers){managers.forEach((manager)=>{constexpectedSalary=manager.calculateExpectedSalary();constexperience=manager.getExperience();constportfolio=manager.getMBAProjects();constdata={
expectedSalary,
experience,
portfolio
};render(data);});}Baik:
functionshowEmployeeList(employees){employees.forEach((employee)=>{constexpectedSalary=employee.calculateExpectedSalary();constexperience=employee.getExperience();letportfolio;switch(employee.type){case'manager':
portfolio=employee.getMBAProjects();break;case'developer':
portfolio=employee.getGithubLink();break;}constdata={
expectedSalary,
experience,
portfolio
};render(data);});}Buruk:
constmenuConfig={title: null,body: 'Bar',buttonText: null,cancellable: true};functioncreateMenu(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);Baik:
constmenuConfig={title: 'Order',// Pengguna tidak memasukkan key 'body'buttonText: 'Send',cancellable: true};functioncreateMenu(config){config=Object.assign({title: 'Foo',body: 'Bar',buttonText: 'Baz',cancellable: true},config);// config sekarang sama dengan: {title: "Order", body: "Bar",// buttonText: "Send", cancellable: true}// ...}createMenu(menuConfig);Flag memberitahu penggunamu bahwa fungsi ini melakukan lebih dari satu hal. Fungsi seharusnya melakukan satu hal. Pisahkan fungsimu jika mereka mengikuti jalur kode yg berbeda berdasarkan boolean.
Buruk:
functioncreateFile(name,temp){if(temp){fs.create(`./temp/${name}`);}else{fs.create(name);}}Baik:
functioncreateFile(name){fs.create(name);}functioncreateTempFile(name){createFile(`./temp/${name}`);}Sebuah fungsi memproduksi efek samping ketika dia melakukan apapun selain memasukkan nilai dan mengembalikan nilai. Sebuah efek samping bisa berupa menulis ke sebuah file, memodifikasi sebuah variabel global, atau secara tidak sengaja mentransfer semua uangmu ke orang yg tidak kamu kenal.
Sekarang, kamu memang perlu memiliki efek samping dalam sebuah program pada suatu kesempatan. Seperti pada contoh sebelumnya, kamu bisa jadi perlu untuk menulis sebuah file. Apa yg akan kamu lakukan adalah memusatkan dimana kamu melakukan ini. Tidak memiliki beberapa fungsi dan class yang menulis ke file tertentu. Miliki satu service yg melakukan hal itu. Satu dan satu-satunya.
Poin utama untuk menghindari jebakan yg umum seperti berbagi state antara objek tanpa struktur apapun, menggunakan tipe data yg mutable yg dapat ditulis ke apapun, dan tidak memusatkan dimana efek samping tersebut muncul. Jika kamu dapat melakukan hal ini, kamu akan lebih bahagia daripada programmer-programmer kebanyakan.
Buruk:
// Variabel global direferensi oleh beberapa fungsi// Jika kita mempunyai fungsi lain yg menggunakan nama variabel ini, sekarang akan menjadi array dan dapat merusak ini semua.letname='Ryan McDermott';functionsplitIntoFirstAndLastName(){name=name.split(' ');}splitIntoFirstAndLastName();console.log(name);// ['Ryan', 'McDermott'];Baik:
functionsplitIntoFirstAndLastName(name){returnname.split(' ');}constname='Ryan McDermott';constnewName=splitIntoFirstAndLastName(name);console.log(name);// 'Ryan McDermott';console.log(newName);// ['Ryan', 'McDermott'];Dalam JavaScript, primitif dilewatkan oleh nilai dan objek / array dilewatkan
melalui referensi. Dalam kasus dimana objek dan array, jika fungsimu membuat
perubahan di array keranjang belanja, sebagai contoh, dengan menambahkan
sebuah barang untuk pembelian, maka fungsi lain yg menggunakan arraykeranjang
akan terpengaruh dengan penambahan ini. Hal itu mungkin bagus, namun bisa
jadi hal yg buruk jg. Mari kita mengimajinasikan situasi berikut:
Pengguna menekan "Beli", tombol yg memanggil fungsi beli yg memunculkan
permintaan jaringan dan mengirim arraykeranjang ke server. Karena
koneksi jaringan yg buruk, fungsi beli harus terus mencoba request kembali.
Sekarang, bagaimana jika di waktu yg sama si pengguna secara tidak sengaja
menekan tombol "Tambahkan ke Keranjang" pada barang yg sedang tidak mereka
inginkan sebelum request jaringan dimulai? Jika hal itu terjadi dan request
jaringan dimulai, maka fungsi beli tersebut akan mengirimkan tambahan barang
secara tidak sengaja karena hal itu mereferensi ke array keranjang belanja
yg mana fungsi addItemToCart dimodifikasi dengan menambahkan barang yg
tidak diinginkan.
Solusi yg bagus bisa jadi addItemToCart harus selalu mengkloning cart,
mengeditnya, dan mengembalikan klonnya. Ini memastikan bahwa tidak ada fungsi
lain yang memegang referensi keranjang belanja akan terpengaruh oleh perubahan
apapun.
Dua keberatan yg disebutkan pada pendekatan ini:
- Terdapat beberapa kasus dimana kamu benar-benar ingin memodifikasi objek masukan, namun ketika kamu menggunakan praktek pemrograman ini kamu akan menemukan bahwa kasus tersebut sangatlah langka. Kebanyakan apapun yg refaktorkan tidak memiliki efek samping.
- Kloning objek-objek yg besar dapat sangat mahal dalam hal performa. Beruntungnya, hal ini bukan isu yg besar dalam prakteknya karena terdapat library seperti great libraries yg memperbolehkan pendekatan pemrograman seperti ini lebih cepat dan tidak terlalu banyak memakan memory seperti mengkloning objek-objek dan array secara manual.
Buruk:
constaddItemToCart=(cart,item)=>{cart.push({ item,date: Date.now()});};Baik:
constaddItemToCart=(cart,item)=>{return[...cart,{ item,date : Date.now()}];};Mencemari hal yg bersifat global adalah praktek yg buruk di JavaScript karena
kamu dapat berbenturan dengan library yg lain dan pengguna dari API-mu tidak
akan menjadi yg bijak sampai mereka mendapatkan exception di production.
Mari berpikir tentang sebuah contoh: Bagaimana jika kamu ingin memperpanjang
metode Array asli JavaScript untuk memiliki metode diff yang dapat menunjukkan
perbedaan antara dua array? Kamu dapat menulis fungsi barumu ke Array.prototype,
tapi hal tersebut dapat berbenturan dengan library lain yang mencoba melakukan
hal yang sama. Bagaimana jika library lainnya sudah menggunakan diff untuk
mencari perbedaan antara elemen pertama dan terakhir dalam sebuah array? Inilah
kenapa lebih baiknya menggunakan class ES2015/ES6 dan secara sederhana
memperpanjang global Array.
Buruk:
Array.prototype.diff=functiondiff(comparisonArray){consthash=newSet(comparisonArray);returnthis.filter(elem=>!hash.has(elem));};Baik:
classSuperArrayextendsArray{diff(comparisonArray){consthash=newSet(comparisonArray);returnthis.filter(elem=>!hash.has(elem));}}JavaScript bukanlah sebuah bahasa fungsional seperti Haskell, namun dia memiliki aroma fungsional juga. Bahasa yg fungsional lebih sederhana dan lebih mudah diuji. Gunakan gaya pemrograman seperti ini sebisa mungkin.
Buruk:
constprogrammerOutput=[{name: 'Uncle Bobby',linesOfCode: 500},{name: 'Suzie Q',linesOfCode: 1500},{name: 'Jimmy Gosling',linesOfCode: 150},{name: 'Gracie Hopper',linesOfCode: 1000}];lettotalOutput=0;for(leti=0;i<programmerOutput.length;i++){totalOutput+=programmerOutput[i].linesOfCode;}Baik:
constprogrammerOutput=[{name: 'Uncle Bobby',linesOfCode: 500},{name: 'Suzie Q',linesOfCode: 1500},{name: 'Jimmy Gosling',linesOfCode: 150},{name: 'Gracie Hopper',linesOfCode: 1000}];constINITIAL_VALUE=0;consttotalOutput=programmerOutput.map((programmer)=>programmer.linesOfCode).reduce((acc,linesOfCode)=>acc+linesOfCode,INITIAL_VALUE);Buruk:
if(fsm.state==='fetching'&&isEmpty(listNode)){// ...}Baik:
functionshouldShowSpinner(fsm,listNode){returnfsm.state==='fetching'&&isEmpty(listNode);}if(shouldShowSpinner(fsmInstance,listNodeInstance)){// ...}Buruk:
functionisDOMNodeNotPresent(node){// ...}if(!isDOMNodeNotPresent(node)){// ...}Baik:
functionisDOMNodePresent(node){// ...}if(isDOMNodePresent(node)){// ...}Ini terlihat seperti pekerjaan yg tidak mungkin. Sejak pertama kali mendengar
hal ini, kebanyakan orang berkata "Bagaimana saya bisa bekerja tanpa sebuah
statemen if ?" Jawabannya aalah kamu bisa menggunakan polimorfisme untuk
mengerjakan hal-hal di berbagai kasus. Pertanyaan kedua biasanya, "Oke itu bagus
tapi kenapa aku harus melakukannya?" Jawabannya adalah kode yg bersih seperti
apa yg sudah kita pelajari sebelumnya: sebuah fungsi harus melakukan satu hal
saja. Ketika kamu memiliki class dan fungsi yang memiliki statemen if, kamu
memberitahu pengguna bahwa fungsimu melakukan hal yg lebih dari satu. Ingat, hanya
lakukan satu hal saja.
Buruk:
classAirplane{// ...getCruisingAltitude(){switch(this.type){case'777':
returnthis.getMaxAltitude()-this.getPassengerCount();case'Air Force One':
returnthis.getMaxAltitude();case'Cessna':
returnthis.getMaxAltitude()-this.getFuelExpenditure();}}}Baik:
classAirplane{// ...}classBoeing777extendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude()-this.getPassengerCount();}}classAirForceOneextendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude();}}classCessnaextendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude()-this.getFuelExpenditure();}}JavaScript tidak memiliki tipe data, yang mana fungsimu dapat mengambil tipe data apapun dari sebuah argumen. Terkadang kamu tergigit karena kebebasan ini dan menjadi tergoda untuk melakukan type-checking pada fungsimu. Terdapat banyak jalan untuk terhindar dari hal ini. Hal pertama yg harus dipertimbangkan adalah memiliki API yg konsisten.
Buruk:
functiontravelToTexas(vehicle){if(vehicleinstanceofBicycle){vehicle.pedal(this.currentLocation,newLocation('texas'));}elseif(vehicleinstanceofCar){vehicle.drive(this.currentLocation,newLocation('texas'));}}Baik:
functiontravelToTexas(vehicle){vehicle.move(this.currentLocation,newLocation('texas'));}Jika kamu bekerja dengan nilai primitif sederhana seperti string, integer dan array, dan kamu tidak bisa menggunakan polimorfisme tapi kamu tetap merasa perlu untuk melakukan type-check, kamu harus mempertimbangkan untuk menggunakan TypeScript. Itu merupakan alternatif yg sangat baik untuk JavaScript biasa, yg mana menyediakanmu static typing diatas standar sintaks JavaScript. Masalah dengan type-checking secara manual di Javascript adalah hal itu memerlukan banyak kata tambahan palsu yang merusak readibilitas. Pastikan JavaScript-mu bersih, tulis pengujian yang baik, dan memiliki kode review yang bagus. Jika tidak, lakukan semuanya dengan TypeScript (yang mana, seperti apa yang aku katakan, itu adalah sebuah alternatif yang sangat bagus)
Buruk:
functioncombine(val1,val2){if(typeofval1==='number'&&typeofval2==='number'||typeofval1==='string'&&typeofval2==='string'){returnval1+val2;}thrownewError('Must be of type String or Number');}Baik:
functioncombine(val1,val2){returnval1+val2;}Browser-browser yang modern melakukan banyak optimisasi dibalik layar di runtime. Seringkali, kamu mengoptimasi dan kamu hanya menghabiskan waktumu. Terdapat Sumber yg bagus untuk dilihat dimana optimasi itu kurang. Targetkan mereka sementara, sampai mereka diperbaiki jika mereka bisa melakukannya.
Buruk:
// Di Browser lama, setiap iterasi dengan `list.length` yg uncached akan sangat costly// karena `list.length` menghitung ulang. Di browser yang modern, ini sudah teroptimasifor(leti=0,len=list.length;i<len;i++){// ...}Baik:
for(leti=0;i<list.length;i++){// ...}Kode yg mati akan sama buruknya dengan kode yang duplikat. Tidak ada alasan untuk menyimpannya dalam basis kode-mu. Bisa dibuang jika tidak digunakan, kode akan tetap tersimpan di "version history" jika kamu membutuhkannya lagi.
Buruk:
functionoldRequestModule(url){// ...}functionnewRequestModule(url){// ...}constreq=newRequestModule;inventoryTracker('apples',req,'www.inventory-awesome.io');Baik:
functionnewRequestModule(url){// ...}constreq=newRequestModule;inventoryTracker('apples',req,'www.inventory-awesome.io');Menggunakan getters dan setter untuk mengakses data di objek akan lebih baik daripada sekedar mencari sebuah properti dari objek, "Kenapa?" kamu mungkin bertanya. Baik, terdapat daftar yg tidak teratur sebagai alasan:
- Ketika kamu ingin melakukan hal lebih dari sekedar mendapatkan properti objek, kamu tidak harus mencari dan mengubah setiap aksesor di basis kode-mu.
- Buat menambahkan validasi sederhana ketika melakukan sebuah
set. - Enkapsulasi representasi internal.
- Memudahkan untuk tambah catatan dan penanggulangan kesalahan ketika getting dan setting.
- Kamu bisa lazy load properti-properti dari objek, seperti mendapatkannya dari server.
Buruk:
functionmakeBankAccount(){// ...return{balance: 0,// ...};}constaccount=makeBankAccount();account.balance=100;Baik:
functionmakeBankAccount(){// yg satu ini privatletbalance=0;// sebuah "getter", dibuat publik via objek yg dikembalikan di bawah inifunctiongetBalance(){returnbalance;}// sebuah "setter", dibuat publik via objek yg dikembalikan di bawah inifunctionsetBalance(amount){// ... validasi sebelum memperbarui balancebalance=amount;}return{// ...
getBalance,
setBalance,};}constaccount=makeBankAccount();account.setBalance(100);Hal ini dapat diselesaikan lewat closures (untuk ES5 serta diatasnya)
Buruk:
constEmployee=function(name){this.name=name;};Employee.prototype.getName=functiongetName(){returnthis.name;};constemployee=newEmployee('John Doe');console.log(`Employee name: ${employee.getName()}`);// Nama employee: John Doedeleteemployee.name;console.log(`Employee name: ${employee.getName()}`);// Nama employee: undefinedBaik:
functionmakeEmployee(name){return{getName(){returnname;},};}constemployee=makeEmployee('John Doe');console.log(`Employee name: ${employee.getName()}`);// Nama employee: John Doedeleteemployee.name;console.log(`Employee name: ${employee.getName()}`);// Nama employee: John DoeSangat susah untuk mendapatkan class inheritance, construction, dan definisi method yg mudah dibaca untuk class ES5 yg terdahulu. Jika kamu memerlukan inheritance (dan waspadai jika kamu tidak), maka pilihlah class ES2015/ES6. Akan tetapi, gunakan fungsi yg kecil daripada class sampai kamu mendapati dirimu memerlukan objek yang lebih besar dan kompleks.
Buruk:
constAnimal=function(age){if(!(thisinstanceofAnimal)){thrownewError('Instantiate Animal with `new`');}this.age=age;};Animal.prototype.move=functionmove(){};constMammal=function(age,furColor){if(!(thisinstanceofMammal)){thrownewError('Instantiate Mammal with `new`');}Animal.call(this,age);this.furColor=furColor;};Mammal.prototype=Object.create(Animal.prototype);Mammal.prototype.constructor=Mammal;Mammal.prototype.liveBirth=functionliveBirth(){};constHuman=function(age,furColor,languageSpoken){if(!(thisinstanceofHuman)){thrownewError('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=functionspeak(){};Baik:
classAnimal{constructor(age){this.age=age;}move(){/* ... */}}classMammalextendsAnimal{constructor(age,furColor){super(age);this.furColor=furColor;}liveBirth(){/* ... */}}classHumanextendsMammal{constructor(age,furColor,languageSpoken){super(age,furColor);this.languageSpoken=languageSpoken;}speak(){/* ... */}}Pola ini sangat berguna di JavaScript dan kamu akan melihatnya di banyak library
seperti jQuery dan Lodash. Hal ini memperbolehkan kode-mu jadi lebih ekspresif,
dan lebih sedikit bertele-tele. Untuk alasan tersebut, aku katakan, gunakan
metode chaining dan lihatlah betapa bersih kodemu nanti. Di fungsi class,
sederhanaya kembalikan this pada akhir setiap fungsi, dan kamu akan merantai
metode class setelahnya.
Buruk:
classCar{constructor(){this.make='Honda';this.model='Accord';this.color='white';}setMake(make){this.make=make;}setModel(model){this.model=model;}setColor(color){this.color=color;}save(){console.log(this.make,this.model,this.color);}}constcar=newCar();car.setColor('pink');car.setMake('Ford');car.setModel('F-150');car.save();Baik:
classCar{constructor(){this.make='Honda';this.model='Accord';this.color='white';}setMake(make){this.make=make;// CATATAN: Mengembalikan this untuk chainingreturnthis;}setModel(model){this.model=model;// CATATAN: Mengembalikan this untuk chainingreturnthis;}setColor(color){this.color=color;// CATATAN: Mengembalikan this untuk chainingreturnthis;}save(){console.log(this.make,this.model,this.color);// CATATAN: Mengembalikan this untuk chainingreturnthis;}}constcar=newCar().setColor('pink').setMake('Ford').setModel('F-150').save();Sebagaimana terkenal dinyatakan di Design Patterns oleh Gang of Four, kamu sebaiknya memilih composition daripada inheritance dimanapun kamu bisa. Terdapat banyak alasan baik untuk menggunakan inheritance dan banyak alasan baik pula untuk menggunakan composition. Poin utamanya untuk pepatah ini adalah jika pikiranmu secara insting lebih memilih inheritance, coba untuk memikir jika composition dapat memodelkan problemmu lebih baik. di beberapa kasus hal tersebut bisa.
Kamu mungkin saja bertanya-tanya, "Kapan aku seharusnya menggunakan inheritance?" hal itu tergantung pada problemnya, tapi ini adalah alasan yg lumayan dari kapan inheritance membuat hal lebih daripada composition:
- Inheritance-mu merepresentasikan sebuah hubungan "adalah-sebuah" dan bukan hubungan "memiliki-sebuah" (Manusia->Hewan vs. Pengguna->DetailPengguna).
- Kamu dapat menggunakan kembali kode dari basis class (Manusa dapat bergerak seperti semua hewan-hewan).
- Kamu ingin membuat perubahan yg global untuk menurunkan class dengan mengubah sebuah basis class. (Mengubah pengeluaran kalori dari semua hewan-hewan ketika ia bergerak)
Buruk:
classEmployee{constructor(name,email){this.name=name;this.email=email;}// ...}// Buruk karena Employee "memiliki" data pajak. EmployeeTaxData bukan sebuah tipe dari EmployeeclassEmployeeTaxDataextendsEmployee{constructor(ssn,salary){super();this.ssn=ssn;this.salary=salary;}// ...}Baik:
classEmployeeTaxData{constructor(ssn,salary){this.ssn=ssn;this.salary=salary;}// ...}classEmployee{constructor(name,email){this.name=name;this.email=email;}setTaxData(ssn,salary){this.taxData=newEmployeeTaxData(ssn,salary);}// ...}Seperti dinyatakan di Clean Code, "Semestinya tidak akan lebih dari satu alasan sebuah class untuk berubah". Hal itu sangat menggoda untuk memadati class dengan fungsionalitas yang banyak, seperti ketika kamu hanya dapat mengambil satu koper dalam penerbanganmu. Masalahnya dengan ini adalah class-mu tidak akan secara konseptual kohesif dan hal hal itu akan memberikan alasan untuk berubah. Meminimalisir jumlah waktu yang kamu gunakan untuk mengubah class sangat penting. Hal itu penting karena jika terlalu banyak fungsionalitas dalam sebuah class dan kamu memodifikasi sebagian saja, hal itu akan sulit untuk dimengerti Bagaimana itu akan mempengaruhi modul lain yg terhubung dalam basis kode-mu.
Buruk:
classUserSettings{constructor(user){this.user=user;}changeSettings(settings){if(this.verifyCredentials()){// ...}}verifyCredentials(){// ...}}Baik:
classUserAuth{constructor(user){this.user=user;}verifyCredentials(){// ...}}classUserSettings{constructor(user){this.user=user;this.auth=newUserAuth(user);}changeSettings(settings){if(this.auth.verifyCredentials()){// ...}}}Seperti yang dinyatakan Bertrand Meyer, "entitas perangkat lunak (class, modul, fungsi, dsb) harus terbuka untuk ekstensi, namun tertutup untuk modifikasi." Apa maksudnya? Prinsip ini dasarnya menyatakan bahwa kamu harus tetap memperbolehkan pengguna untuk menambahkan fungsionalitas baru tanpa mengubah kode yg sudah ada.
Buruk:
classAjaxAdapterextendsAdapter{constructor(){super();this.name='ajaxAdapter';}}classNodeAdapterextendsAdapter{constructor(){super();this.name='nodeAdapter';}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){if(this.adapter.name==='ajaxAdapter'){returnmakeAjaxCall(url).then((response)=>{// mengubah respon dan mengembalikan});}elseif(this.adapter.name==='httpNodeAdapter'){returnmakeHttpCall(url).then((response)=>{// mengubah respon dan mengembalikan});}}}functionmakeAjaxCall(url){// request dan mengembalikan promise}functionmakeHttpCall(url){// request dan mengembalikan promise}Baik:
classAjaxAdapterextendsAdapter{constructor(){super();this.name='ajaxAdapter';}request(url){// request dan mengembalikan promise}}classNodeAdapterextendsAdapter{constructor(){super();this.name='nodeAdapter';}request(url){// request dan mengembalikan promise}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){returnthis.adapter.request(url).then((response)=>{// request dan mengembalikan promise});}}Ini merupakan istilah mengerikan untuk sebuah konsep sederhana. Hal ini secara formal didefinisikan sebagai "jika S adalah sub-tipe dari T, maka objek dari tipe T mungkin terganti oleh objek tipe S (contohnya, objek tipe S mungkin pengganti objek dari tipe T) tanpa mengubah tiap properti dari program (kebenaran, tugas yg dilakukan, dsb)." Hal itu lebih mengerikan daripada definisinya
Penjelasan terbaik dari hal ini adalah jika kamu memiliki parent class dan sebuah child class, maka basis class dan child class dapat digunakan secara bergantian tanpa mendapatkan hasil yg salah. Hal ini mungkin membingungkan, jadi mari kita lihat pada contoh Kotak-Segi Empat klasik. Secara matematis, sebuah kotak adalah Segi Empat, namun jika kamu memodelkannya menggunakan hubungan "adalah-sebuah" via inheritance, maka kamu secara cepat ada dalam masalah
Buruk:
classRectangle{constructor(){this.width=0;this.height=0;}setColor(color){// ...}render(area){// ...}setWidth(width){this.width=width;}setHeight(height){this.height=height;}getArea(){returnthis.width*this.height;}}classSquareextendsRectangle{setWidth(width){this.width=width;this.height=width;}setHeight(height){this.width=height;this.height=height;}}functionrenderLargeRectangles(rectangles){rectangles.forEach((rectangle)=>{rectangle.setWidth(4);rectangle.setHeight(5);constarea=rectangle.getArea();// Buruk: Mengembalikan 25 untuk kotak. Harusnya 20.rectangle.render(area);});}constrectangles=[newRectangle(),newRectangle(),newSquare()];renderLargeRectangles(rectangles);Baik:
classShape{setColor(color){// ...}render(area){// ...}}classRectangleextendsShape{constructor(width,height){super();this.width=width;this.height=height;}getArea(){returnthis.width*this.height;}}classSquareextendsShape{constructor(length){super();this.length=length;}getArea(){returnthis.length*this.length;}}functionrenderLargeShapes(shapes){shapes.forEach((shape)=>{constarea=shape.getArea();shape.render(area);});}constshapes=[newRectangle(4,5),newRectangle(4,5),newSquare(5)];renderLargeShapes(shapes);JavaScript tidak memiliki interface jadi prinsip ini tidak digunakan secara ketat seperti yang lainnya. Akan tetapi, hal ini penting dan relevan walaupun JavaScript tidak memiliki sistem penggolongan (type).
ISP dinyatakan seperti "Klien harus dipaksa untuk bergantung pada interface yg mereka tidak gunakan." Interface adalah kontrak implisit di JavaScript karena duck typing.
Sebuah contoh yang baik untuk dilihat adalah demonstrasi prinsip ini di JavaScript adalah untuk class yang membutuhkan objek pengaturan yang besar. Tidak membutuhkan Klien untuk mengatur banyak opsi adalah keuntungan, karena sebagian besar waktu mereka tidak memerlukan sebua setting. Membuat mereka opsional mencegah mendapati sebuah "fat interface"
Buruk:
classDOMTraverser{constructor(settings){this.settings=settings;this.setup();}setup(){this.rootNode=this.settings.rootNode;this.animationModule.setup();}traverse(){// ...}}const$=newDOMTraverser({rootNode: document.getElementsByTagName('body'),animationModule(){}// Sebagian besar waktu, kita tidak membutuhkan animasi sewaktu tranversing.// ...});Baik:
classDOMTraverser{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$=newDOMTraverser({rootNode: document.getElementsByTagName('body'),options: {animationModule(){}}});Prinsip ini menyatakan dua hal esensial:
- Modul Tingkat-Tinggi seharusnya tidak tergantung pada modul tingkat-rendah. Keduanya harus bergantung pada abstraksi.
- Abstraksi seharusnya tidak bergantung pada detail. Detail harus bergantung pada abstraksi.
Hal ini dapat menjadi susah untuk dimengerti saat pertama kali, namun jika kamu telah bekerja menggunakan AngularJS, kamu telah melihat dari implementasi dari prinsip ini dalam bentuk Dependency Injection (DI). Ketika mereka bukan konsep yang identik, DIP menjaga modul tingkat-tinggi dari mengetahui detail dari modul tingkat-rendah-nya dan mengatur mereka. Hal ini dapat diselesaikan melalui DI. Keuntungan terbesar dari hal ini adalah mengurangi kopel antar modul. Kopel sangat buruk untuk pola pengembangan karena hal itu membuat kodemu susah untuk direfaktor.
Seperti dinyatakan sebelumnya, JavaScript tidak memiliki Interface jadi
abstraksi yang tergantung adalah kontrak implisit. Hal itu dapat dikatakan,
metode dan properti yang sebuah objek/class buka pada objek/class lain. Di
contoh dibawah ini, kontrak implisit adalah Request modul apapun untuk
sebuah InventoryTracker akan memiliki sebuah metode requestItems.
Buruk:
classInventoryRequester{constructor(){this.REQ_METHODS=['HTTP'];}requestItem(item){// ...}}classInventoryTracker{constructor(items){this.items=items;// Buruk: Kita telah menciptakan ketergantungan pada penerapan request tertentu.// Kita harus memiliki sebuah requestItems yg bergantung pada metode request: `request`this.requester=newInventoryRequester();}requestItems(){this.items.forEach((item)=>{this.requester.requestItem(item);});}}constinventoryTracker=newInventoryTracker(['apples','bananas']);inventoryTracker.requestItems();Baik:
classInventoryTracker{constructor(items,requester){this.items=items;this.requester=requester;}requestItems(){this.items.forEach((item)=>{this.requester.requestItem(item);});}}classInventoryRequesterV1{constructor(){this.REQ_METHODS=['HTTP'];}requestItem(item){// ...}}classInventoryRequesterV2{constructor(){this.REQ_METHODS=['WS'];}requestItem(item){// ...}}// Dengan membangun ketergantungan kita secara eksternal dan menyuntikkannya, kita dapat dengan mudah// mengganti modul request kita untuk satu hal baru yang menggunakan WebSockets.constinventoryTracker=newInventoryTracker(['apples','bananas'],newInventoryRequesterV2());inventoryTracker.requestItems();Pengujian itu lebih penting daripada shipping. Jika kamu tidak punya pengujian atau tidak mencukupunya, maka setiap kali kamu ship kodemu kamu tidak akan yakin kalau kamu tidak merusak segalanya. Memutuskan berapa jumlah yang cukup tergantung pada tim-mu, tapi memiliki 100% cakupan (semua statemen dan cabang) adalah bagaimana kamu meraih percaya diri yg snagat tinggi dan kedamaian pikiran pengembang. Hal ini berarti selain memiliki kerangka pengujian yg bagus, kamu juga perlu menggunakan sebuah alat cakupan yang bagus.
Tidak ada alasan untuk tidak menulis pengujuan. Terdapat banyak framework pengujian yg bagus di Javascript, jadi pilih satu yang tim-mu lebih suka. Ketika kamu menemukan satu yang dapat bekerja dengan tim-mu, lalu arahkan untuk selalu menulis test untuk setiap fitur/modul baru yang kamu perkenalkan. Jika kamu lebih suka metode TDD (Test Driven Development), itu hal yang bagus, tapi poin utamanya adalah untuk selalu memastikan kamu meraih cakupan tujuan-tujuan sebelum meluncurkan fitur apapun, atau merefaktor satu yg telah ada.
Buruk:
importassertfrom'assert';describe('MakeMomentJSGreatAgain',()=>{it('handles date boundaries',()=>{letdate;date=newMakeMomentJSGreatAgain('1/1/2015');date.addDays(30);assert.equal('1/31/2015',date);date=newMakeMomentJSGreatAgain('2/1/2016');date.addDays(28);assert.equal('02/29/2016',date);date=newMakeMomentJSGreatAgain('2/1/2015');date.addDays(28);assert.equal('03/01/2015',date);});});Baik:
importassertfrom'assert';describe('MakeMomentJSGreatAgain',()=>{it('handles 30-day months',()=>{constdate=newMakeMomentJSGreatAgain('1/1/2015');date.addDays(30);assert.equal('1/31/2015',date);});it('handles leap year',()=>{constdate=newMakeMomentJSGreatAgain('2/1/2016');date.addDays(28);assert.equal('02/29/2016',date);});it('handles non-leap year',()=>{constdate=newMakeMomentJSGreatAgain('2/1/2015');date.addDays(28);assert.equal('03/01/2015',date);});});Callback itu tidak clean, dan mereka memngakibatkan nesting dengan jumlah yang berlebihan. Dengan ES2015/ES6, promise sudah built-in. Jadi gunakanlah!
Buruk:
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');}});}});Baik:
import{get}from'request';import{writeFile}from'fs';get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin').then((response)=>{returnwriteFile('article.html',response);}).then(()=>{console.log('File written');}).catch((err)=>{console.error(err);});Promise adalah alternatif dari callback yang sangat bersih, namun ES2017/ES8 membawa
async dan await yang mana menawarkan solusi yang lebih bersih lagi. Yang kamu perlukan
adalah fungsi yang memiliki prefiks sebuah kata kunci async, lalu kamu dapat
menulis logika secara imperatif tanpa rantai sebuah then dari fungsi. Gunakan
ini jika kamu dapat mengambil manfaat dari fitur ES2017/ES8 hari ini!
Buruk:
import{get}from'request-promise';import{writeFile}from'fs-promise';get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin').then((response)=>{returnwriteFile('article.html',response);}).then(()=>{console.log('File written');}).catch((err)=>{console.error(err);});Baik:
import{get}from'request-promise';import{writeFile}from'fs-promise';asyncfunctiongetCleanCodeArticle(){try{constresponse=awaitget('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');awaitwriteFile('article.html',response);console.log('File written');}catch(err){console.error(err);}}Dilempari error adalah sebuah hal yang baik! Hal itu berarti di runtime telah sukses mengidentifikasi ketika sesuatu di programmu ada kesalahan dan hal itu memberi kesempatan kepadamu untuk menghentikan eksekusi fungsi dari stack yang sekarang, menghentikan process (di Node) dan memberitahumu di konsol dengan tumpukan jejak.
Tidak melakukan apapun dengan error yg tertangkan tidak akan memberikanmu
kemampuan untuk membetulkan atau bereaksi pada error yang telah dikatakan.
Mencatat error untuk konsol (console.log) tidak jauh lebih baik seperti
sering kali bisa tersesat di lautan benda-benda yang dicetak ke konsol.
Jika kamu menyelimuti kodemu sedikit-sedikit di sebuah try/catch hal itu
berarti kamu memikirkan sebuah error yang mungkin saja muncul disana dan
oleh karena itu kamu harus memiliki sebuah rencana, atau membuat sebuah
jalur kode, untuk kapan hal itu akan muncul.
Buruk:
try{functionThatMightThrow();}catch(error){console.log(error);}Baik:
try{functionThatMightThrow();}catch(error){// Satu opsi (lebih berisik daripada console.log):console.error(error);// opsi lainnya:notifyUserOfError(error);// opsi lainnya:reportErrorToService(error);// atau lakukan ketiganya!}Untuk alasan yang sama seperti kamu tidak boleh mengabaikan error yang tertangkap
dari try/catch.
Buruk:
getdata().then((data)=>{functionThatMightThrow(data);}).catch((error)=>{console.log(error);});Baik:
getdata().then((data)=>{functionThatMightThrow(data);}).catch((error)=>{// Satu opsi (lebih berisik daripada console.log):console.error(error);// Opsi lain:notifyUserOfError(error);// Opsi lain:reportErrorToService(error);// Atau lakukan ketiganya!});Menyusun format adalah subjektif. Seperti aturan disini, tidak ada aturan keras yang harus kamu ikuti. Poin utamanya adalah JANGAN BERDEBAT atas format. Ada banyak sekali alat untuk mengotomasi hal ini. Gunakan salah satu! Hal ini adalah pemborosan waktu dan uang para engineer untuk berdebat soal format.
Untuk hal-hal yang tidak termasuk dalam hal format otomatis (indentasi, tabulasi vs. spasi, ganda vs tanda petik tunggal, dll) lihatlah kesini untuk beberapa bimbingan.
JavaScript tidak memiliki penggolongan, jadi kapitalisasi memberitahumu banyak tentang variabel, fungsi, dll. Aturan ini adalah subjektif, jadi tim-mu dapat memilih apapun yang mereka inginkan. Poinnya adalah, tidak masalah apa yang kalian semua pilih, cukup tetaplah konsisten.
Buruk:
constDAYS_IN_WEEK=7;constdaysInMonth=30;constsongs=['Back In Black','Stairway to Heaven','Hey Jude'];constArtists=['ACDC','Led Zeppelin','The Beatles'];functioneraseDatabase(){}functionrestore_database(){}classanimal{}classAlpaca{}Baik:
constDAYS_IN_WEEK=7;constDAYS_IN_MONTH=30;constsongs=['Back In Black','Stairway to Heaven','Hey Jude'];constartists=['ACDC','Led Zeppelin','The Beatles'];functioneraseDatabase(){}functionrestoreDatabase(){}classAnimal{}classAlpaca{}Jika sebuah fungsi memanggil yang lainnya, jagalah fungsi tersebut secara vertikal berdekatan dengan sumber. Secara ideal, jaga yang memanggil tepat dibawah yang dipanggil. Kita cenderung membaca kode dari atas-ke-bawah, seperti koran. Karenanya, buat kodemu terbaca seperti itu.
Buruk:
classPerformanceReview{constructor(employee){this.employee=employee;}lookupPeers(){returndb.lookup(this.employee,'peers');}lookupManager(){returndb.lookup(this.employee,'manager');}getPeerReviews(){constpeers=this.lookupPeers();// ...}perfReview(){this.getPeerReviews();this.getManagerReview();this.getSelfReview();}getManagerReview(){constmanager=this.lookupManager();}getSelfReview(){// ...}}constreview=newPerformanceReview(employee);review.perfReview();Baik:
classPerformanceReview{constructor(employee){this.employee=employee;}perfReview(){this.getPeerReviews();this.getManagerReview();this.getSelfReview();}getPeerReviews(){constpeers=this.lookupPeers();// ...}lookupPeers(){returndb.lookup(this.employee,'peers');}getManagerReview(){constmanager=this.lookupManager();}lookupManager(){returndb.lookup(this.employee,'manager');}getSelfReview(){// ...}}constreview=newPerformanceReview(employee);review.perfReview();Komentar adalah sebuah permintaan maaf, bukan sebuah syarat. Kode yang baik kebanyakan bisa mendokumentasikan dirinya sendiri.
Buruk:
functionhashIt(data){// ini hashlethash=0;// panjang dari sebuah stringconstlength=data.length;// Loop setiap karakter dalam sebuah datafor(leti=0;i<length;i++){// mendapatkan karakter di kodeconstchar=data.charCodeAt(i);// Membuah hashhash=((hash<<5)-hash)+char;// mengonversi ke 32-bit integerhash&=hash;}}Baik:
functionhashIt(data){lethash=0;constlength=data.length;for(leti=0;i<length;i++){constchar=data.charCodeAt(i);hash=((hash<<5)-hash)+char;// Mengonversi ke 32-bit integerhash&=hash;}}Version control ada karena sebuah alasan. Tinggalkan kode lamamu di riwayat.
Buruk:
doStuff();// doOtherStuff();// doSomeMoreStuff();// doSoMuchStuff();Baik:
doStuff();Ingat, gunakan version control! Tidak perlu kode mati, kode yang dikomentari,
dan khususnya komentar tentang jurnal. Gunakan git log untuk mendapatkan
riwayat!
Buruk:
/** * 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) */functioncombine(a,b){returna+b;}Baik:
functioncombine(a,b){returna+b;}Mereka biasanya hanya menambahkan kebisingan. Biarkan nama fungsi dan variabel bersama dengan indentasi yang layak dan susunan format memberikan struktur visual ke kode-mu.
Buruk:
////////////////////////////////////////////////////////////////////////////////// Scope Model Instantiation////////////////////////////////////////////////////////////////////////////////$scope.model={menu: 'foo',nav: 'bar'};////////////////////////////////////////////////////////////////////////////////// Action setup////////////////////////////////////////////////////////////////////////////////constactions=function(){// ...};Baik:
$scope.model={menu: 'foo',nav: 'bar'};constactions=function(){// ...};Panduan ini juga tersedia di berbagai bahasa:
Bahasa Brazil Portugis: fesnt/clean-code-javascript
Bahasa Spanyol: andersontr15/clean-code-javascript
Bahasa Mandarin:
Bahasa Jerman: marcbruederlin/clean-code-javascript
Bahasa Korea: qkraudghgh/clean-code-javascript-ko
Bahasa Polandia: greg-dev/clean-code-javascript-pl
Bahasa Rusia:
Bahasa Vietnam: hienvd/clean-code-javascript/
Bahasa Jepang: mitsuruog/clean-code-javascript/
