From ccd53bfb9362604fdb26e1fac71d68173dcdd89b Mon Sep 17 00:00:00 2001 From: Greg Dev Date: Tue, 16 May 2017 00:11:25 +0200 Subject: [PATCH 001/170] Add Polish translation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 42b9156a..05cb0b83 100644 --- a/README.md +++ b/README.md @@ -2174,6 +2174,7 @@ This is also available in other languages: - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) From 51683128c4d58372027c112f797c605266d578bb Mon Sep 17 00:00:00 2001 From: "Andi R. Hermawan" Date: Fri, 2 Jun 2017 11:47:17 +0700 Subject: [PATCH 002/170] Just Add an Indonesia Language Link (#219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * translate kata pengantar / introduction * translate variabel * typo * function 1/2 * 1/2 replace * translate ### 😂 * translate Function section in Bahasa Indonesia is done * 1/2 SOLID translation to Bahasa Indonesia * SOLID translation to Bahasa Indonesia is Done * translating Error Handling to bahasa indonesia is done * well done, translated to Bahasa Indonesia * review 1, check typo * review 2 * just link --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 05cb0b83..e82f8a3d 100644 --- a/README.md +++ b/README.md @@ -2180,5 +2180,7 @@ This is also available in other languages: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) + - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: + [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) **[⬆ back to top](#table-of-contents)** From eae257506cd019b17290d2680a4b284e34dc3d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Zaj=C4=85c?= Date: Fri, 2 Jun 2017 18:32:07 +0200 Subject: [PATCH 003/170] Update README.md Capital letters in const, remove default data in class Car --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e82f8a3d..1c899561 100644 --- a/README.md +++ b/README.md @@ -1148,9 +1148,9 @@ car.save(); ```javascript class Car { constructor() { - this.make = 'Honda'; - this.model = 'Accord'; - this.color = 'white'; + this.make = ''; + this.model = ''; + this.color = ''; } setMake(make) { @@ -1945,8 +1945,8 @@ class Alpaca {} 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']; +const SONGS = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +const ARTISTS = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restoreDatabase() {} From 465c3800ab371337b6a561a56b6484fd5948dd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Zaj=C4=85c?= Date: Wed, 7 Jun 2017 20:31:27 +0200 Subject: [PATCH 004/170] Pass make, model and color in constructor --- README.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 1c899561..bbca759c 100644 --- a/README.md +++ b/README.md @@ -1114,10 +1114,10 @@ and you can chain further class methods onto it. **Bad:** ```javascript class Car { - constructor() { - this.make = 'Honda'; - this.model = 'Accord'; - this.color = 'white'; + constructor(make, model, color) { + this.make = make; + this.model = model; + this.color = color; } setMake(make) { @@ -1137,20 +1137,18 @@ class Car { } } -const car = new Car(); +const car = new Car('Ford','F-150','red'); car.setColor('pink'); -car.setMake('Ford'); -car.setModel('F-150'); car.save(); ``` **Good:** ```javascript class Car { - constructor() { - this.make = ''; - this.model = ''; - this.color = ''; + constructor(make, model, color) { + this.make = make; + this.model = model; + this.color = color; } setMake(make) { @@ -1178,10 +1176,8 @@ class Car { } } -const car = new Car() +const car = new Car('Ford','F-150','red') .setColor('pink') - .setMake('Ford') - .setModel('F-150') .save(); ``` **[⬆ back to top](#table-of-contents)** From e12f62f10574eb557d1b9317f5710bc840c68c58 Mon Sep 17 00:00:00 2001 From: filipetedim Date: Wed, 5 Jul 2017 10:34:56 +0100 Subject: [PATCH 005/170] Fixed duplicate code The proposed "good" way of coding was actually changing a property's name in the developers object. While one can argue that in this case they could be the same, the point of the example was to show that you can still remove duplicated code that require *different* things. --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bbca759c..b7cb2216 100644 --- a/README.md +++ b/README.md @@ -439,23 +439,21 @@ function showEmployeeList(employees) { employees.forEach((employee) => { const expectedSalary = employee.calculateExpectedSalary(); const experience = employee.getExperience(); + + let data = { + expectedSalary, + experience + }; - let portfolio; switch (employee.type) { case 'manager': - portfolio = employee.getMBAProjects(); + data.portfolio = employee.getMBAProjects(); break; case 'developer': - portfolio = employee.getGithubLink(); + data.githubLink = employee.getGithubLink(); break; } - const data = { - expectedSalary, - experience, - portfolio - }; - render(data); }); } From fdbd90782d61772e9a6a309271567541f4237a51 Mon Sep 17 00:00:00 2001 From: Lars Gyrup Brink Nielsen Date: Wed, 19 Jul 2017 22:47:33 +0200 Subject: [PATCH 006/170] Object style consistency Remove space before colon in object. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7cb2216..7de65ace 100644 --- a/README.md +++ b/README.md @@ -621,7 +621,7 @@ const addItemToCart = (cart, item) => { **Good:** ```javascript const addItemToCart = (cart, item) => { - return [...cart, { item, date : Date.now() }]; + return [...cart, { item, date: Date.now() }]; }; ``` From 2ae57ca9beb9bac75bdf1a6ccf455226b13d9e95 Mon Sep 17 00:00:00 2001 From: Redstone Zhao <13740080@qq.com> Date: Fri, 28 Jul 2017 23:56:37 +0000 Subject: [PATCH 007/170] 'data' is never reassigned. Use 'const' instead. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7de65ace..622e43f2 100644 --- a/README.md +++ b/README.md @@ -439,8 +439,8 @@ function showEmployeeList(employees) { employees.forEach((employee) => { const expectedSalary = employee.calculateExpectedSalary(); const experience = employee.getExperience(); - - let data = { + + const data = { expectedSalary, experience }; From 177b636bc00382f39a8305929155dca48b6542ca Mon Sep 17 00:00:00 2001 From: snirad Date: Sat, 29 Jul 2017 20:37:00 +0300 Subject: [PATCH 008/170] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7de65ace..b13da613 100644 --- a/README.md +++ b/README.md @@ -841,7 +841,7 @@ function travelToTexas(vehicle) { **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 2) -If you are working with basic primitive values like strings, integers, and arrays, +If you are working with basic primitive values like strings and integers, and you can't use polymorphism but you still feel the need to type-check, you should consider using TypeScript. It is an excellent alternative to normal JavaScript, as it provides you with static typing on top of standard JavaScript From 1d7362d986570ea1221c3efaa56a641df5216c2b Mon Sep 17 00:00:00 2001 From: Vladimir Karamyshev Date: Sun, 3 Sep 2017 02:07:24 +0300 Subject: [PATCH 009/170] Replace map method with object destructuring --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ef70ff62..8d6fefe7 100644 --- a/README.md +++ b/README.md @@ -708,8 +708,7 @@ const programmerOutput = [ const INITIAL_VALUE = 0; const totalOutput = programmerOutput - .map((programmer) => programmer.linesOfCode) - .reduce((acc, linesOfCode) => acc + linesOfCode, INITIAL_VALUE); + .reduce((acc, { linesOfCode }) => acc + linesOfCode, INITIAL_VALUE); ``` **[⬆ back to top](#table-of-contents)** From 672434a461bb444a830e6abd79e3d8ffe637f9b5 Mon Sep 17 00:00:00 2001 From: Dave C Date: Tue, 19 Sep 2017 11:13:17 -0500 Subject: [PATCH 010/170] minor grammar edit There's -> There are --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d6fefe7..6f7d03fe 100644 --- a/README.md +++ b/README.md @@ -1684,7 +1684,7 @@ 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](http://gotwarlost.github.io/istanbul/). -There's no excuse to not write tests. There's [plenty of good JS test frameworks](http://jstherightway.org/#testing-tools), so find one that your team prefers. +There's no excuse to not write tests. There are [plenty of good JS test frameworks](http://jstherightway.org/#testing-tools), 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 From dda95822ed3f0a00eaaec79a6b466b004e91d065 Mon Sep 17 00:00:00 2001 From: Nicolas Gomes De Oliveira Date: Mon, 16 Oct 2017 17:02:45 +0200 Subject: [PATCH 011/170] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f7d03fe..b682cdd1 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ function addToDate(date, month) { const date = new Date(); -// It's hard to to tell from the function name what is added +// It's hard to tell from the function name what is added addToDate(date, 1); ``` From 263d2b1cb49b9c8392002de4aa9ef8afae9b6e09 Mon Sep 17 00:00:00 2001 From: Vinicius Brasil Date: Sun, 5 Nov 2017 17:15:57 -0200 Subject: [PATCH 012/170] fix "Use searchable names" section It doesn't need to be a global constant. JavaScript constants are block-scoped. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b682cdd1..86267600 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ setTimeout(blastOff, 86400000); **Good:** ```javascript -// Declare them as capitalized `const` globals. +// Declare them as capitalized named constants. const MILLISECONDS_IN_A_DAY = 86400000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); From a3233f5517f6aa6bbb9fbfe5d972a635b075dd34 Mon Sep 17 00:00:00 2001 From: Dan Wood Date: Tue, 21 Nov 2017 17:57:44 +1100 Subject: [PATCH 013/170] Change complex reduce funciton to clearer map reduce. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 86267600..35cf3ced 100644 --- a/README.md +++ b/README.md @@ -659,7 +659,7 @@ class SuperArray extends Array { ### Favor functional programming over imperative programming JavaScript isn't a functional language in the way that Haskell is, but it has -a functional flavor to it. Functional languages are cleaner and easier to test. +a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can. **Bad:** @@ -708,7 +708,8 @@ const programmerOutput = [ const INITIAL_VALUE = 0; const totalOutput = programmerOutput - .reduce((acc, { linesOfCode }) => acc + linesOfCode, INITIAL_VALUE); + .map(output => output.linesOfCode) + .reduce((totalLines, lines) => totalLines + lines); ``` **[⬆ back to top](#table-of-contents)** From eb364be959c3477a5c5e911e5bab5e8c1b51109a Mon Sep 17 00:00:00 2001 From: Dan Wood Date: Wed, 22 Nov 2017 10:44:15 +1100 Subject: [PATCH 014/170] Remove unnecessary INITAL_VALUE const --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 35cf3ced..3316bb54 100644 --- a/README.md +++ b/README.md @@ -705,8 +705,6 @@ const programmerOutput = [ } ]; -const INITIAL_VALUE = 0; - const totalOutput = programmerOutput .map(output => output.linesOfCode) .reduce((totalLines, lines) => totalLines + lines); From 69df4896bdd85ab3167c6bf32d3dcb79b7dea335 Mon Sep 17 00:00:00 2001 From: Jeffrey van der Heide Date: Mon, 15 Jan 2018 12:05:09 +0100 Subject: [PATCH 015/170] Change argument name of "Use default arguments instead of short circuiting or conditionals". Did not adhere to "Don't add unneeded context". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3316bb54..a401258c 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ function createMicrobrewery(name) { **Good:** ```javascript -function createMicrobrewery(breweryName = 'Hipster Brew Co.') { +function createMicrobrewery(name = 'Hipster Brew Co.') { // ... } From f6ab827ed7394f8661669a5b6de86aac825193d6 Mon Sep 17 00:00:00 2001 From: Franz Knipp Date: Fri, 23 Feb 2018 12:33:13 +0100 Subject: [PATCH 016/170] Changing order in example of *Functions should only be one level of abstraction* Like said in https://github.com/ryanmcdermott/clean-code-javascript#function-callers-and-callees-should-be-close, the code is read from top to bottom, so I'd prefer the definition of the *function parseBetterJSAlternative* above the functions called within. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a401258c..47305f60 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ saveCityZipCode(city, zipCode); ``` **[⬆ back to top](#table-of-contents)** -### Avoid Mental Mapping +### Avoid Mental Mappingf Explicit is better than implicit. **Bad:** @@ -343,6 +343,14 @@ function parseBetterJSAlternative(code) { **Good:** ```javascript +function parseBetterJSAlternative(code) { + const tokens = tokenize(code); + const ast = lexer(tokens); + ast.forEach((node) => { + // parse... + }); +} + function tokenize(code) { const REGEXES = [ // ... @@ -367,14 +375,6 @@ function lexer(tokens) { return ast; } - -function parseBetterJSAlternative(code) { - const tokens = tokenize(code); - const ast = lexer(tokens); - ast.forEach((node) => { - // parse... - }); -} ``` **[⬆ back to top](#table-of-contents)** From 3ada14239ba970aa3ccf832334b2e9205468f3fc Mon Sep 17 00:00:00 2001 From: Franz Knipp Date: Fri, 23 Feb 2018 12:39:12 +0100 Subject: [PATCH 017/170] Fixing typo introduced by previous patch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 47305f60..db12e609 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ saveCityZipCode(city, zipCode); ``` **[⬆ back to top](#table-of-contents)** -### Avoid Mental Mappingf +### Avoid Mental Mapping Explicit is better than implicit. **Bad:** From 4dfb70766f0080bdfae3a7e528ee78f95c55d42a Mon Sep 17 00:00:00 2001 From: edward liu <35801357+ForJing@users.noreply.github.com> Date: Sun, 24 Jun 2018 21:10:10 +0800 Subject: [PATCH 018/170] Update README.md there are `nodeAdapter`, no `httpNodeAdapter` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db12e609..4902180e 100644 --- a/README.md +++ b/README.md @@ -1334,7 +1334,7 @@ class HttpRequester { return makeAjaxCall(url).then((response) => { // transform response and return }); - } else if (this.adapter.name === 'httpNodeAdapter') { + } else if (this.adapter.name === 'nodeAdapter') { return makeHttpCall(url).then((response) => { // transform response and return }); From d98f9e71f0619f33c44926616fa7649aee20dce5 Mon Sep 17 00:00:00 2001 From: Wolf Oliver Date: Sun, 8 Jul 2018 09:30:35 +0200 Subject: [PATCH 019/170] improve "Functions should only be one level of abstraction" example - rename ast variable to be more explicit (see, Avoid Mental Mapping) - rename function lexer to include a verb in it's name (see, https://herbertograca.com/2016/09/03/clean-code-3-functions-by-robert-c-martin-uncle-bob/) - from my understanding, the term lexer is usally used for the process of building a stream of tokens. So lexer and tokenize are different names for the same thing --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index db12e609..af7fe8b0 100644 --- a/README.md +++ b/README.md @@ -345,8 +345,8 @@ function parseBetterJSAlternative(code) { ```javascript function parseBetterJSAlternative(code) { const tokens = tokenize(code); - const ast = lexer(tokens); - ast.forEach((node) => { + const syntaxTree = parse(tokens); + syntaxTree.forEach((node) => { // parse... }); } @@ -367,13 +367,13 @@ function tokenize(code) { return tokens; } -function lexer(tokens) { - const ast = []; +function parse(tokens) { + const syntaxTree = []; tokens.forEach((token) => { - ast.push( /* ... */ ); + syntaxTree.push( /* ... */ ); }); - return ast; + return syntaxTree; } ``` **[⬆ back to top](#table-of-contents)** From 7b280e4b9d50fe89eb514a59bd473a7b55997539 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Thu, 11 Oct 2018 07:51:21 +0200 Subject: [PATCH 020/170] Starting translate --- README.md | 595 ++++++++++++++++++++++++------------------------------ 1 file changed, 268 insertions(+), 327 deletions(-) diff --git a/README.md b/README.md index db12e609..9789e9ef 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,108 @@ # clean-code-javascript -## Table of Contents - 1. [Introduction](#introduction) - 2. [Variables](#variables) - 3. [Functions](#functions) - 4. [Objects and Data Structures](#objects-and-data-structures) - 5. [Classes](#classes) +## Lista dei contenuti + 1. [Introduzione](#introduzione) + 2. [Variabili](#variabili) + 3. [Funzioni](#funzioni) + 4. [Ogetti e strutture dati](#objects-and-data-structures) + 5. [Classi](#Classi) 6. [SOLID](#solid) - 7. [Testing](#testing) + 7. [Test](#Test) 8. [Concurrency](#concurrency) 9. [Error Handling](#error-handling) 10. [Formatting](#formatting) 11. [Comments](#comments) 12. [Translation](#translation) -## Introduction -![Humorous image of software quality estimation as a count of how many expletives -you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) +## Introduzione +![Immagine umoristica che rappresenta quanto sia possibile stimare la qualità di un software attraverso il numero di parolacce espresse durante la lettura del codice](http://www.osnews.com/images/comics/wtfm.jpg) -Software engineering principles, from Robert C. Martin's book +Principi di Ingegneria del Software, dal libro di Robert C. Martin [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adapted for JavaScript. This is not a style guide. It's a guide to producing -[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. +adattati a JavaScript. Non si tratta di una guida stilistica, bensì una guida per cercare di produrre software +[leggibile, riutilizzabile e rifattorizzabile](https://github.com/ryanmcdermott/3rs-of-software-architecture) in JavaScript. -Not every principle herein has to be strictly followed, and even fewer will be -universally agreed upon. These are guidelines and nothing more, but they are -ones codified over many years of collective experience by the authors of -*Clean Code*. +Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code* -Our craft of software engineering is just a bit over 50 years old, and we are -still learning a lot. When software architecture is as old as architecture -itself, maybe then we will have harder rules to follow. For now, let these -guidelines serve as a touchstone by which to assess the quality of the -JavaScript code that you and your team produce. +Il nostro lavoro come ingegnieri del software ha solo 50 anni e stiamo ancora apprendendo molto. Quando l'architettura del software sarà antica come l'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. -One more thing: knowing these won't immediately make you a better software -developer, and working with them for many years doesn't mean you won't make -mistakes. Every piece of code starts as a first draft, like wet clay getting -shaped into its final form. Finally, we chisel away the imperfections when -we review it with our peers. Don't beat yourself up for first drafts that need -improvement. Beat up the code instead! +Un ultima cosa: conoscere queste regole non farà di te immediatamente uno sviluppatore di software migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. +Ogni singola parte di codice parte come bozza, prima, per per poi prendere forma come una scultura di argilla. +Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice con i nostri colleghi. Ma non ti abbattre alla prima revisione che richiederà miglioramenti: *Beat up the code instead!* -## **Variables** -### Use meaningful and pronounceable variable names +## **Variabili** +### Utilizza nomi di variabili comprensibili e pronunciabili -**Bad:** +**Male:** ```javascript const yyyymmdstr = moment().format('YYYY/MM/DD'); ``` -**Good:** +**Bene:** ```javascript const currentDate = moment().format('YYYY/MM/DD'); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Use the same vocabulary for the same type of variable +### Usa lo stesso lessico per lo stesso tipo di variabili -**Bad:** +**Male:** ```javascript getUserInfo(); getClientData(); getCustomerRecord(); ``` -**Good:** +**Bene:** ```javascript getUser(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Use searchable names -We will read more code than we will ever write. It's important that the code we -do write is readable and searchable. By *not* naming variables that end up -being meaningful for understanding our program, we hurt our readers. -Make your names searchable. Tools like -[buddy.js](https://github.com/danielstjules/buddy.js) and -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) -can help identify unnamed constants. +### Utilizza nomi ricercabili +Leggeremo molto più codice di quanto non ne scriveremo mai. È importante che il codice che noi scriviamo sia leggibile e ricercabile. Nominando variabili che non assumono uno specifico contesto all'interno del nostro software, irritiamo il lettore. +Fai in modo che i nomi delle tue variabili siano ricercabili. +Strumenti come [buddy.js](https://github.com/danielstjules/buddy.js) e +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare costanti non rinominate. -**Bad:** +**Male:** ```javascript -// What the heck is 86400000 for? +// Cosa caspita significa 86400000? setTimeout(blastOff, 86400000); ``` -**Good:** +**Bene:** ```javascript -// Declare them as capitalized named constants. +// Dichiarala come costante in maiuscolo. const MILLISECONDS_IN_A_DAY = 86400000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Use explanatory variables -**Bad:** +### Utilizza nomi di variabili esplicartivi +**Male:** ```javascript const address = 'One Infinite Loop, Cupertino 95014'; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]); ``` -**Good:** +**Bene:** ```javascript const address = 'One Infinite Loop, Cupertino 95014'; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; const [, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid Mental Mapping -Explicit is better than implicit. +### Evita mappe mentali +Essere espliciti è meglio che non esserlo. -**Bad:** +**Male:** ```javascript const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { @@ -125,12 +111,12 @@ locations.forEach((l) => { // ... // ... // ... - // Wait, what is `l` for again? + // A cosa fa riferimento esattamente `l`? dispatch(l); }); ``` -**Good:** +**Bene:** ```javascript const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { @@ -142,13 +128,13 @@ locations.forEach((location) => { dispatch(location); }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't add unneeded context -If your class/object name tells you something, don't repeat that in your -variable name. +### Non contestualizzare inutilmente -**Bad:** +Se il nome della tua classe/oggetto ti indica a cosa fa riferimento, non ripeterlo nei nomi delle sue proprietà o funzioni. + +**Male:** ```javascript const Car = { carMake: 'Honda', @@ -161,7 +147,7 @@ function paintCar(car) { } ``` -**Good:** +**Bene:** ```javascript const Car = { make: 'Honda', @@ -173,15 +159,15 @@ function paintCar(car) { car.color = 'Red'; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Utilizza i valori di default (predefiniti), anzichè usare condizioni o cortocircuiti -### Use default arguments instead of short circuiting or conditionals -Default arguments are often cleaner than short circuiting. Be aware that if you -use them, your function will only provide default values for `undefined` -arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and -`NaN`, will not be replaced by a default value. +nonI valori di default, generalmente sono più chiari dei cortocircuiti. Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. +Tutti gli altri valori "falsi" come `''`, `""`, `false`, `null`, `0`, e +`NaN`, non saranno sostituiti da un valore predefinito. -**Bad:** +**Male:** ```javascript function createMicrobrewery(name) { const breweryName = name || 'Hipster Brew Co.'; @@ -190,52 +176,43 @@ function createMicrobrewery(name) { ``` -**Good:** +**Bene:** ```javascript function createMicrobrewery(name = 'Hipster Brew Co.') { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +## **Funzioni** +### Argomenti di una funzione (idealmente 2 o anche meno) -## **Functions** -### Function arguments (2 or fewer ideally) -Limiting the amount of function parameters is incredibly important because it -makes testing your function easier. Having more than three leads to a -combinatorial explosion where you have to test tons of different cases with -each separate argument. +Limitare il numero di argomenti di una funzione è incredibilmente importante perchè ti permette di testarla più facilmente. Avere più di 3 argomenti può portare ad un'esplosione di combinazioni da testare, che produrranno una lunga serie di casi da verificare. -One or two arguments is the ideal case, and three should be avoided if possible. -Anything more than that should be consolidated. Usually, if you have -more than two arguments then your function is trying to do too much. In cases -where it's not, most of the time a higher-level object will suffice as an -argument. +1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. In alcuni casi, in cui questo non sia del tutto vero, un oggetto può aiutare ad ovviare a questo problema. -Since JavaScript allows you to make objects on the fly, without a lot of class -boilerplate, you can use an object if you are finding yourself needing a -lot of arguments. +Dal momento in cui JavaScript permette la creazione di oggetti al volo, senza dover passare attraverso classi specifiche, puoi usare un oggetto se pensi che il tuo metodo richieda molti argomenti. + +Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi To make it obvious what properties the function expects, you can use the ES2015/ES6 -destructuring syntax. This has a few advantages: +. This has a few advantages: + +1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà sono state utilizzate -1. When someone looks at the function signature, it's immediately clear what -properties are being used. -2. Destructuring also clones the specified primitive values of the argument -object passed into the function. This can help prevent side effects. Note: -objects and arrays that are destructured from the argument object are NOT -cloned. -3. Linters can warn you about unused properties, which would be impossible -without destructuring. +2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti indesiderati. Nota: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. -**Bad:** +3. Un Linter può avvertirti che non stai utilizzando alcune delle proprietà del tuo oggetto, non utilizzando la sintassi destrutturata non sarebbe possibile + +**Male:** ```javascript function createMenu(title, body, buttonText, cancellable) { // ... } ``` -**Good:** +**Bene:** ```javascript function createMenu({ title, body, buttonText, cancellable }) { // ... @@ -248,17 +225,14 @@ createMenu({ cancellable: true }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Functions should do one thing -This is by far the most important rule in software engineering. When functions -do more than one thing, they are harder to compose, test, and reason about. -When you can isolate a function to just one action, they can be refactored -easily and your code will read much cleaner. If you take nothing else away from -this guide other than this, you'll be ahead of many developers. +### Un metodo dovrebbe fare una sola cosa +Questa è di sicuro la regola più importante nell'ingegneria del software. Quando un metodo si occupa di più di un solo aspetto sarà più difficile da testare, comporre e ragioraci sopra. +Se è possibile far eseguire al metodo una sola azione sarà più facile da rifattorizzare e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. -**Bad:** +**Male:** ```javascript function emailClients(clients) { clients.forEach((client) => { @@ -270,7 +244,7 @@ function emailClients(clients) { } ``` -**Good:** +**Bene:** ```javascript function emailActiveClients(clients) { clients @@ -283,11 +257,11 @@ function isActiveClient(client) { return clientRecord.isActive(); } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Function names should say what they do +### I nomi delle funzioni dovrebbero farti capire cosa fanno -**Bad:** +**Male:** ```javascript function addToDate(date, month) { // ... @@ -295,11 +269,11 @@ function addToDate(date, month) { const date = new Date(); -// It's hard to tell from the function name what is added +// Difficile da dire esattamente cosa viene aggiunto tramite questa funzione addToDate(date, 1); ``` -**Good:** +**Bene:** ```javascript function addMonthToDate(month, date) { // ... @@ -308,14 +282,13 @@ function addMonthToDate(month, date) { const date = new Date(); addMonthToDate(1, date); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### I mteodi dovrebbero avere un solo livello di astrazione -### Functions should only be one level of abstraction -When you have more than one level of abstraction your function is usually -doing too much. Splitting up functions leads to reusability and easier -testing. +Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarla ed a testarla più facilmente. -**Bad:** +**Male:** ```javascript function parseBetterJSAlternative(code) { const REGEXES = [ @@ -341,7 +314,7 @@ function parseBetterJSAlternative(code) { } ``` -**Good:** +**Bene:** ```javascript function parseBetterJSAlternative(code) { const tokens = tokenize(code); @@ -376,31 +349,20 @@ function lexer(tokens) { return ast; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Remove duplicate code -Do your absolute best to avoid duplicate code. Duplicate code is bad because it -means that there's more than one place to alter something if you need to change -some logic. +### Rimuovi il codice duplicato +Fai del tuo meglio per evitare codice duplicato. Duplicare il codice è un male, perchè vuol dire che c'è più di un punto da modificare nel caso in cui dovessi cambiare alcune logiche. -Imagine if you run a restaurant and you keep track of your inventory: all your -tomatoes, onions, garlic, spices, etc. If you have multiple lists that -you keep this on, then all have to be updated when you serve a dish with -tomatoes in them. If you only have one list, there's only one place to update! +Immagina di avere un ristorante e di dover tener traccia del tuo magazzino: la riserva di pomodori, cipolle, aglio, spezie, etc. Se hai più di una lista in cui tieni traccia di queste quantità dovrai aggiornarle tutte, ogni volta che servirai un piatto con dei pomodori. Al contrario, se dovessi avere una sola lista, avrai un solo posto un cui dovrai tenere traccia delle modifiche sulle quantità in magazzino. -Oftentimes you have duplicate code because you have two or more slightly -different things, that share a lot in common, but their differences force you -to have two or more separate functions that do much of the same things. Removing -duplicate code means creating an abstraction that can handle this set of -different things with just one function/module/class. +Generalmente si duplica il codice perchè ci sono due o tre piccole differenze tra una parte e l'altra del software. Questo permette di condividere le parti comuni del codice, ma allo stesso tempo avrai dei duplicati di parti che fanno la stessa cosa. +Rimuovere questi duplicati, significa creare un'astrazione che permette di gestire queste differenze attraverso un unico metodo/modulo/classe. -Getting the abstraction right is critical, that's why you should follow the -SOLID principles laid out in the *Classes* section. Bad abstractions can be -worse than duplicate code, so be careful! Having said this, if you can make -a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself -updating multiple places anytime you want to change one thing. +Ottenere la sufficiente astrazione può essere complicato. Per questo dovresti seguire i principi SOLID, approfonditi nella sezione *Classi*. +Un'astrazione non ottimale potrebbe anche essere peggio del codice duplicato, per cui fai attenzione! Non ripeterti, altrimenti dovrai aggiornare tutte le occorrenze della stessa logica ogni volta che vorrai cambiare qualcosa. -**Bad:** +**Male:** ```javascript function showDeveloperList(developers) { developers.forEach((developer) => { @@ -433,7 +395,7 @@ function showManagerList(managers) { } ``` -**Good:** +**Bene:** ```javascript function showEmployeeList(employees) { employees.forEach((employee) => { @@ -458,11 +420,11 @@ function showEmployeeList(employees) { }); } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Set default objects with Object.assign +### Estendi un oggetto con Object.assign -**Bad:** +**Male:** ```javascript const menuConfig = { title: null, @@ -481,7 +443,7 @@ function createMenu(config) { createMenu(menuConfig); ``` -**Good:** +**Bene:** ```javascript const menuConfig = { title: 'Order', @@ -504,13 +466,14 @@ function createMenu(config) { createMenu(menuConfig); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't use flags as function parameters -Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. +### Non usare valori flag (true/false) come parametri di una funzione -**Bad:** +Un valore di tipo flag indica che la tua funzione può eseguire più di una sola operazione. Una funzione dovrebbe eseguire una sola operazione. Separa la tua funzione se deve eseguire più di una operazione in base al parametro flag che riceve in input. + +**Male:** ```javascript function createFile(name, temp) { if (temp) { @@ -521,7 +484,7 @@ function createFile(name, temp) { } ``` -**Good:** +**Bene:** ```javascript function createFile(name) { fs.create(name); @@ -531,28 +494,24 @@ function createTempFile(name) { createFile(`./temp/${name}`); } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Evitare effetti collaterali (parte 1) -### Avoid Side Effects (part 1) -A function produces a side effect if it does anything other than take a value in -and return another value or values. A side effect could be writing to a file, -modifying some global variable, or accidentally wiring all your money to a -stranger. +Una funzione può generare un effetto collaterale se fa altro oltre a ricevere un valore e restituirne uno o più. L'effetto collaterale potrebbe essere scrivere su un file, modificare una variabile globale o accidentalmente girare tutti i tuoi soldi ad uno sconosciuto. -Now, you do need to have side effects in a program on occasion. Like the previous -example, you might need to write to a file. What you want to do is to -centralize where you are doing this. Don't have several functions and classes -that write to a particular file. Have one service that does it. One and only one. +Probabilmente e occasionalmente avrai bisogno di generare un effetto collaterale: come nell'esempio precedente magari proprio scrivere su un file. +Quello che dovrai fare è centralizzare il punto in cui lo fai. Non avere più funzioni e classi che fanno la stessa cosa, ma averne un servizio ed uno soltanto che se ne occupa. -The main point is to avoid common pitfalls like sharing state between objects -without any structure, using mutable data types that can be written to by anything, -and not centralizing where your side effects occur. If you can do this, you will -be happier than the vast majority of other programmers. +La questione più importante è evitare le insidie che stanno dietro ad errate manipolazioni di oggetti senza alcuna struttura, utilizzando strutture che possono essere modificate da qualunque parte. +Se riuscirai ad evitare che questo accada...sarai ben più felice della maggior parte degli altri programmatori. -**Bad:** +**Male:** ```javascript -// 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. + +// Variable globale utilizzata dalla funzione seguente. +// Nel caso in cui dovessimo utilizzarla in un'altra funzione a questo punto si tratterebbe di un array e potrebbe generare un errore. + let name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { @@ -564,7 +523,7 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**Good:** +**Bene:** ```javascript function splitIntoFirstAndLastName(name) { return name.split(' '); @@ -576,59 +535,41 @@ const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ back to top](#table-of-contents)** - -### Avoid Side Effects (part 2) -In JavaScript, primitives are passed by value and objects/arrays are passed by -reference. In the case of objects and arrays, if your function makes a change -in a shopping cart array, for example, by adding an item to purchase, -then any other function that uses that `cart` array will be affected by this -addition. That may be great, however it can be bad too. Let's imagine a bad -situation: - -The user clicks the "Purchase", button which calls a `purchase` function that -spawns a network request and sends the `cart` array to the server. Because -of a bad network connection, the `purchase` function has to keep retrying the -request. Now, what if in the meantime the user accidentally clicks "Add to Cart" -button on an item they don't actually want before the network request begins? -If that happens and the network request begins, then that purchase function -will send the accidentally added item because it has a reference to a shopping -cart array that the `addItemToCart` function modified by adding an unwanted -item. - -A great solution would be for the `addItemToCart` to always clone the `cart`, -edit it, and return the clone. This ensures that no other functions that are -holding onto a reference of the shopping cart will be affected by any changes. - -Two caveats to mention to this approach: - 1. 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! - - 2. 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](https://facebook.github.io/immutable-js/) 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. - -**Bad:** +**[⬆ torna su](#lista-dei-contenuti)** + +### Evitare effetti collaterali (parte 2) +In Javascript i valori primitivi sono passati come valori, mentre oggetti ed array vengon passati come riferimento. In caso di oggetti ed array, se la tua funzione modifica l'array contenente un carrello della spesa, per esempio aggiungendo o rimuovendo un oggetto da acquistare, tutte le altre funzioni che utilizzano l'array `carrello` saranno condizionati da questa modifica. Queso potrebbe essere un bene ed un male allo stesso tempo. Immaginiamo una situazione in cui questo è un male: + +l'utente clicca sul tasto "Acquista", che richiamerà una funzione `acquista` che effettua una richiesta ed invia l'array `carrello` al server. Per via di una pessima connessione, il metodo `acquista` riproverà ad effettuare la richiesta al server. Cosa succede se nello stesso momento accidentalmemte l'utente clicca su "Aggiungi al carrello" su di un oggetto che non ha intenzione di acquistare prima che venga eseguita nuovamente la funzione? +Verrà inviata la richiesta con il nuovo oggetto accidentalmente aggiunto al carrello utilizzando la funzione `aggiungiOggettoAlCarrello`. + +Un'ottima soluzione è quella di di clonare sempre l'array `carrello`, modificarlo e restituire il clone. +Questi ci assicurerà che che nessun'altra funzione che gestisce il carrello subirà cambiamenti non voluti. + +Due precisazioni vanno fatte su questo approccio: + +1. Potrebbe essere che tu voglia realmente modificare l'oggetto in input, ma vedrai che utilizzando questo approccio ti accorgerai che le questi casi sono veramente rari. La maggior parte delle volte dovrai utilizzare questo approccio per non generare effetti collaterali + +2. Clonare oggetti molto grandi potrebbe essere davvero dispendioso in termini di risorse. Fortunatamente questo non è un problema perchè esistono [ottime librerie](https://facebook.github.io/immutable-js/) che permettono di utilizzare questo approccio senza dispendio di memoria e più velocemente rispetto al dovelo fare manualmente. + +**Male:** ```javascript const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); }; ``` -**Good:** +**Bene:** ```javascript const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't write to global functions -Polluting globals is a bad practice in JavaScript because you could clash with another +### Don't write to global funzioni +Polluting globals is a Male practice in JavaScript because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to extend JavaScript's native Array method to have a `diff` method that could @@ -636,9 +577,9 @@ show the difference between two arrays? You could write your new function to the `Array.prototype`, but it could clash with another library that tried to do the same thing. What if that other library was just using `diff` to find the difference between the first and last elements of an array? This is why it -would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. +would be much better to just use ES2015/ES6 Classi and simply extend the `Array` global. -**Bad:** +**Male:** ```javascript Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); @@ -646,7 +587,7 @@ Array.prototype.diff = function diff(comparisonArray) { }; ``` -**Good:** +**Bene:** ```javascript class SuperArray extends Array { diff(comparisonArray) { @@ -655,14 +596,14 @@ class SuperArray extends Array { } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Favor functional programming over imperative programming JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can. -**Bad:** +**Male:** ```javascript const programmerOutput = [ { @@ -687,7 +628,7 @@ for (let i = 0; i < programmerOutput.length; i++) { } ``` -**Good:** +**Bene:** ```javascript const programmerOutput = [ { @@ -709,18 +650,18 @@ const totalOutput = programmerOutput .map(output => output.linesOfCode) .reduce((totalLines, lines) => totalLines + lines); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Encapsulate conditionals -**Bad:** +**Male:** ```javascript if (fsm.state === 'fetching' && isEmpty(listNode)) { // ... } ``` -**Good:** +**Bene:** ```javascript function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); @@ -730,11 +671,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Avoid negative conditionals -**Bad:** +**Male:** ```javascript function isDOMNodeNotPresent(node) { // ... @@ -745,7 +686,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**Good:** +**Bene:** ```javascript function isDOMNodePresent(node) { // ... @@ -755,7 +696,7 @@ if (isDOMNodePresent(node)) { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Avoid conditionals This seems like an impossible task. Upon first hearing this, most people say, @@ -763,11 +704,11 @@ This seems like an impossible task. Upon first hearing this, most people say, you can use polymorphism to achieve the same task in many cases. The second question is usually, "well that's great but why would I want to do that?" The answer is a previous clean code concept we learned: a function should only do -one thing. When you have classes and functions that have `if` statements, you +one thing. When you have Classi and funzioni that have `if` statements, you are telling your user that your function does more than one thing. Remember, just do one thing. -**Bad:** +**Male:** ```javascript class Airplane { // ... @@ -784,7 +725,7 @@ class Airplane { } ``` -**Good:** +**Bene:** ```javascript class Airplane { // ... @@ -811,15 +752,15 @@ class Cessna extends Airplane { } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Avoid type-checking (part 1) -JavaScript is untyped, which means your functions can take any type of argument. +JavaScript is untyped, which means your funzioni can take any type of argument. Sometimes you are bitten by this freedom and it becomes tempting to do -type-checking in your functions. There are many ways to avoid having to do this. +type-checking in your funzioni. There are many ways to avoid having to do this. The first thing to consider is consistent APIs. -**Bad:** +**Male:** ```javascript function travelToTexas(vehicle) { if (vehicle instanceof Bicycle) { @@ -830,13 +771,13 @@ function travelToTexas(vehicle) { } ``` -**Good:** +**Bene:** ```javascript function travelToTexas(vehicle) { vehicle.move(this.currentLocation, new Location('texas')); } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Avoid type-checking (part 2) If you are working with basic primitive values like strings and integers, @@ -849,7 +790,7 @@ doesn't make up for the lost readability. Keep your JavaScript clean, write good tests, and have good code reviews. Otherwise, do all of that but with TypeScript (which, like I said, is a great alternative!). -**Bad:** +**Male:** ```javascript function combine(val1, val2) { if (typeof val1 === 'number' && typeof val2 === 'number' || @@ -861,13 +802,13 @@ function combine(val1, val2) { } ``` -**Good:** +**Bene:** ```javascript function combine(val1, val2) { return val1 + val2; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Don't over-optimize Modern browsers do a lot of optimization under-the-hood at runtime. A lot of @@ -876,7 +817,7 @@ resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) for seeing where optimization is lacking. Target those in the meantime, until they are fixed if they can be. -**Bad:** +**Male:** ```javascript // On old browsers, each iteration with uncached `list.length` would be costly @@ -886,20 +827,20 @@ for (let i = 0, len = list.length; i < len; i++) { } ``` -**Good:** +**Bene:** ```javascript for (let i = 0; i < list.length; i++) { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Remove dead code -Dead code is just as bad as duplicate code. There's no reason to keep it in +Dead code is just as Male as duplicate code. There's no reason to keep it in your codebase. If it's not being called, get rid of it! It will still be safe in your version history if you still need it. -**Bad:** +**Male:** ```javascript function oldRequestModule(url) { // ... @@ -914,7 +855,7 @@ inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**Good:** +**Bene:** ```javascript function newRequestModule(url) { // ... @@ -923,9 +864,9 @@ function newRequestModule(url) { const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Objects and Data Structures** +## **Ogetti e strutture dati** ### Use getters and setters Using getters and setters to access data on objects could be better than simply looking for a property on an object. "Why?" you might ask. Well, here's an @@ -940,7 +881,7 @@ to look up and change every accessor in your codebase. server. -**Bad:** +**Male:** ```javascript function makeBankAccount() { // ... @@ -955,7 +896,7 @@ const account = makeBankAccount(); account.balance = 100; ``` -**Good:** +**Bene:** ```javascript function makeBankAccount() { // this one is private @@ -982,13 +923,13 @@ function makeBankAccount() { const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Make objects have private members This can be accomplished through closures (for ES5 and below). -**Bad:** +**Male:** ```javascript const Employee = function(name) { @@ -1005,7 +946,7 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**Good:** +**Bene:** ```javascript function makeEmployee(name) { return { @@ -1020,17 +961,17 @@ console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Classes** -### Prefer ES2015/ES6 classes over ES5 plain functions +## **Classi** +### Prefer ES2015/ES6 Classi over ES5 plain funzioni It's very difficult to get readable class inheritance, construction, and method -definitions for classical ES5 classes. If you need inheritance (and be aware -that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over -classes until you find yourself needing larger and more complex objects. +definitions for classical ES5 Classi. If you need inheritance (and be aware +that you might not), then prefer ES2015/ES6 Classi. However, prefer small funzioni over +Classi until you find yourself needing larger and more complex objects. -**Bad:** +**Male:** ```javascript const Animal = function(age) { if (!(this instanceof Animal)) { @@ -1069,7 +1010,7 @@ Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ``` -**Good:** +**Bene:** ```javascript class Animal { constructor(age) { @@ -1097,17 +1038,17 @@ class Human extends Mammal { speak() { /* ... */ } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Use method chaining This pattern is very useful in JavaScript and you see it in many libraries such as jQuery and Lodash. It allows your code to be expressive, and less verbose. For that reason, I say, use method chaining and take a look at how clean your code -will be. In your class functions, simply return `this` at the end of every function, +will be. In your class funzioni, simply return `this` at the end of every function, and you can chain further class methods onto it. -**Bad:** +**Male:** ```javascript class Car { constructor(make, model, color) { @@ -1138,7 +1079,7 @@ car.setColor('pink'); car.save(); ``` -**Good:** +**Bene:** ```javascript class Car { constructor(make, model, color) { @@ -1176,7 +1117,7 @@ const car = new Car('Ford','F-150','red') .setColor('pink') .save(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Prefer composition over inheritance As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, @@ -1192,11 +1133,11 @@ makes more sense than composition: 1. Your inheritance represents an "is-a" relationship and not a "has-a" relationship (Human->Animal vs. User->UserDetails). -2. You can reuse code from the base classes (Humans can move like all animals). -3. You want to make global changes to derived classes by changing a base class. +2. You can reuse code from the base Classi (Humans can move like all animals). +3. You want to make global changes to derived Classi by changing a base class. (Change the caloric expenditure of all animals when they move). -**Bad:** +**Male:** ```javascript class Employee { constructor(name, email) { @@ -1207,7 +1148,7 @@ class Employee { // ... } -// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee +// Male because Employees "have" tax data. EmployeeTaxData is not a type of Employee class EmployeeTaxData extends Employee { constructor(ssn, salary) { super(); @@ -1219,7 +1160,7 @@ class EmployeeTaxData extends Employee { } ``` -**Good:** +**Bene:** ```javascript class EmployeeTaxData { constructor(ssn, salary) { @@ -1242,7 +1183,7 @@ class Employee { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ## **SOLID** ### Single Responsibility Principle (SRP) @@ -1255,7 +1196,7 @@ It's important because if too much functionality is in one class and you modify a piece of it, it can be difficult to understand how that will affect other dependent modules in your codebase. -**Bad:** +**Male:** ```javascript class UserSettings { constructor(user) { @@ -1274,7 +1215,7 @@ class UserSettings { } ``` -**Good:** +**Bene:** ```javascript class UserAuth { constructor(user) { @@ -1300,15 +1241,15 @@ class UserSettings { } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Open/Closed Principle (OCP) -As stated by Bertrand Meyer, "software entities (classes, modules, functions, +As stated by Bertrand Meyer, "software entities (Classi, modules, funzioni, etc.) should be open for extension, but closed for modification." What does that mean though? This principle basically states that you should allow users to add new functionalities without changing existing code. -**Bad:** +**Male:** ```javascript class AjaxAdapter extends Adapter { constructor() { @@ -1351,7 +1292,7 @@ function makeHttpCall(url) { } ``` -**Good:** +**Bene:** ```javascript class AjaxAdapter extends Adapter { constructor() { @@ -1387,7 +1328,7 @@ class HttpRequester { } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Liskov Substitution Principle (LSP) This is a scary term for a very simple concept. It's formally defined as "If S @@ -1403,7 +1344,7 @@ classic Square-Rectangle example. Mathematically, a square is a rectangle, but if you model it using the "is-a" relationship via inheritance, you quickly get into trouble. -**Bad:** +**Male:** ```javascript class Rectangle { constructor() { @@ -1448,7 +1389,7 @@ function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. + const area = rectangle.getArea(); // Male: Returns 25 for Square. Should be 20. rectangle.render(area); }); } @@ -1457,7 +1398,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**Good:** +**Bene:** ```javascript class Shape { setColor(color) { @@ -1502,7 +1443,7 @@ function renderLargeShapes(shapes) { const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Interface Segregation Principle (ISP) JavaScript doesn't have interfaces so this principle doesn't apply as strictly @@ -1514,12 +1455,12 @@ 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 +Classi 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". -**Bad:** +**Male:** ```javascript class DOMTraverser { constructor(settings) { @@ -1545,7 +1486,7 @@ const $ = new DOMTraverser({ ``` -**Good:** +**Bene:** ```javascript class DOMTraverser { constructor(settings) { @@ -1577,7 +1518,7 @@ const $ = new DOMTraverser({ } }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Dependency Inversion Principle (DIP) This principle states two essential things: @@ -1591,7 +1532,7 @@ 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 +the coupling between modules. Coupling is a very Male development pattern because it makes your code hard to refactor. As stated previously, JavaScript doesn't have interfaces so the abstractions @@ -1600,7 +1541,7 @@ 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. -**Bad:** +**Male:** ```javascript class InventoryRequester { constructor() { @@ -1616,7 +1557,7 @@ class InventoryTracker { constructor(items) { this.items = items; - // BAD: We have created a dependency on a specific request implementation. + // Male: 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(); } @@ -1632,7 +1573,7 @@ const inventoryTracker = new InventoryTracker(['apples', 'bananas']); inventoryTracker.requestItems(); ``` -**Good:** +**Bene:** ```javascript class InventoryTracker { constructor(items, requester) { @@ -1672,18 +1613,18 @@ class InventoryRequesterV2 { const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); inventoryTracker.requestItems(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Testing** -Testing is more important than shipping. If you have no tests or an +## **Test** +Test 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 +in addition to having a great Test framework, you also need to use a [good coverage tool](http://gotwarlost.github.io/istanbul/). -There's no excuse to not write tests. There are [plenty of good JS test frameworks](http://jstherightway.org/#testing-tools), so find one that your team prefers. +There's no excuse to not write tests. There are [plenty of good JS test frameworks](http://jstherightway.org/#Test-tools), 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 @@ -1692,7 +1633,7 @@ or refactoring an existing one. ### Single concept per test -**Bad:** +**Male:** ```javascript import assert from 'assert'; @@ -1715,7 +1656,7 @@ describe('MakeMomentJSGreatAgain', () => { }); ``` -**Good:** +**Bene:** ```javascript import assert from 'assert'; @@ -1739,14 +1680,14 @@ describe('MakeMomentJSGreatAgain', () => { }); }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ## **Concurrency** ### Use Promises, not callbacks Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, Promises are a built-in global type. Use them! -**Bad:** +**Male:** ```javascript import { get } from 'request'; import { writeFile } from 'fs'; @@ -1767,7 +1708,7 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) ``` -**Good:** +**Bene:** ```javascript import { get } from 'request'; import { writeFile } from 'fs'; @@ -1784,16 +1725,16 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Async/Await are even cleaner than Promises Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await which offer an even cleaner solution. All you need is a function that is prefixed in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features +a `then` chain of funzioni. Use this if you can take advantage of ES2017/ES8 features today! -**Bad:** +**Male:** ```javascript import { get } from 'request-promise'; import { writeFile } from 'fs-promise'; @@ -1811,7 +1752,7 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') ``` -**Good:** +**Bene:** ```javascript import { get } from 'request-promise'; import { writeFile } from 'fs-promise'; @@ -1826,7 +1767,7 @@ async function getCleanCodeArticle() { } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ## **Error Handling** @@ -1843,7 +1784,7 @@ to the console. If you wrap any bit of code in a `try/catch` it means you think an error may occur there and therefore you should have a plan, or create a code path, for when it occurs. -**Bad:** +**Male:** ```javascript try { functionThatMightThrow(); @@ -1852,7 +1793,7 @@ try { } ``` -**Good:** +**Bene:** ```javascript try { functionThatMightThrow(); @@ -1871,7 +1812,7 @@ try { For the same reason you shouldn't ignore caught errors from `try/catch`. -**Bad:** +**Male:** ```javascript getdata() .then((data) => { @@ -1882,7 +1823,7 @@ getdata() }); ``` -**Good:** +**Bene:** ```javascript getdata() .then((data) => { @@ -1899,7 +1840,7 @@ getdata() }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ## **Formatting** @@ -1913,11 +1854,11 @@ For things that don't fall under the purview of automatic formatting for some guidance. ### Use consistent capitalization -JavaScript is untyped, so capitalization tells you a lot about your variables, -functions, etc. These rules are subjective, so your team can choose whatever +JavaScript is untyped, so capitalization tells you a lot about your Variabili, +funzioni, etc. These rules are subjective, so your team can choose whatever they want. The point is, no matter what you all choose, just be consistent. -**Bad:** +**Male:** ```javascript const DAYS_IN_WEEK = 7; const daysInMonth = 30; @@ -1932,7 +1873,7 @@ class animal {} class Alpaca {} ``` -**Good:** +**Bene:** ```javascript const DAYS_IN_WEEK = 7; const DAYS_IN_MONTH = 30; @@ -1946,15 +1887,15 @@ function restoreDatabase() {} class Animal {} class Alpaca {} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Function callers and callees should be close -If a function calls another, keep those functions vertically close in the source +If a function calls another, keep those funzioni vertically close in the source file. Ideally, keep the caller right above the callee. We tend to read code from top-to-bottom, like a newspaper. Because of this, make your code read that way. -**Bad:** +**Male:** ```javascript class PerformanceReview { constructor(employee) { @@ -1993,7 +1934,7 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**Good:** +**Bene:** ```javascript class PerformanceReview { constructor(employee) { @@ -2032,13 +1973,13 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ## **Comments** ### Only comment things that have business logic complexity. Comments are an apology, not a requirement. Good code *mostly* documents itself. -**Bad:** +**Male:** ```javascript function hashIt(data) { // The hash @@ -2059,7 +2000,7 @@ function hashIt(data) { } ``` -**Good:** +**Bene:** ```javascript function hashIt(data) { @@ -2076,12 +2017,12 @@ function hashIt(data) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Don't leave commented out code in your codebase Version control exists for a reason. Leave old code in your history. -**Bad:** +**Male:** ```javascript doStuff(); // doOtherStuff(); @@ -2089,17 +2030,17 @@ doStuff(); // doSoMuchStuff(); ``` -**Good:** +**Bene:** ```javascript doStuff(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Don't have journal comments Remember, use version control! There's no need for dead code, commented code, and especially journal comments. Use `git log` to get history! -**Bad:** +**Male:** ```javascript /** * 2016-12-20: Removed monads, didn't understand them (RM) @@ -2112,19 +2053,19 @@ function combine(a, b) { } ``` -**Good:** +**Bene:** ```javascript function combine(a, b) { return a + b; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ### Avoid positional markers -They usually just add noise. Let the functions and variable names along with the +They usually just add noise. Let the funzioni and variable names along with the proper indentation and formatting give the visual structure to your code. -**Bad:** +**Male:** ```javascript //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation @@ -2142,7 +2083,7 @@ const actions = function() { }; ``` -**Good:** +**Bene:** ```javascript $scope.model = { menu: 'foo', @@ -2153,7 +2094,7 @@ const actions = function() { // ... }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** ## Translation @@ -2175,4 +2116,4 @@ This is also available in other languages: - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** From a2cd63a041fa836089761fa64074e7430c654904 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Thu, 11 Oct 2018 07:56:58 +0200 Subject: [PATCH 021/170] Improve some translation --- README.md | 100 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 9789e9ef..f6091560 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,14 @@ Principi di Ingegneria del Software, dal libro di Robert C. Martin [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adattati a JavaScript. Non si tratta di una guida stilistica, bensì una guida per cercare di produrre software +adattati a JavaScript. + +Non si tratta di una guida stilistica, bensì una guida per cercare di produrre software [leggibile, riutilizzabile e rifattorizzabile](https://github.com/ryanmcdermott/3rs-of-software-architecture) in JavaScript. -Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code* +Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code*. -Il nostro lavoro come ingegnieri del software ha solo 50 anni e stiamo ancora apprendendo molto. Quando l'architettura del software sarà antica come l'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. +Il nostro lavoro come ingegneri del software ha solo 50 anni e stiamo ancora cercando di apprendere molto. Quando l'architettura del software sarà antica come l'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. Un ultima cosa: conoscere queste regole non farà di te immediatamente uno sviluppatore di software migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. Ogni singola parte di codice parte come bozza, prima, per per poi prendere forma come una scultura di argilla. @@ -33,7 +35,7 @@ Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice ## **Variabili** ### Utilizza nomi di variabili comprensibili e pronunciabili -**Male:** +**Da evitare** ```javascript const yyyymmdstr = moment().format('YYYY/MM/DD'); ``` @@ -46,7 +48,7 @@ const currentDate = moment().format('YYYY/MM/DD'); ### Usa lo stesso lessico per lo stesso tipo di variabili -**Male:** +**Da evitare** ```javascript getUserInfo(); getClientData(); @@ -65,7 +67,7 @@ Fai in modo che i nomi delle tue variabili siano ricercabili. Strumenti come [buddy.js](https://github.com/danielstjules/buddy.js) e [ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare costanti non rinominate. -**Male:** +**Da evitare** ```javascript // Cosa caspita significa 86400000? setTimeout(blastOff, 86400000); @@ -83,7 +85,7 @@ setTimeout(blastOff, MILLISECONDS_IN_A_DAY); **[⬆ torna su](#lista-dei-contenuti)** ### Utilizza nomi di variabili esplicartivi -**Male:** +**Da evitare** ```javascript const address = 'One Infinite Loop, Cupertino 95014'; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; @@ -102,7 +104,7 @@ saveCityZipCode(city, zipCode); ### Evita mappe mentali Essere espliciti è meglio che non esserlo. -**Male:** +**Da evitare** ```javascript const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { @@ -134,7 +136,7 @@ locations.forEach((location) => { Se il nome della tua classe/oggetto ti indica a cosa fa riferimento, non ripeterlo nei nomi delle sue proprietà o funzioni. -**Male:** +**Da evitare** ```javascript const Car = { carMake: 'Honda', @@ -167,7 +169,7 @@ nonI valori di default, generalmente sono più chiari dei cortocircuiti. Tieni p Tutti gli altri valori "falsi" come `''`, `""`, `false`, `null`, `0`, e `NaN`, non saranno sostituiti da un valore predefinito. -**Male:** +**Da evitare** ```javascript function createMicrobrewery(name) { const breweryName = name || 'Hipster Brew Co.'; @@ -205,7 +207,7 @@ To make it obvious what properties the function expects, you can use the ES2015/ 3. Un Linter può avvertirti che non stai utilizzando alcune delle proprietà del tuo oggetto, non utilizzando la sintassi destrutturata non sarebbe possibile -**Male:** +**Da evitare** ```javascript function createMenu(title, body, buttonText, cancellable) { // ... @@ -232,7 +234,7 @@ createMenu({ Questa è di sicuro la regola più importante nell'ingegneria del software. Quando un metodo si occupa di più di un solo aspetto sarà più difficile da testare, comporre e ragioraci sopra. Se è possibile far eseguire al metodo una sola azione sarà più facile da rifattorizzare e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. -**Male:** +**Da evitare** ```javascript function emailClients(clients) { clients.forEach((client) => { @@ -261,7 +263,7 @@ function isActiveClient(client) { ### I nomi delle funzioni dovrebbero farti capire cosa fanno -**Male:** +**Da evitare** ```javascript function addToDate(date, month) { // ... @@ -288,7 +290,7 @@ addMonthToDate(1, date); Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarla ed a testarla più facilmente. -**Male:** +**Da evitare** ```javascript function parseBetterJSAlternative(code) { const REGEXES = [ @@ -362,7 +364,7 @@ Rimuovere questi duplicati, significa creare un'astrazione che permette di gesti Ottenere la sufficiente astrazione può essere complicato. Per questo dovresti seguire i principi SOLID, approfonditi nella sezione *Classi*. Un'astrazione non ottimale potrebbe anche essere peggio del codice duplicato, per cui fai attenzione! Non ripeterti, altrimenti dovrai aggiornare tutte le occorrenze della stessa logica ogni volta che vorrai cambiare qualcosa. -**Male:** +**Da evitare** ```javascript function showDeveloperList(developers) { developers.forEach((developer) => { @@ -424,7 +426,7 @@ function showEmployeeList(employees) { ### Estendi un oggetto con Object.assign -**Male:** +**Da evitare** ```javascript const menuConfig = { title: null, @@ -473,7 +475,7 @@ createMenu(menuConfig); Un valore di tipo flag indica che la tua funzione può eseguire più di una sola operazione. Una funzione dovrebbe eseguire una sola operazione. Separa la tua funzione se deve eseguire più di una operazione in base al parametro flag che riceve in input. -**Male:** +**Da evitare** ```javascript function createFile(name, temp) { if (temp) { @@ -506,7 +508,7 @@ Quello che dovrai fare è centralizzare il punto in cui lo fai. Non avere più f La questione più importante è evitare le insidie che stanno dietro ad errate manipolazioni di oggetti senza alcuna struttura, utilizzando strutture che possono essere modificate da qualunque parte. Se riuscirai ad evitare che questo accada...sarai ben più felice della maggior parte degli altri programmatori. -**Male:** +**Da evitare** ```javascript // Variable globale utilizzata dalla funzione seguente. @@ -552,7 +554,7 @@ Due precisazioni vanno fatte su questo approccio: 2. Clonare oggetti molto grandi potrebbe essere davvero dispendioso in termini di risorse. Fortunatamente questo non è un problema perchè esistono [ottime librerie](https://facebook.github.io/immutable-js/) che permettono di utilizzare questo approccio senza dispendio di memoria e più velocemente rispetto al dovelo fare manualmente. -**Male:** +**Da evitare** ```javascript const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); @@ -579,7 +581,7 @@ to do the same thing. What if that other library was just using `diff` to find the difference between the first and last elements of an array? This is why it would be much better to just use ES2015/ES6 Classi and simply extend the `Array` global. -**Male:** +**Da evitare** ```javascript Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); @@ -603,7 +605,7 @@ JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can. -**Male:** +**Da evitare** ```javascript const programmerOutput = [ { @@ -654,7 +656,7 @@ const totalOutput = programmerOutput ### Encapsulate conditionals -**Male:** +**Da evitare** ```javascript if (fsm.state === 'fetching' && isEmpty(listNode)) { // ... @@ -675,7 +677,7 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { ### Avoid negative conditionals -**Male:** +**Da evitare** ```javascript function isDOMNodeNotPresent(node) { // ... @@ -708,7 +710,7 @@ one thing. When you have Classi and funzioni that have `if` statements, you are telling your user that your function does more than one thing. Remember, just do one thing. -**Male:** +**Da evitare** ```javascript class Airplane { // ... @@ -760,7 +762,7 @@ Sometimes you are bitten by this freedom and it becomes tempting to do type-checking in your funzioni. There are many ways to avoid having to do this. The first thing to consider is consistent APIs. -**Male:** +**Da evitare** ```javascript function travelToTexas(vehicle) { if (vehicle instanceof Bicycle) { @@ -790,7 +792,7 @@ doesn't make up for the lost readability. Keep your JavaScript clean, write good tests, and have good code reviews. Otherwise, do all of that but with TypeScript (which, like I said, is a great alternative!). -**Male:** +**Da evitare** ```javascript function combine(val1, val2) { if (typeof val1 === 'number' && typeof val2 === 'number' || @@ -817,7 +819,7 @@ resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) for seeing where optimization is lacking. Target those in the meantime, until they are fixed if they can be. -**Male:** +**Da evitare** ```javascript // On old browsers, each iteration with uncached `list.length` would be costly @@ -840,7 +842,7 @@ Dead code is just as Male as duplicate code. There's no reason to keep it in your codebase. If it's not being called, get rid of it! It will still be safe in your version history if you still need it. -**Male:** +**Da evitare** ```javascript function oldRequestModule(url) { // ... @@ -881,7 +883,7 @@ to look up and change every accessor in your codebase. server. -**Male:** +**Da evitare** ```javascript function makeBankAccount() { // ... @@ -929,7 +931,7 @@ account.setBalance(100); ### Make objects have private members This can be accomplished through closures (for ES5 and below). -**Male:** +**Da evitare** ```javascript const Employee = function(name) { @@ -971,7 +973,7 @@ definitions for classical ES5 Classi. If you need inheritance (and be aware that you might not), then prefer ES2015/ES6 Classi. However, prefer small funzioni over Classi until you find yourself needing larger and more complex objects. -**Male:** +**Da evitare** ```javascript const Animal = function(age) { if (!(this instanceof Animal)) { @@ -1048,7 +1050,7 @@ For that reason, I say, use method chaining and take a look at how clean your co will be. In your class funzioni, simply return `this` at the end of every function, and you can chain further class methods onto it. -**Male:** +**Da evitare** ```javascript class Car { constructor(make, model, color) { @@ -1137,7 +1139,7 @@ relationship (Human->Animal vs. User->UserDetails). 3. You want to make global changes to derived Classi by changing a base class. (Change the caloric expenditure of all animals when they move). -**Male:** +**Da evitare** ```javascript class Employee { constructor(name, email) { @@ -1196,7 +1198,7 @@ It's important because if too much functionality is in one class and you modify a piece of it, it can be difficult to understand how that will affect other dependent modules in your codebase. -**Male:** +**Da evitare** ```javascript class UserSettings { constructor(user) { @@ -1249,7 +1251,7 @@ etc.) should be open for extension, but closed for modification." What does that mean though? This principle basically states that you should allow users to add new functionalities without changing existing code. -**Male:** +**Da evitare** ```javascript class AjaxAdapter extends Adapter { constructor() { @@ -1344,7 +1346,7 @@ classic Square-Rectangle example. Mathematically, a square is a rectangle, but if you model it using the "is-a" relationship via inheritance, you quickly get into trouble. -**Male:** +**Da evitare** ```javascript class Rectangle { constructor() { @@ -1460,7 +1462,7 @@ 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". -**Male:** +**Da evitare** ```javascript class DOMTraverser { constructor(settings) { @@ -1541,7 +1543,7 @@ 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. -**Male:** +**Da evitare** ```javascript class InventoryRequester { constructor() { @@ -1633,7 +1635,7 @@ or refactoring an existing one. ### Single concept per test -**Male:** +**Da evitare** ```javascript import assert from 'assert'; @@ -1687,7 +1689,7 @@ describe('MakeMomentJSGreatAgain', () => { Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, Promises are a built-in global type. Use them! -**Male:** +**Da evitare** ```javascript import { get } from 'request'; import { writeFile } from 'fs'; @@ -1734,7 +1736,7 @@ in an `async` keyword, and then you can write your logic imperatively without a `then` chain of funzioni. Use this if you can take advantage of ES2017/ES8 features today! -**Male:** +**Da evitare** ```javascript import { get } from 'request-promise'; import { writeFile } from 'fs-promise'; @@ -1784,7 +1786,7 @@ to the console. If you wrap any bit of code in a `try/catch` it means you think an error may occur there and therefore you should have a plan, or create a code path, for when it occurs. -**Male:** +**Da evitare** ```javascript try { functionThatMightThrow(); @@ -1812,7 +1814,7 @@ try { For the same reason you shouldn't ignore caught errors from `try/catch`. -**Male:** +**Da evitare** ```javascript getdata() .then((data) => { @@ -1858,7 +1860,7 @@ JavaScript is untyped, so capitalization tells you a lot about your Variabili, funzioni, etc. These rules are subjective, so your team can choose whatever they want. The point is, no matter what you all choose, just be consistent. -**Male:** +**Da evitare** ```javascript const DAYS_IN_WEEK = 7; const daysInMonth = 30; @@ -1895,7 +1897,7 @@ If a function calls another, keep those funzioni vertically close in the source file. Ideally, keep the caller right above the callee. We tend to read code from top-to-bottom, like a newspaper. Because of this, make your code read that way. -**Male:** +**Da evitare** ```javascript class PerformanceReview { constructor(employee) { @@ -1979,7 +1981,7 @@ review.perfReview(); ### Only comment things that have business logic complexity. Comments are an apology, not a requirement. Good code *mostly* documents itself. -**Male:** +**Da evitare** ```javascript function hashIt(data) { // The hash @@ -2022,7 +2024,7 @@ function hashIt(data) { ### Don't leave commented out code in your codebase Version control exists for a reason. Leave old code in your history. -**Male:** +**Da evitare** ```javascript doStuff(); // doOtherStuff(); @@ -2040,7 +2042,7 @@ doStuff(); Remember, use version control! There's no need for dead code, commented code, and especially journal comments. Use `git log` to get history! -**Male:** +**Da evitare** ```javascript /** * 2016-12-20: Removed monads, didn't understand them (RM) @@ -2065,7 +2067,7 @@ function combine(a, b) { They usually just add noise. Let the funzioni and variable names along with the proper indentation and formatting give the visual structure to your code. -**Male:** +**Da evitare** ```javascript //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation From 2baf082f7b5db98fdc5535c21b67b80f7fc13c35 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Thu, 11 Oct 2018 18:33:32 +0200 Subject: [PATCH 022/170] translation (wip) --- README.md | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f6091560..30b87b53 100644 --- a/README.md +++ b/README.md @@ -571,15 +571,11 @@ const addItemToCart = (cart, item) => { **[⬆ torna su](#lista-dei-contenuti)** ### Don't write to global funzioni -Polluting globals is a Male practice in JavaScript because you could clash with another -library and the user of your API would be none-the-wiser until they get an -exception in production. Let's think about an example: what if you wanted to -extend JavaScript's native Array method to have a `diff` method that could -show the difference between two arrays? You could write your new function -to the `Array.prototype`, but it could clash with another library that tried -to do the same thing. What if that other library was just using `diff` to find -the difference between the first and last elements of an array? This is why it -would be much better to just use ES2015/ES6 Classi and simply extend the `Array` global. +### Non aggiungere funzioni globali + +Contaminare delle variabili globali è una pratica sconsigliata, in quanto potresti entrare in conflitto con altre librerie e l'utilizzatore delle tue API potrebbe non accorgersene fintanto che non si trova in produzione, generando un'eccezione. +Facciamo un esempio pratico: supponiamo che tu voglia estendere il costruttore Array nativo di JavaScript aggiungendo il metodo `diff` che mostra le differenze tra due array. Come puoi fare? +Potresti scrivere il metodo utilizzando `Array.prototype`, che però potrebbe entrare in conflitto con con un'altra libreria che fa la stessa cosa. Cosa succederebbe se anche l'altra libreria utilizzasse `diff` per trovare le differenze tra due array? Ecco perchè è molto meglio utilizzare le classi ES2015/ES6 e semplicemente estendere `Array`. **Da evitare** ```javascript @@ -600,10 +596,15 @@ class SuperArray extends Array { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Favor functional programming over imperative programming -JavaScript isn't a functional language in the way that Haskell is, but it has -a functional flavor to it. Functional languages can be cleaner and easier to test. -Favor this style of programming when you can. +### Preferisci la programmazione funzionale a quella imperativa + +[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale) + +[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa) + +Javascript non è un linguaggio funzionale alla stregua di Haskell, ma entrambi hanno qualcosa che li accomuna. +I linguaggi funzionali generalmente sono più puliti e facili da testare. +Preferisci questo stile se possibile. **Da evitare** ```javascript @@ -654,7 +655,7 @@ const totalOutput = programmerOutput ``` **[⬆ torna su](#lista-dei-contenuti)** -### Encapsulate conditionals +### Incapsula i condizionali **Da evitare** ```javascript @@ -675,7 +676,7 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Avoid negative conditionals +### Evita di verificare condizioni in negativo **Da evitare** ```javascript @@ -700,15 +701,13 @@ if (isDOMNodePresent(node)) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Avoid conditionals -This seems like an impossible task. Upon first hearing this, most people say, -"how am I supposed to do anything without an `if` statement?" The answer is that -you can use polymorphism to achieve the same task in many cases. The second -question is usually, "well that's great but why would I want to do that?" The -answer is a previous clean code concept we learned: a function should only do -one thing. When you have Classi and funzioni that have `if` statements, you -are telling your user that your function does more than one thing. Remember, -just do one thing. +### Evita i condizionali +Sembrerebbe un task impossibile. Ad un rapido sguardo molti sviluppatori potrebbero pensare "come posso pensare di far funzionare qualcosa senza utilizzare un `if`?" +La risposta è che puoi utilizzare il polimorfismo per ottenere lo stesso risultato in molti casi. +La seconda domanda generalmente è "Ottimo! Ma perchè dovrei farlo?". +La risposta è data in uno dei concetti precedentemente descritti: una funzione dovrebbe eseguire una sola operazione. +Quando hai una Classe con delle funzioni che utilizzano lo stato `if` stai dicendo all'utente che la tua funzione può fare più di una operazione. + **Da evitare** ```javascript @@ -756,7 +755,7 @@ class Cessna extends Airplane { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Avoid type-checking (part 1) +### Evita di verificare i tipi (parte 1) JavaScript is untyped, which means your funzioni can take any type of argument. Sometimes you are bitten by this freedom and it becomes tempting to do type-checking in your funzioni. There are many ways to avoid having to do this. From a755162e9df0a55c891b9b17e6236c6dc1e32a0e Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Thu, 11 Oct 2018 21:59:18 +0200 Subject: [PATCH 023/170] translation wip --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 30b87b53..ab446113 100644 --- a/README.md +++ b/README.md @@ -755,11 +755,12 @@ class Cessna extends Airplane { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Evita di verificare i tipi (parte 1) -JavaScript is untyped, which means your funzioni can take any type of argument. -Sometimes you are bitten by this freedom and it becomes tempting to do -type-checking in your funzioni. There are many ways to avoid having to do this. -The first thing to consider is consistent APIs. +### Evita la validazione dei tipi (parte 1) + +JavaScript è un linguaggio non tipizzato, il che significa che le tue funzioni possono accettare qualunque tipo di argomento. +Qualche volta potresti essere tentato da tutta questa libertà e potresti essere altrettanto tentato di veririfcare il tipo di dato ricevuto nella tua funzione. +Ci sono molti modi per evitare di dover fare questo tipo di controllo. +Come prima cosa cerca scrivere API consistenti. **Da evitare** ```javascript @@ -780,7 +781,12 @@ function travelToTexas(vehicle) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Avoid type-checking (part 2) +### Evita la validazione dei tipi (part 2) + +Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di TypeScript. +È una vlidissima alternativa al normale JavaScript che fornicsce una validazione di tipi statica utilizzando la sintassi JavaScript. + + If you are working with basic primitive values like strings and integers, and you can't use polymorphism but you still feel the need to type-check, you should consider using TypeScript. It is an excellent alternative to normal From fa1b8dbdef0708eba093d9f438e2085289c39ec9 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Thu, 11 Oct 2018 22:17:33 +0200 Subject: [PATCH 024/170] translation --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ab446113..3e611972 100644 --- a/README.md +++ b/README.md @@ -598,9 +598,8 @@ class SuperArray extends Array { ### Preferisci la programmazione funzionale a quella imperativa -[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale) - -[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa) +*[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale)* - +*[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa)* Javascript non è un linguaggio funzionale alla stregua di Haskell, ma entrambi hanno qualcosa che li accomuna. I linguaggi funzionali generalmente sono più puliti e facili da testare. From abf7b81b642a1c423f686e5a206cd739f17615d1 Mon Sep 17 00:00:00 2001 From: frappacchio Date: Fri, 12 Oct 2018 07:54:00 +0200 Subject: [PATCH 025/170] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e611972..84d77762 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ function paintCar(car) { ### Utilizza i valori di default (predefiniti), anzichè usare condizioni o cortocircuiti -nonI valori di default, generalmente sono più chiari dei cortocircuiti. Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. +I valori di default, generalmente sono più chiari dei cortocircuiti. Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. Tutti gli altri valori "falsi" come `''`, `""`, `false`, `null`, `0`, e `NaN`, non saranno sostituiti da un valore predefinito. From a08fd6917b18c0b4d34bc8e68cc6c3f03bf49921 Mon Sep 17 00:00:00 2001 From: frappacchio Date: Fri, 12 Oct 2018 07:58:02 +0200 Subject: [PATCH 026/170] Update README.md --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 84d77762..db383708 100644 --- a/README.md +++ b/README.md @@ -192,20 +192,17 @@ function createMicrobrewery(name = 'Hipster Brew Co.') { Limitare il numero di argomenti di una funzione è incredibilmente importante perchè ti permette di testarla più facilmente. Avere più di 3 argomenti può portare ad un'esplosione di combinazioni da testare, che produrranno una lunga serie di casi da verificare. -1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. In alcuni casi, in cui questo non sia del tutto vero, un oggetto può aiutare ad ovviare a questo problema. +1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. In alcuni casi, in cui questo non è del tutto vero, un oggetto può aiutare ad ovviare a questo problema. Dal momento in cui JavaScript permette la creazione di oggetti al volo, senza dover passare attraverso classi specifiche, puoi usare un oggetto se pensi che il tuo metodo richieda molti argomenti. -Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi - -To make it obvious what properties the function expects, you can use the ES2015/ES6 -. This has a few advantages: +Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi: 1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà sono state utilizzate -2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti indesiderati. Nota: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. +2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti collaterali. Nota: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. -3. Un Linter può avvertirti che non stai utilizzando alcune delle proprietà del tuo oggetto, non utilizzando la sintassi destrutturata non sarebbe possibile +3. Un Linter può avvisarti che non stai utilizzando alcune delle proprietà del tuo oggetto, non utilizzando la sintassi destrutturata non sarebbe possibile **Da evitare** ```javascript @@ -286,9 +283,9 @@ addMonthToDate(1, date); ``` **[⬆ torna su](#lista-dei-contenuti)** -### I mteodi dovrebbero avere un solo livello di astrazione +### Le funzioni dovrebbero avere un solo livello di astrazione -Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarla ed a testarla più facilmente. +Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarla e testarla più facilmente. **Da evitare** ```javascript From 7b6f3d658977db32d80e1d431df2275e2c55540b Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 09:09:55 +0200 Subject: [PATCH 027/170] translation --- README.md | 61 ++++++++++++++++++++----------------------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index db383708..0d32fd37 100644 --- a/README.md +++ b/README.md @@ -781,17 +781,8 @@ function travelToTexas(vehicle) { Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di TypeScript. È una vlidissima alternativa al normale JavaScript che fornicsce una validazione di tipi statica utilizzando la sintassi JavaScript. - - -If you are working with basic primitive values like strings and integers, -and you can't use polymorphism but you still feel the need to type-check, -you should consider using TypeScript. It is an excellent alternative to normal -JavaScript, as it provides you with static typing on top of standard JavaScript -syntax. The problem with manually type-checking normal JavaScript is that -doing it well requires so much extra verbiage that the faux "type-safety" you get -doesn't make up for the lost readability. Keep your JavaScript clean, write -good tests, and have good code reviews. Otherwise, do all of that but with -TypeScript (which, like I said, is a great alternative!). +Il problema con la validazione manuale dei tipi utilizzando JavaScript standard è che richiede tanto codice extra che non compensa la scarsa leggibilità del codice ottenuto. +Mantieni pulito il tuo codice, scrivi dei test validi e cerca di fare delle buone revisioni del coidice. Altrimenti utilizza TypeScript (che come detto in precedenza è una validissima alternativa!) **Da evitare** ```javascript @@ -813,18 +804,15 @@ function combine(val1, val2) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Don't over-optimize -Modern browsers do a lot of optimization under-the-hood at runtime. A lot of -times, if you are optimizing then you are just wasting your time. [There are good -resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) -for seeing where optimization is lacking. Target those in the meantime, until -they are fixed if they can be. +### Non ottimizzare eccessivamente +I browser moderni eseguono un sacco di ottimizzazione "sottobanco". Molte volte, quando cerchi di ottimizzare il tuo codice, stai perdendo tempo prezioso. [Ci sono tante guide](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) che aiutano a capire quali tipi di ottimizzazione sono superflui e quali no. Utilizza queste guide nel frattempo, fintanto che queste mancanze non verranno colmate. **Da evitare** ```javascript -// On old browsers, each iteration with uncached `list.length` would be costly -// because of `list.length` recomputation. In modern browsers, this is optimized. +// Nei vecchi browser ogni volta che in un ciclo verifichi `list.length` potrebbe +// essere dispendioso per via del ricalcolo della lunghezza. Nei browser moderni +// questa operazione è stata ottimizzata for (let i = 0, len = list.length; i < len; i++) { // ... } @@ -838,10 +826,9 @@ for (let i = 0; i < list.length; i++) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Remove dead code -Dead code is just as Male as duplicate code. There's no reason to keep it in -your codebase. If it's not being called, get rid of it! It will still be safe -in your version history if you still need it. +### Rimuovi il codice inutilizzato +Il codice inutilizzato è dannoso quanto il codice duplicato. Non c'è motivo per cui tenerlo nella tua codebase. Se realmente non viene utilizzato rimuovilo! +Sarà comunque presente nella storia del tuo file se utilizzi un sistema di versioning nel caso in cui dovesse servirti. **Da evitare** ```javascript @@ -870,18 +857,14 @@ inventoryTracker('apples', req, 'www.inventory-awesome.io'); **[⬆ torna su](#lista-dei-contenuti)** ## **Ogetti e strutture dati** -### Use getters and setters -Using getters and setters to access data on objects could be better than simply -looking for a property on an object. "Why?" you might ask. Well, here's an -unorganized list of reasons why: - -* When you want to do more beyond getting an object property, you don't have -to look up and change every accessor in your codebase. -* Makes adding validation simple when doing a `set`. -* Encapsulates the internal representation. -* Easy to add logging and error handling when getting and setting. -* You can lazy load your object's properties, let's say getting it from a -server. +### Utilizza getter e setter +Utilizzare getter e setter per acceedere ai dati di un oggetto può essere meglio che accedere direttamente alle sue proprietà. Ti starai chiedendo il motivo. +Eccoti qualche motivo per cui utilizzare getter e setter: +* Quando hai bisogno di eseguire un'operazione col dato recuperato, non devi andare a modificarlo ogni volta che accedi al dato nel tuo codice. +* Valida il dato nel momento in cui lo setti con `set` +* Incapsula la rappresentazione interna +* È più facile loggare e gestire gli errori quando imposti o recuperi un dato +* Puoi caricare i dati del tuo oggetto in modalità *lazy*, per esempio caricandoli dal server. **Da evitare** @@ -902,17 +885,17 @@ account.balance = 100; **Bene:** ```javascript function makeBankAccount() { - // this one is private + // questa proprietà è privata let balance = 0; - // a "getter", made public via the returned object below + // un "getter", la rende pubblica restituendola in questo modo function getBalance() { return balance; } - // a "setter", made public via the returned object below + // un "setter", la rende pubblica in questo modo function setBalance(amount) { - // ... validate before updating the balance + // ... e la valida prima di impostarla balance = amount; } From dc12ea6931d215d5680a1ac03fb62c897230e89d Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 10:39:16 +0200 Subject: [PATCH 028/170] translation --- README.md | 105 ++++++++++++++++++++---------------------------------- 1 file changed, 38 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 0d32fd37..aa17f404 100644 --- a/README.md +++ b/README.md @@ -912,8 +912,8 @@ account.setBalance(100); **[⬆ torna su](#lista-dei-contenuti)** -### Make objects have private members -This can be accomplished through closures (for ES5 and below). +### Imposta proprietà private in un oggetto +Può essere fatto attraverso le *closure* (per ES5 e versioni precedenti). **Da evitare** ```javascript @@ -951,11 +951,8 @@ console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ## **Classi** -### Prefer ES2015/ES6 Classi over ES5 plain funzioni -It's very difficult to get readable class inheritance, construction, and method -definitions for classical ES5 Classi. If you need inheritance (and be aware -that you might not), then prefer ES2015/ES6 Classi. However, prefer small funzioni over -Classi until you find yourself needing larger and more complex objects. +### Utilizza le classi ES2015/ES6 piuttosto che le funzioni di ES5 +È molto difficile ottenere leggibilità per ereditarietà, costrutti e metodi in definizioni di oggetti in ES5. Se hai bisogno di ereditarietà (e bada bene, non è detto che tu ne abbia bisogno), utilizza il costrutto Class di ES2015/ES6. Altrimenti utilizza piccole funzioni fintanto che non avrai bisogno di gestire oggetti più complessi. **Da evitare** ```javascript @@ -1027,12 +1024,10 @@ class Human extends Mammal { **[⬆ torna su](#lista-dei-contenuti)** -### Use method chaining -This pattern is very useful in JavaScript and you see it in many libraries such -as jQuery and Lodash. It allows your code to be expressive, and less verbose. -For that reason, I say, use method chaining and take a look at how clean your code -will be. In your class funzioni, simply return `this` at the end of every function, -and you can chain further class methods onto it. +### Concatena i metodi +Questo pattern è molto utilizzato in JavaScript e puoi trovarne applicazione in molte liberie come jQuery e Loadash. Permette al tuo codice di essere maggiormente espressivo e meno verboso. +Proprio per questo motivo, insisto, utilizza la concatenazione dei metodi e guarda come può essere più pulito il tuo codice. Nei metodi della tua classe, semplicemente restituisci il riferimento `this` alla fine di ogni metodo, in modo da poter concatenare altri metodi. + **Da evitare** ```javascript @@ -1076,25 +1071,25 @@ class Car { setMake(make) { this.make = make; - // NOTE: Returning this for chaining + // NOTA: restituisci this per poter concatenare altri metodi. return this; } setModel(model) { this.model = model; - // NOTE: Returning this for chaining + // NOTA: restituisci this per poter concatenare altri metodi. return this; } setColor(color) { this.color = color; - // NOTE: Returning this for chaining + // NOTA: restituisci this per poter concatenare altri metodi. return this; } save() { console.log(this.make, this.model, this.color); - // NOTE: Returning this for chaining + // NOTA: restituisci this per poter concatenare altri metodi. return this; } } @@ -1105,23 +1100,14 @@ const car = new Car('Ford','F-150','red') ``` **[⬆ torna su](#lista-dei-contenuti)** -### Prefer composition over inheritance -As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, -you should prefer composition over inheritance where you can. There are lots of -good reasons to use inheritance and lots of good reasons to use composition. -The main point for this maxim is that if your mind instinctively goes for -inheritance, try to think if composition could model your problem better. In some -cases it can. - -You might be wondering then, "when should I use inheritance?" It -depends on your problem at hand, but this is a decent list of when inheritance -makes more sense than composition: +### Preferisci la composizione all'ereditarietà +Come dichiarato dalla Gang of four in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) dovresti preferire la composizione all'ereditarietà quando puoi. Ci sono validi motivi per utilizzare l'ereditarietà e altrettanto validi motivi per utilizzare la composizione. +Il punto principale di questo assunto è che mentalmente sei portato a preferire l'ereditarietà; prova a pensare alla composizione per risolvere il tuo problema, tante volte è davvero la soluzione migliore. +Ti potresti chiedere: "Quando dovrei utilizzare l'ereditarietà?". Dipende dal problema che devi affrontare, ma c'è una discreta lista di suggerimenti che ti potrebbero aiutare a capire quando l'ereditarietà è meglio della composizione: -1. Your inheritance represents an "is-a" relationship and not a "has-a" -relationship (Human->Animal vs. User->UserDetails). -2. You can reuse code from the base Classi (Humans can move like all animals). -3. You want to make global changes to derived Classi by changing a base class. -(Change the caloric expenditure of all animals when they move). +1. L'estensione che stai mettendo in atto rappresenta una relazione di tipo "è di questo tipo" e non "ha questa proprietà" (Umano->Animale vs. Utente->DettagliUtente). +2. Puoi riutilizzare il codice dalla classe padre (Umano) +3. Vuoi fare cambiamenti globali a tutte le classi estese tramite la classe di partenza. **Da evitare** ```javascript @@ -1172,15 +1158,11 @@ class Employee { **[⬆ torna su](#lista-dei-contenuti)** ## **SOLID** -### Single Responsibility Principle (SRP) -As stated in Clean Code, "There should never be more than one reason for a class -to change". It's tempting to jam-pack a class with a lot of functionality, like -when you can only take one suitcase on your flight. The issue with this is -that your class won't be conceptually cohesive and it will give it many reasons -to change. Minimizing the amount of times you need to change a class is important. -It's important because if too much functionality is in one class and you modify -a piece of it, it can be difficult to understand how that will affect other -dependent modules in your codebase. +### Single Responsibility Principle (SRP) (Principio della singola responsabilità) +Come indicato in *Clean code*, "Non dovrebbe mai esserci più di un solo motivo per modificare una classe". La tentazione è sempre quella di fare un'unica classe con molte funzionalità, come quando vuoi portarti un unico bagaglio sul volo. +Il problema con questo approccio è che le tue classi non saranno concettualmente coese e ti potrebbero dare più di una ragione per modificarle in seguito. +Minimizzare il numero di volte in cui modificare una classe è importante. +È importante perchè quando ci sono troppe funzionalità in una classe è difficile capire che effetto avrà sulle classe che la estendono, nel caso in cui farai un cambiamento. **Da evitare** ```javascript @@ -1229,11 +1211,9 @@ class UserSettings { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Open/Closed Principle (OCP) -As stated by Bertrand Meyer, "software entities (Classi, modules, funzioni, -etc.) should be open for extension, but closed for modification." What does that -mean though? This principle basically states that you should allow users to -add new functionalities without changing existing code. +### Open/Closed Principle (OCP) (Principio di apertura/chiusura) +Come dichiarato da Bertrand Meyer, "Le entità di un software (Classi, moduli, funzioni, etc.) dovrebbero essere aperte all'estensione, ma chiuse alla modifica. Cosa significa esattamente? +Quello che intende è che dovresti dare ai tuoi utilizzatori la possibilità di aggiungere nuove funzionalità, non modificando quelle esistenti. **Da evitare** ```javascript @@ -1316,19 +1296,11 @@ class HttpRequester { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Liskov Substitution Principle (LSP) -This is a scary term for a very simple concept. It's formally defined as "If S -is a subtype of T, then objects of type T may be replaced with objects of type S -(i.e., objects of type S may substitute objects of type T) without altering any -of the desirable properties of that program (correctness, task performed, -etc.)." That's an even scarier definition. - -The best explanation for this is if you have a parent class and a child class, -then the base class and child class can be used interchangeably without getting -incorrect results. This might still be confusing, so let's take a look at the -classic Square-Rectangle example. Mathematically, a square is a rectangle, but -if you model it using the "is-a" relationship via inheritance, you quickly -get into trouble. +### Liskov Substitution Principle (LSP) (Principio dell'intercambiabilità di Liskov) +Questo nome sembra molto più spaventoso di quello che in realtà significa. +Formalmente la sua defnizione è "Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S () senza modificare alcuna della proprietà del software (correttezza, compito da svolgere, etc.)". Questa definizione suona ovviamente come più complessa. +Forse una spiegazione più esaustiva potrebbe essere: "Se hai una classe *figlio* che estende una classe *genitore* allora le due classi possono essere intercambiate all'interno del codice senza generare errori o risultati inattesi". +Potrebbe esssere ancora poco chiaro, ma vediamo con un esempio (Quadrato/Rettangolo): matematicamente il Quadrato è un Rettangolo, ma se il tuo modello usa una relazione di tipo "è-un" per eredità, potresti avere presto qualche problema. **Da evitare** ```javascript @@ -1431,14 +1403,13 @@ renderLargeShapes(shapes); ``` **[⬆ torna su](#lista-dei-contenuti)** -### Interface Segregation Principle (ISP) -JavaScript doesn't have interfaces so this principle doesn't apply as strictly -as others. However, it's important and relevant even with JavaScript's lack of -type system. +### Interface Segregation Principle (ISP) (Principio della segregazione delle interfacce) + +JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante anche per via della sua mancanza di tipizzazione. + +ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [duck-typing](https://it.wikipedia.org/wiki/Duck_typing) + -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 Classi that require large settings objects. Not requiring clients to setup From 064ad71e5290af6f05e2f0b3859a72b567ea1a80 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 11:14:03 +0200 Subject: [PATCH 029/170] translations --- README.md | 54 +++++++++++++++++++++--------------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index aa17f404..bc7013ea 100644 --- a/README.md +++ b/README.md @@ -1407,16 +1407,12 @@ renderLargeShapes(shapes); JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante anche per via della sua mancanza di tipizzazione. -ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [duck-typing](https://it.wikipedia.org/wiki/Duck_typing) +ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [*duck-typing*](https://it.wikipedia.org/wiki/Duck_typing) +Un buon esempio in JavaScript potrebbe essere fatto per le classi che richiedono la deinizione di un set di proprietà molto grande. +Non utilizzare classi che richiedono la definizione di molte proprietà per essere istanziate è sicuramente un beneficio perchè spesso non tutte queste proprietà richiedono di essere impostate per utilizzare la classe. +Rendere questi parametri opzionali evita di avere un'interfaccia pesante. - -A good example to look at that demonstrates this principle in JavaScript is for -Classi 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". - **Da evitare** ```javascript class DOMTraverser { @@ -1437,7 +1433,7 @@ class DOMTraverser { const $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), - animationModule() {} // Most of the time, we won't need to animate when traversing. + animationModule() {} //Il più delle volte potremmo non dover animare questo oggetto // ... }); @@ -1477,26 +1473,16 @@ const $ = new DOMTraverser({ ``` **[⬆ torna su](#lista-dei-contenuti)** -### Dependency Inversion Principle (DIP) -This principle states two essential things: -1. High-level modules should not depend on low-level modules. Both should -depend on abstractions. -2. 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 Male 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. +### Dependency Inversion Principle (DIP) (Principio di inversione delle dipendenze) +Questo principio sostanzialmente indica due cose: +1. Moduli ad alto livello non dovrebbero necessariamente dipendere da moduli di basso livello +2. L'astrazione non dovrebbe dipendere dai dettagli. I dettagli non dovrebbero dipendere dall'astrazione. + +A primo impatto questo concetto potrebbe essere difficile da capire, ma nel caso in cui tu abbia lavorato con AngularJS avrai sicuramente visto l'applicazione di questo principio nel concetto di *Dependency injection (DI)*. Nonostante non sia esattamente identico come concetto, DIP evita che i moduli di alto livello conoscano i dettagli dei moduli di basso livello pur utilizzandoli. +Uno dei benefici di questo utilizzo è che riduce la dipendenza tra due moduli. +La dipendenza tra due moduli è un concetto negativo, perchè ne rende difficile il refactor. +Come detto in precedenza, non essendoci il concetto di interfaccia in JavaScript, tutte le dipendenze sono contratte implicitamente. +Nell'esempio successivo, la dipendenza implicita è che tutte le istanze di `InventoryTracker` avranno un metodo `requestItems`. **Da evitare** ```javascript @@ -1514,8 +1500,9 @@ class InventoryTracker { constructor(items) { this.items = items; - // Male: We have created a dependency on a specific request implementation. - // We should just have requestItems depend on a request method: `request` + //Da evitare: abbiamo creato una dipendenza specifica per ogni istanza. + //Dovremmo fare in modo che requestItems dipenda dal metodo `request` + this.requester = new InventoryRequester(); } @@ -1565,8 +1552,9 @@ class InventoryRequesterV2 { } } -// By constructing our dependencies externally and injecting them, we can easily -// substitute our request module for a fancy new one that uses WebSockets. +//Avendo dichiarato la nostra dipendenza esternamente ed aggiunta dall'esterno, la possiamo +//sostituire facilemente con un'altra che utilizza WebSockets + const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); inventoryTracker.requestItems(); ``` From 9cf38fabc2a6bd1862654b83ca3889a2d01879e8 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 11:37:09 +0200 Subject: [PATCH 030/170] translation --- README.md | 80 +++++++++++++++++++++---------------------------------- 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index bc7013ea..450b260e 100644 --- a/README.md +++ b/README.md @@ -1561,22 +1561,15 @@ inventoryTracker.requestItems(); **[⬆ torna su](#lista-dei-contenuti)** ## **Test** -Test 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 Test framework, you also need to use a -[good coverage tool](http://gotwarlost.github.io/istanbul/). - -There's no excuse to not write tests. There are [plenty of good JS test frameworks](http://jstherightway.org/#Test-tools), so find one that your team prefers. -When you find one that works for your team, then aim to always write tests -for every new feature/module you introduce. If your preferred method is -Test Driven Development (TDD), that is great, but the main point is to just -make sure you are reaching your coverage goals before launching any feature, -or refactoring an existing one. - -### Single concept per test +Testare è più importante che rilasciare. Se non hai test o non ne hai un numero adeguato, non saprai se ad ogni rilascio puoi rompere qualcosa. +Decidere quanti siano il numero sufficiente di test dipende dal tuo team, ma cercare di coprire il 100% dei casi (per tutti gli stati ed i branch) vuol dire avere massima tranquillità durante i rilasci. +Questo significa che oltre ad utilizzare una suite di test valida, dovresti utilizzare anche un [buon strumento di copertura](http://gotwarlost.github.io/istanbul/). + +Non ci sono scuse per non scrivere test. C'è un'abbondanza di ottimi [framewerk per i test in JavaScript](http://jstherightway.org/#Test-tools) quindi cerca quello più adatto alle tue esigenze. +Quando tu ed il tuo team avrete individuato quello più giusto per voi, dovrete iniziare sempre a scrivere i test per ogni modulo/feature che introdurrete nel vostro software. +Se il vostro approccio preferito è quello del TestDrivenDevelopment (TDD) è ottimo, ma assicurati di individuare i tuoi obiettivi prima di rilasciare ogni singola feature o eseguire il refactor di una esistente. + +### Un singolo comportamento per test **Da evitare** ```javascript @@ -1627,10 +1620,9 @@ describe('MakeMomentJSGreatAgain', () => { ``` **[⬆ torna su](#lista-dei-contenuti)** -## **Concurrency** -### Use Promises, not callbacks -Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, -Promises are a built-in global type. Use them! +## **Consequenzialità** +### utilizza le Promise, non i callback +Le funzioni di callback non sono sempre chiare e possono generare un eccessivo numero di nidificazioni. Con ES2015/ES6 sono nativamente e globalmente accessibili. Utilizzale! **Da evitare** ```javascript @@ -1672,12 +1664,10 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') ``` **[⬆ torna su](#lista-dei-contenuti)** -### Async/Await are even cleaner than Promises -Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await -which offer an even cleaner solution. All you need is a function that is prefixed -in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of funzioni. Use this if you can take advantage of ES2017/ES8 features -today! +### Async/Await sono anche più chiari delle Promise +Le Promise sono una valida e chiara alternativa alle funzioni di callback, ma ES2017/ES8 offre anche async and await che possono essere addirittura una soluzione più chiara. +Tutto quello che devi fare non è niente altro che scrivere una funzione che abbia prefisso `async` e puoi scrivere la tua logica senza dover concatenare con la kyword `then`. +Utilizza questo approccio se hai la possibilità di utilizzare le feature ES2017/ES8! **Da evitare** ```javascript @@ -1715,19 +1705,12 @@ async function getCleanCodeArticle() { **[⬆ torna su](#lista-dei-contenuti)** -## **Error Handling** -Thrown errors are a good thing! They mean the runtime has successfully -identified when something in your program has gone wrong and it's letting -you know by stopping function execution on the current stack, killing the -process (in Node), and notifying you in the console with a stack trace. +## **Gestione degli errori** +Generare errori è una buona cosa. Vuol dire che l'esecuzione del tuo codice ha identificato precisamente quando nel tuo software qualcosa è andato storto e ti permette di interromperne l'esecuzione nello stack corrente terminando il processo (in Node), e notificandolo attraverso la console. -### Don't ignore caught errors -Doing nothing with a caught error doesn't give you the ability to ever fix -or react to said error. Logging the error to the console (`console.log`) -isn't much better as often times it can get lost in a sea of things printed -to the console. If you wrap any bit of code in a `try/catch` it means you -think an error may occur there and therefore you should have a plan, -or create a code path, for when it occurs. +### Non ingnorare gli errori intercettati +Non fare niente con gli errori intercettati non ti da l'abilità di fixare o reagire a questi errori. Loggare gli errori nella console (`console.log`) non ti assicura di non perderti nel mare di log stampati in console. +Se invece inserisci il tuo codice all'interno del costrutto `try/catch` vuol dire che pensi che il tuo codice possa generare errori prima ancora di avere una soluzione. **Da evitare** ```javascript @@ -1743,19 +1726,18 @@ try { try { functionThatMightThrow(); } catch (error) { - // One option (more noisy than console.log): + // Un'ozione (più visibile del console.log): console.error(error); - // Another option: + // Un'altra opzione: notifyUserOfError(error); - // Another option: + // Un'altra opzione: reportErrorToService(error); - // OR do all three! + // Oppure usale tutte e tre! } ``` -### Don't ignore rejected promises -For the same reason you shouldn't ignore caught errors -from `try/catch`. +### Non ignorare le Promise quando vengono rigettate +Per la stessa ragione per cui non dovresti ignorare gli errori con `try/catch`. **Da evitare** ```javascript @@ -1775,13 +1757,13 @@ getdata() functionThatMightThrow(data); }) .catch((error) => { - // One option (more noisy than console.log): + // Un'ozione (più visibile del console.log): console.error(error); - // Another option: + // Un'altra opzione: notifyUserOfError(error); - // Another option: + // Un'altra opzione: reportErrorToService(error); - // OR do all three! + // Oppure usale tutte e tre! }); ``` From 095c269dd7f23983399db4833046d7504f8ee689 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 12:09:32 +0200 Subject: [PATCH 031/170] translation --- README.md | 52 +++++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 450b260e..872308d7 100644 --- a/README.md +++ b/README.md @@ -1770,20 +1770,15 @@ getdata() **[⬆ torna su](#lista-dei-contenuti)** -## **Formatting** -Formatting is subjective. Like many rules herein, there is no hard and fast -rule that you must follow. The main point is DO NOT ARGUE over formatting. -There are [tons of tools](http://standardjs.com/rules.html) to automate this. -Use one! It's a waste of time and money for engineers to argue over formatting. +## **Formattazione** -For things that don't fall under the purview of automatic formatting -(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here -for some guidance. +La formattazione è soggettiva. Come molte delle sopracitate, non esiste una regola rigida e veloce che devi seguire. Il punto principale è NON DISCUTERE sulla formattazione. +Ci sono un [sacco di strumenti](http://standardjs.com/rules.html) che automatizzano questo processo. Usane uno. È uno spreco di tempo e denaro per gli sviluppatori discutere sulla formattazione. + +### Utilizza le maiuscole in modo consistente + +JavaScript non è tipizzato, per questo l'uso delle maiuscole può darti indicazioni sulle tue variabili, funzioni, etc. Queste regole sono soggettive, per questo tu ed il tuo team potrete scegliere quella che vorrete. Il punto è: non importa quale regola sceglierete, l'importante è essere consistenti. -### Use consistent capitalization -JavaScript is untyped, so capitalization tells you a lot about your Variabili, -funzioni, etc. These rules are subjective, so your team can choose whatever -they want. The point is, no matter what you all choose, just be consistent. **Da evitare** ```javascript @@ -1818,9 +1813,8 @@ class Alpaca {} ### Function callers and callees should be close -If a function calls another, keep those funzioni vertically close in the source -file. Ideally, keep the caller right above the callee. We tend to read code from -top-to-bottom, like a newspaper. Because of this, make your code read that way. +Se una funzione ne richiama un'altra, mantieni queste funzioni verticalmente vicine nel sorgente. Idealmente, mantieni il chiamante subito sopra il chiamato. +Generalmente tendiamo a leggere il codice dall'alto verso il basso, come un giornale. Proprio per questo manteniamolo leggibile seguendo questa modalità. **Da evitare** ```javascript @@ -1902,10 +1896,9 @@ review.perfReview(); **[⬆ torna su](#lista-dei-contenuti)** -## **Comments** -### Only comment things that have business logic complexity. -Comments are an apology, not a requirement. Good code *mostly* documents itself. - +## **Commento** +### Commenta solo il codice che ha un alto livello di complessità +Commentare è una scusa, non un requisito. Un buon codice *spesso* si spiega da solo. **Da evitare** ```javascript function hashIt(data) { @@ -1946,8 +1939,8 @@ function hashIt(data) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Don't leave commented out code in your codebase -Version control exists for a reason. Leave old code in your history. +### Non lasciare parti del tuo codice commentate all'interno dei sorgenti +I sistemi di versioning esistono per un motivo. Lascia il tuo codice vecchio alla storia. **Da evitare** ```javascript @@ -1963,9 +1956,9 @@ doStuff(); ``` **[⬆ torna su](#lista-dei-contenuti)** -### Don't have journal comments -Remember, use version control! There's no need for dead code, commented code, -and especially journal comments. Use `git log` to get history! +### non utilizzare commenti giornalieri +Ricordati di usare sistemi di version control. Non c'è motivo per cui codice non utilizzato, codice commentato e specialmente commenti con riferimenti a date esistano nel tuo file. +Usa `git log` per avere lo storico! **Da evitare** ```javascript @@ -1988,9 +1981,8 @@ function combine(a, b) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Avoid positional markers -They usually just add noise. Let the funzioni and variable names along with the -proper indentation and formatting give the visual structure to your code. +### Evita di specificare di cosa si tratta +Generalmente è solo fastidioso. Lascia che le tue funzioni e le tue variabili, insieme ad una corretta indentazioni ti diano una struttura visiva del tuo codice. **Da evitare** ```javascript @@ -2023,9 +2015,9 @@ const actions = function() { ``` **[⬆ torna su](#lista-dei-contenuti)** -## Translation +## Traduzioni -This is also available in other languages: +Questa guida è disponibile in altre lingue: - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) @@ -2042,5 +2034,7 @@ This is also available in other languages: - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Italiano**: + [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) **[⬆ torna su](#lista-dei-contenuti)** From 6cabe5d7a7e91060ea091a01f8d3ac3fc4c28eef Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 12:12:23 +0200 Subject: [PATCH 032/170] italian translation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 872308d7..c50291d5 100644 --- a/README.md +++ b/README.md @@ -2034,7 +2034,7 @@ Questa guida è disponibile in altre lingue: - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Italiano**: + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italiano**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) **[⬆ torna su](#lista-dei-contenuti)** From 86a8a0187528bb032bf742467f1c9a082899d5f7 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 13:44:43 +0200 Subject: [PATCH 033/170] translation --- README.md | 150 +++++++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index c50291d5..0252f9e8 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ Non si tratta di una guida stilistica, bensì una guida per cercare di produrre Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code*. -Il nostro lavoro come ingegneri del software ha solo 50 anni e stiamo ancora cercando di apprendere molto. Quando l'architettura del software sarà antica come l'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. +Il nostro lavoro come software engeneer esiste da soli 50 anni e stiamo ancora cercando di apprendere molto. Quando l'architettura del software godrà della stessa anzianità dell'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. -Un ultima cosa: conoscere queste regole non farà di te immediatamente uno sviluppatore di software migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. -Ogni singola parte di codice parte come bozza, prima, per per poi prendere forma come una scultura di argilla. -Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice con i nostri colleghi. Ma non ti abbattre alla prima revisione che richiederà miglioramenti: *Beat up the code instead!* +Un ultima cosa: conoscere queste regole non farà di te immediatamente un developer migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. +Ogni singola parte di codice parte come bozza, inizialmente, per per poi prendere forma esattamente come una scultura di argilla. +Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice con i nostri colleghi. Ma non abbatterti tu la prima volta che il tuo codice sarà revisionato e richiederà miglioramenti: *Abbatti il codice!* ## **Variabili** ### Utilizza nomi di variabili comprensibili e pronunciabili @@ -46,7 +46,7 @@ const currentDate = moment().format('YYYY/MM/DD'); ``` **[⬆ torna su](#lista-dei-contenuti)** -### Usa lo stesso lessico per lo stesso tipo di variabili +### Usa la stessa semantica per lo stesso tipo di variabili **Da evitare** ```javascript @@ -61,11 +61,11 @@ getUser(); ``` **[⬆ torna su](#lista-dei-contenuti)** -### Utilizza nomi ricercabili -Leggeremo molto più codice di quanto non ne scriveremo mai. È importante che il codice che noi scriviamo sia leggibile e ricercabile. Nominando variabili che non assumono uno specifico contesto all'interno del nostro software, irritiamo il lettore. +### Utilizza nomi che possano essere cercati +Leggiamo molto più codice di quanto non ne scriveremo mai. È importante che il codice che noi scriviamo sia leggibile e ricercabile. Nominando variabili che non assumono uno specifico contesto all'interno del nostro software, possiamo irritare chi lo legge. Fai in modo che i nomi delle tue variabili siano ricercabili. Strumenti come [buddy.js](https://github.com/danielstjules/buddy.js) e -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare costanti non rinominate. +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare, per esempio, costanti non rinominate. **Da evitare** ```javascript @@ -77,9 +77,9 @@ setTimeout(blastOff, 86400000); **Bene:** ```javascript // Dichiarala come costante in maiuscolo. -const MILLISECONDS_IN_A_DAY = 86400000; +const MILLISECONDI_IN_UN_GIORNO = 86400000; -setTimeout(blastOff, MILLISECONDS_IN_A_DAY); +setTimeout(blastOff, MILLISECONDI_IN_UN_GIORNO); ``` **[⬆ torna su](#lista-dei-contenuti)** @@ -163,10 +163,10 @@ function paintCar(car) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Utilizza i valori di default (predefiniti), anzichè usare condizioni o cortocircuiti +### Utilizza i valori di default (predefiniti), anzichè usare condizioni o valutazioni minime -I valori di default, generalmente sono più chiari dei cortocircuiti. Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. -Tutti gli altri valori "falsi" come `''`, `""`, `false`, `null`, `0`, e +I valori di default, generalmente sono più chiari dei [valutazioni minime](https://it.wikipedia.org/wiki/Valutazione_a_corto_circuito). Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. +Tutti gli altri valori "falsy" come `''`, `""`, `false`, `null`, `0`, e `NaN`, non saranno sostituiti da un valore predefinito. **Da evitare** @@ -192,17 +192,17 @@ function createMicrobrewery(name = 'Hipster Brew Co.') { Limitare il numero di argomenti di una funzione è incredibilmente importante perchè ti permette di testarla più facilmente. Avere più di 3 argomenti può portare ad un'esplosione di combinazioni da testare, che produrranno una lunga serie di casi da verificare. -1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. In alcuni casi, in cui questo non è del tutto vero, un oggetto può aiutare ad ovviare a questo problema. +1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. Nei casi in cui questo non sia del tutto vero, un oggetto può aiutare ad ovviare a questo problema. -Dal momento in cui JavaScript permette la creazione di oggetti al volo, senza dover passare attraverso classi specifiche, puoi usare un oggetto se pensi che il tuo metodo richieda molti argomenti. +Dal momento in cui JavaScript permette la creazione di oggetti al volo puoi usare un oggetto, se pensi che il tuo metodo richieda molti argomenti. Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi: -1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà sono state utilizzate +1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà saranno utilizzate -2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti collaterali. Nota: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. +2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti inattesi. **Nota**: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. -3. Un Linter può avvisarti che non stai utilizzando alcune delle proprietà del tuo oggetto, non utilizzando la sintassi destrutturata non sarebbe possibile +3. Un Linter può avvisarti che non stai utilizzando alcune delle proprietà del tuo oggetto, diversamente non sarebbe possibile. **Da evitare** ```javascript @@ -229,7 +229,7 @@ createMenu({ ### Un metodo dovrebbe fare una sola cosa Questa è di sicuro la regola più importante nell'ingegneria del software. Quando un metodo si occupa di più di un solo aspetto sarà più difficile da testare, comporre e ragioraci sopra. -Se è possibile far eseguire al metodo una sola azione sarà più facile da rifattorizzare e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. +Se è possibile far eseguire al metodo una sola azione sarà più facile il suo refactor e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. **Da evitare** ```javascript @@ -285,7 +285,7 @@ addMonthToDate(1, date); ### Le funzioni dovrebbero avere un solo livello di astrazione -Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarla e testarla più facilmente. +Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarle e testare più facilmente. **Da evitare** ```javascript @@ -353,7 +353,7 @@ function lexer(tokens) { ### Rimuovi il codice duplicato Fai del tuo meglio per evitare codice duplicato. Duplicare il codice è un male, perchè vuol dire che c'è più di un punto da modificare nel caso in cui dovessi cambiare alcune logiche. -Immagina di avere un ristorante e di dover tener traccia del tuo magazzino: la riserva di pomodori, cipolle, aglio, spezie, etc. Se hai più di una lista in cui tieni traccia di queste quantità dovrai aggiornarle tutte, ogni volta che servirai un piatto con dei pomodori. Al contrario, se dovessi avere una sola lista, avrai un solo posto un cui dovrai tenere traccia delle modifiche sulle quantità in magazzino. +Immagina di avere un ristorante e di dover tener traccia del tuo magazzino: la riserva di pomodori, cipolle, aglio, spezie, etc. Se hai più di una lista in cui tieni traccia di queste quantità dovrai aggiornarle tutte ogni volta che servirai, per esempio, un piatto con dei pomodori. Al contrario, se dovessi avere una sola lista, avrai un solo posto un cui dovrai tenere traccia delle modifiche sulle quantità in magazzino. Generalmente si duplica il codice perchè ci sono due o tre piccole differenze tra una parte e l'altra del software. Questo permette di condividere le parti comuni del codice, ma allo stesso tempo avrai dei duplicati di parti che fanno la stessa cosa. Rimuovere questi duplicati, significa creare un'astrazione che permette di gestire queste differenze attraverso un unico metodo/modulo/classe. @@ -459,7 +459,7 @@ function createMenu(config) { cancellable: true }, config); - // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config adesso sarà: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } @@ -495,11 +495,11 @@ function createTempFile(name) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Evitare effetti collaterali (parte 1) +### Evitare effetti inattesi (parte 1) Una funzione può generare un effetto collaterale se fa altro oltre a ricevere un valore e restituirne uno o più. L'effetto collaterale potrebbe essere scrivere su un file, modificare una variabile globale o accidentalmente girare tutti i tuoi soldi ad uno sconosciuto. -Probabilmente e occasionalmente avrai bisogno di generare un effetto collaterale: come nell'esempio precedente magari proprio scrivere su un file. +Probabilmente, e occasionalmente, avrai bisogno di generare un effetto collaterale: come nell'esempio precedente magari proprio scrivere su un file. Quello che dovrai fare è centralizzare il punto in cui lo fai. Non avere più funzioni e classi che fanno la stessa cosa, ma averne un servizio ed uno soltanto che se ne occupa. La questione più importante è evitare le insidie che stanno dietro ad errate manipolazioni di oggetti senza alcuna struttura, utilizzando strutture che possono essere modificate da qualunque parte. @@ -536,18 +536,18 @@ console.log(newName); // ['Ryan', 'McDermott']; ``` **[⬆ torna su](#lista-dei-contenuti)** -### Evitare effetti collaterali (parte 2) -In Javascript i valori primitivi sono passati come valori, mentre oggetti ed array vengon passati come riferimento. In caso di oggetti ed array, se la tua funzione modifica l'array contenente un carrello della spesa, per esempio aggiungendo o rimuovendo un oggetto da acquistare, tutte le altre funzioni che utilizzano l'array `carrello` saranno condizionati da questa modifica. Queso potrebbe essere un bene ed un male allo stesso tempo. Immaginiamo una situazione in cui questo è un male: +### Evitare effetti inattesi (parte 2) +In Javascript i valori primitivi sono passati come valori, mentre oggetti ed array vengono passati come reference. Nel caso di oggetti ed array, se la tua funzione modifica l'array contenente un carrello della spesa, per esempio, aggiungendo o rimuovendo un oggetto da acquistare, tutte le altre funzioni che utilizzeranno l'array `carrello` saranno condizionati da questa modifica. Questo potrebbe essere un bene ed un male allo stesso tempo. Immaginiamo una situazione in cui questo è un male: l'utente clicca sul tasto "Acquista", che richiamerà una funzione `acquista` che effettua una richiesta ed invia l'array `carrello` al server. Per via di una pessima connessione, il metodo `acquista` riproverà ad effettuare la richiesta al server. Cosa succede se nello stesso momento accidentalmemte l'utente clicca su "Aggiungi al carrello" su di un oggetto che non ha intenzione di acquistare prima che venga eseguita nuovamente la funzione? Verrà inviata la richiesta con il nuovo oggetto accidentalmente aggiunto al carrello utilizzando la funzione `aggiungiOggettoAlCarrello`. Un'ottima soluzione è quella di di clonare sempre l'array `carrello`, modificarlo e restituire il clone. -Questi ci assicurerà che che nessun'altra funzione che gestisce il carrello subirà cambiamenti non voluti. +Questo ci assicurerà che che nessun'altra funzione che gestisce il carrello subirà cambiamenti non voluti. Due precisazioni vanno fatte su questo approccio: -1. Potrebbe essere che tu voglia realmente modificare l'oggetto in input, ma vedrai che utilizzando questo approccio ti accorgerai che le questi casi sono veramente rari. La maggior parte delle volte dovrai utilizzare questo approccio per non generare effetti collaterali +1. Potrebbe essere che tu voglia realmente modificare l'oggetto in input, ma vedrai che utilizzando questo approccio ti accorgerai che questi casi sono veramente rari. La maggior parte delle volte dovrai utilizzare questo approccio per non generare effetti inattesi 2. Clonare oggetti molto grandi potrebbe essere davvero dispendioso in termini di risorse. Fortunatamente questo non è un problema perchè esistono [ottime librerie](https://facebook.github.io/immutable-js/) che permettono di utilizzare questo approccio senza dispendio di memoria e più velocemente rispetto al dovelo fare manualmente. @@ -567,11 +567,9 @@ const addItemToCart = (cart, item) => { **[⬆ torna su](#lista-dei-contenuti)** -### Don't write to global funzioni ### Non aggiungere funzioni globali - -Contaminare delle variabili globali è una pratica sconsigliata, in quanto potresti entrare in conflitto con altre librerie e l'utilizzatore delle tue API potrebbe non accorgersene fintanto che non si trova in produzione, generando un'eccezione. -Facciamo un esempio pratico: supponiamo che tu voglia estendere il costruttore Array nativo di JavaScript aggiungendo il metodo `diff` che mostra le differenze tra due array. Come puoi fare? +Contaminare o aggiungere delle variabili globali è una pratica sconsigliata, in quanto potresti entrare in conflitto con altre librerie e chi utiliza le tue API potrebbe non accorgersene fintanto che non si trova in produzione, generando un'eccezione. +Facciamo un esempio pratico: supponiamo che tu voglia estendere il costrutto Array nativo di JavaScript aggiungendo il metodo `diff` che mostra le differenze tra due array. Come puoi fare? Potresti scrivere il metodo utilizzando `Array.prototype`, che però potrebbe entrare in conflitto con con un'altra libreria che fa la stessa cosa. Cosa succederebbe se anche l'altra libreria utilizzasse `diff` per trovare le differenze tra due array? Ecco perchè è molto meglio utilizzare le classi ES2015/ES6 e semplicemente estendere `Array`. **Da evitare** @@ -598,7 +596,7 @@ class SuperArray extends Array { *[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale)* - *[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa)* -Javascript non è un linguaggio funzionale alla stregua di Haskell, ma entrambi hanno qualcosa che li accomuna. +Javascript non è un linguaggio funzionale alla stregua di [Haskell](https://www.haskell.org/), ma entrambi hanno qualcosa che li accomuna. I linguaggi funzionali generalmente sono più puliti e facili da testare. Preferisci questo stile se possibile. @@ -651,7 +649,7 @@ const totalOutput = programmerOutput ``` **[⬆ torna su](#lista-dei-contenuti)** -### Incapsula i condizionali +### Incapsula le condizioni **Da evitare** ```javascript @@ -697,7 +695,7 @@ if (isDOMNodePresent(node)) { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Evita i condizionali +### Evita le condizioni Sembrerebbe un task impossibile. Ad un rapido sguardo molti sviluppatori potrebbero pensare "come posso pensare di far funzionare qualcosa senza utilizzare un `if`?" La risposta è che puoi utilizzare il polimorfismo per ottenere lo stesso risultato in molti casi. La seconda domanda generalmente è "Ottimo! Ma perchè dovrei farlo?". @@ -752,7 +750,6 @@ class Cessna extends Airplane { **[⬆ torna su](#lista-dei-contenuti)** ### Evita la validazione dei tipi (parte 1) - JavaScript è un linguaggio non tipizzato, il che significa che le tue funzioni possono accettare qualunque tipo di argomento. Qualche volta potresti essere tentato da tutta questa libertà e potresti essere altrettanto tentato di veririfcare il tipo di dato ricevuto nella tua funzione. Ci sono molti modi per evitare di dover fare questo tipo di controllo. @@ -778,10 +775,9 @@ function travelToTexas(vehicle) { **[⬆ torna su](#lista-dei-contenuti)** ### Evita la validazione dei tipi (part 2) - -Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di TypeScript. +Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di [TypeScript](https://www.typescriptlang.org/). È una vlidissima alternativa al normale JavaScript che fornicsce una validazione di tipi statica utilizzando la sintassi JavaScript. -Il problema con la validazione manuale dei tipi utilizzando JavaScript standard è che richiede tanto codice extra che non compensa la scarsa leggibilità del codice ottenuto. +Il problema con la validazione manuale dei tipi utilizzando lo standard JavaScript è che richiede tanto codice extra che non compensa la scarsa leggibilità del codice ottenuto. Mantieni pulito il tuo codice, scrivi dei test validi e cerca di fare delle buone revisioni del coidice. Altrimenti utilizza TypeScript (che come detto in precedenza è una validissima alternativa!) **Da evitare** @@ -805,13 +801,13 @@ function combine(val1, val2) { **[⬆ torna su](#lista-dei-contenuti)** ### Non ottimizzare eccessivamente -I browser moderni eseguono un sacco di ottimizzazione "sottobanco". Molte volte, quando cerchi di ottimizzare il tuo codice, stai perdendo tempo prezioso. [Ci sono tante guide](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) che aiutano a capire quali tipi di ottimizzazione sono superflui e quali no. Utilizza queste guide nel frattempo, fintanto che queste mancanze non verranno colmate. +I browser moderni eseguono un sacco di ottimizzazione "sottobanco". Molte volte, quando cerchi di ottimizzare il tuo codice, stai perdendo tempo prezioso. [Ci sono tante guide](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) che aiutano a capire quali tipi di ottimizzazione sono superflui e quali no. Utilizza queste guide nel frattempo, fintanto che queste lacune non verranno colmate. **Da evitare** ```javascript // Nei vecchi browser ogni volta che in un ciclo verifichi `list.length` potrebbe -// essere dispendioso per via del ricalcolo della lunghezza. Nei browser moderni +// essere dispendioso per via del suo ricalcolo. Nei browser moderni // questa operazione è stata ottimizzata for (let i = 0, len = list.length; i < len; i++) { // ... @@ -827,7 +823,7 @@ for (let i = 0; i < list.length; i++) { **[⬆ torna su](#lista-dei-contenuti)** ### Rimuovi il codice inutilizzato -Il codice inutilizzato è dannoso quanto il codice duplicato. Non c'è motivo per cui tenerlo nella tua codebase. Se realmente non viene utilizzato rimuovilo! +Il codice inutilizzato è dannoso quanto il codice duplicato. Non c'è motivo per cui tenerlo nel tuo codebase. Se realmente non viene utilizzato rimuovilo! Sarà comunque presente nella storia del tuo file se utilizzi un sistema di versioning nel caso in cui dovesse servirti. **Da evitare** @@ -858,9 +854,9 @@ inventoryTracker('apples', req, 'www.inventory-awesome.io'); ## **Ogetti e strutture dati** ### Utilizza getter e setter -Utilizzare getter e setter per acceedere ai dati di un oggetto può essere meglio che accedere direttamente alle sue proprietà. Ti starai chiedendo il motivo. +Utilizzare getter e setter per acceedere ai dati di un oggetto può essere meglio che accedere direttamente alle sue proprietà. Ti starai sicuramente chiedendo il motivo. Eccoti qualche motivo per cui utilizzare getter e setter: -* Quando hai bisogno di eseguire un'operazione col dato recuperato, non devi andare a modificarlo ogni volta che accedi al dato nel tuo codice. +* Quando hai bisogno di eseguire un'operazione sul dato recuperato, non devi andare a modificarlo ogni volta che accedi al dato nel tuo codice. * Valida il dato nel momento in cui lo setti con `set` * Incapsula la rappresentazione interna * È più facile loggare e gestire gli errori quando imposti o recuperi un dato @@ -952,7 +948,7 @@ console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ## **Classi** ### Utilizza le classi ES2015/ES6 piuttosto che le funzioni di ES5 -È molto difficile ottenere leggibilità per ereditarietà, costrutti e metodi in definizioni di oggetti in ES5. Se hai bisogno di ereditarietà (e bada bene, non è detto che tu ne abbia bisogno), utilizza il costrutto Class di ES2015/ES6. Altrimenti utilizza piccole funzioni fintanto che non avrai bisogno di gestire oggetti più complessi. +È molto difficile ottenere leggibilità sull'ereditarietà, costrutti e metodi in definizioni di oggetti in ES5. Se hai bisogno di ereditarietà (e bada bene, non è detto che tu ne abbia bisogno), utilizza il costrutto Class di ES2015/ES6. Altrimenti utilizza piccole funzioni fintanto che non avrai bisogno di gestire oggetti più complessi. **Da evitare** ```javascript @@ -1025,7 +1021,7 @@ class Human extends Mammal { ### Concatena i metodi -Questo pattern è molto utilizzato in JavaScript e puoi trovarne applicazione in molte liberie come jQuery e Loadash. Permette al tuo codice di essere maggiormente espressivo e meno verboso. +Questo pattern è molto utilizzato in JavaScript e puoi trovarne applicazione in molte liberie come [jQuery](https://jquery.com/) e [Lodash](https://lodash.com/). Permette al tuo codice di essere maggiormente espressivo e meno verboso. Proprio per questo motivo, insisto, utilizza la concatenazione dei metodi e guarda come può essere più pulito il tuo codice. Nei metodi della tua classe, semplicemente restituisci il riferimento `this` alla fine di ogni metodo, in modo da poter concatenare altri metodi. @@ -1100,13 +1096,13 @@ const car = new Car('Ford','F-150','red') ``` **[⬆ torna su](#lista-dei-contenuti)** -### Preferisci la composizione all'ereditarietà -Come dichiarato dalla Gang of four in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) dovresti preferire la composizione all'ereditarietà quando puoi. Ci sono validi motivi per utilizzare l'ereditarietà e altrettanto validi motivi per utilizzare la composizione. -Il punto principale di questo assunto è che mentalmente sei portato a preferire l'ereditarietà; prova a pensare alla composizione per risolvere il tuo problema, tante volte è davvero la soluzione migliore. -Ti potresti chiedere: "Quando dovrei utilizzare l'ereditarietà?". Dipende dal problema che devi affrontare, ma c'è una discreta lista di suggerimenti che ti potrebbero aiutare a capire quando l'ereditarietà è meglio della composizione: +### Preferisci una struttura compositiva all'ereditarietà +Come dichiarato dalla Gang of four in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) dovresti preferire la strutturazione all'ereditarietà quando puoi. Ci sono validi motivi per utilizzare l'ereditarietà e altrettanto validi motivi per utilizzare la strutturazione. +Il punto principale di questo assunto è che mentalmente sei portato a preferire l'ereditarietà. Prova a pensare alla strutturazione per risolvere il tuo problema: tante volte è davvero la soluzione migliore. +Ti potresti chiedere: "Quando dovrei utilizzare l'ereditarietà?". Dipende dal problema che devi affrontare, ma c'è una discreta lista di suggerimenti che ti potrebbero aiutare a capire quando l'ereditarietà è meglio della strutturazione: -1. L'estensione che stai mettendo in atto rappresenta una relazione di tipo "è di questo tipo" e non "ha questa proprietà" (Umano->Animale vs. Utente->DettagliUtente). -2. Puoi riutilizzare il codice dalla classe padre (Umano) +1. L'estensione che stai mettendo in atto rappresenta una relazione di tipo "è-un" e non "ha-un" (Umano->Animale vs. Utente->DettagliUtente). +2. Puoi riutilizzare il codice dalla classe padre 3. Vuoi fare cambiamenti globali a tutte le classi estese tramite la classe di partenza. **Da evitare** @@ -1158,8 +1154,8 @@ class Employee { **[⬆ torna su](#lista-dei-contenuti)** ## **SOLID** -### Single Responsibility Principle (SRP) (Principio della singola responsabilità) -Come indicato in *Clean code*, "Non dovrebbe mai esserci più di un solo motivo per modificare una classe". La tentazione è sempre quella di fare un'unica classe con molte funzionalità, come quando vuoi portarti un unico bagaglio sul volo. +### Single Responsibility Principle (SRP) (Principio di singola responsabilità) +Come indicato in *Clean code*, "Non dovrebbe mai esserci più di un solo motivo per modificare una classe". La tentazione è sempre quella di fare un'unica classe con molte funzionalità, come quando vuoi portarti un unico bagaglio a bordo. Il problema con questo approccio è che le tue classi non saranno concettualmente coese e ti potrebbero dare più di una ragione per modificarle in seguito. Minimizzare il numero di volte in cui modificare una classe è importante. È importante perchè quando ci sono troppe funzionalità in una classe è difficile capire che effetto avrà sulle classe che la estendono, nel caso in cui farai un cambiamento. @@ -1211,7 +1207,7 @@ class UserSettings { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Open/Closed Principle (OCP) (Principio di apertura/chiusura) +### Open/Closed Principle (OCP) (Principio aperto/chiuso) Come dichiarato da Bertrand Meyer, "Le entità di un software (Classi, moduli, funzioni, etc.) dovrebbero essere aperte all'estensione, ma chiuse alla modifica. Cosa significa esattamente? Quello che intende è che dovresti dare ai tuoi utilizzatori la possibilità di aggiungere nuove funzionalità, non modificando quelle esistenti. @@ -1296,11 +1292,12 @@ class HttpRequester { ``` **[⬆ torna su](#lista-dei-contenuti)** -### Liskov Substitution Principle (LSP) (Principio dell'intercambiabilità di Liskov) +### Liskov Substitution Principle (LSP) (Principio di sostituzione di Liskov) Questo nome sembra molto più spaventoso di quello che in realtà significa. -Formalmente la sua defnizione è "Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S () senza modificare alcuna della proprietà del software (correttezza, compito da svolgere, etc.)". Questa definizione suona ovviamente come più complessa. +Formalmente la sua defnizione è "Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S (per esempio un oggetto di tipo S può sostituire un oggetto di tipo T) senza modificare alcuna della proprietà del software (correttezza, compito da svolgere, etc.)". Questa definizione suona comunque complessa. + Forse una spiegazione più esaustiva potrebbe essere: "Se hai una classe *figlio* che estende una classe *genitore* allora le due classi possono essere intercambiate all'interno del codice senza generare errori o risultati inattesi". -Potrebbe esssere ancora poco chiaro, ma vediamo con un esempio (Quadrato/Rettangolo): matematicamente il Quadrato è un Rettangolo, ma se il tuo modello usa una relazione di tipo "è-un" per eredità, potresti avere presto qualche problema. +Potrebbe esssere ancora poco chiaro, ma vediamo con un esempio (Quadrato/Rettangolo): matematicamente il Quadrato è un Rettangolo, ma se il tuo modello eredita una relazione di tipo "è-un", potresti avere presto qualche problema. **Da evitare** ```javascript @@ -1347,7 +1344,8 @@ function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // Male: Returns 25 for Square. Should be 20. + const area = rectangle.getArea(); // Sbagliato: Restituisce 25 anche + // per il quadrato. Dovrebbe essere 20. rectangle.render(area); }); } @@ -1403,9 +1401,9 @@ renderLargeShapes(shapes); ``` **[⬆ torna su](#lista-dei-contenuti)** -### Interface Segregation Principle (ISP) (Principio della segregazione delle interfacce) +### Interface Segregation Principle (ISP) (Principio di segregazione delle interfacce) -JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante anche per via della sua mancanza di tipizzazione. +JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante nonostante la sua mancanza di tipizzazione. ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [*duck-typing*](https://it.wikipedia.org/wiki/Duck_typing) Un buon esempio in JavaScript potrebbe essere fatto per le classi che richiedono la deinizione di un set di proprietà molto grande. @@ -1475,8 +1473,8 @@ const $ = new DOMTraverser({ ### Dependency Inversion Principle (DIP) (Principio di inversione delle dipendenze) Questo principio sostanzialmente indica due cose: -1. Moduli ad alto livello non dovrebbero necessariamente dipendere da moduli di basso livello -2. L'astrazione non dovrebbe dipendere dai dettagli. I dettagli non dovrebbero dipendere dall'astrazione. +1. Moduli ad alto livello non dovrebbero dipendere da molidi a basso livello. Entrambi dovrebbero dipendere da moduli astratti. +2. L'astrazione non dovrebbe dipendere da classi concrete. Le classi concrete dovrebbero dipendere da astrazioni. A primo impatto questo concetto potrebbe essere difficile da capire, ma nel caso in cui tu abbia lavorato con AngularJS avrai sicuramente visto l'applicazione di questo principio nel concetto di *Dependency injection (DI)*. Nonostante non sia esattamente identico come concetto, DIP evita che i moduli di alto livello conoscano i dettagli dei moduli di basso livello pur utilizzandoli. Uno dei benefici di questo utilizzo è che riduce la dipendenza tra due moduli. @@ -1562,12 +1560,12 @@ inventoryTracker.requestItems(); ## **Test** Testare è più importante che rilasciare. Se non hai test o non ne hai un numero adeguato, non saprai se ad ogni rilascio puoi rompere qualcosa. -Decidere quanti siano il numero sufficiente di test dipende dal tuo team, ma cercare di coprire il 100% dei casi (per tutti gli stati ed i branch) vuol dire avere massima tranquillità durante i rilasci. +Decidere quale sia il numero sufficiente di test dipende dal tuo team, ma cercare di coprire il 100% dei casi (per tutti gli stati ed i branch) vuol dire avere massima tranquillità durante i rilasci. Questo significa che oltre ad utilizzare una suite di test valida, dovresti utilizzare anche un [buon strumento di copertura](http://gotwarlost.github.io/istanbul/). Non ci sono scuse per non scrivere test. C'è un'abbondanza di ottimi [framewerk per i test in JavaScript](http://jstherightway.org/#Test-tools) quindi cerca quello più adatto alle tue esigenze. Quando tu ed il tuo team avrete individuato quello più giusto per voi, dovrete iniziare sempre a scrivere i test per ogni modulo/feature che introdurrete nel vostro software. -Se il vostro approccio preferito è quello del TestDrivenDevelopment (TDD) è ottimo, ma assicurati di individuare i tuoi obiettivi prima di rilasciare ogni singola feature o eseguire il refactor di una esistente. +Se il vostro approccio preferito è quello del TestDrivenDevelopment (TDD) ottimo, ma assicurati di individuare i tuoi obiettivi prima di rilasciare ogni singola feature o eseguire il refactor di una esistente. ### Un singolo comportamento per test @@ -1621,7 +1619,7 @@ describe('MakeMomentJSGreatAgain', () => { **[⬆ torna su](#lista-dei-contenuti)** ## **Consequenzialità** -### utilizza le Promise, non i callback +### utilizza le Promise, non funzioni di callback Le funzioni di callback non sono sempre chiare e possono generare un eccessivo numero di nidificazioni. Con ES2015/ES6 sono nativamente e globalmente accessibili. Utilizzale! **Da evitare** @@ -1664,8 +1662,8 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') ``` **[⬆ torna su](#lista-dei-contenuti)** -### Async/Await sono anche più chiari delle Promise -Le Promise sono una valida e chiara alternativa alle funzioni di callback, ma ES2017/ES8 offre anche async and await che possono essere addirittura una soluzione più chiara. +### Async/Await sono addirittura più chiari delle Promise +Le Promise sono una valida e chiara alternativa alle funzioni di callback, ma ES2017/ES8 offrono anche async and await che possono essere addirittura una soluzione più migliore. Tutto quello che devi fare non è niente altro che scrivere una funzione che abbia prefisso `async` e puoi scrivere la tua logica senza dover concatenare con la kyword `then`. Utilizza questo approccio se hai la possibilità di utilizzare le feature ES2017/ES8! @@ -1709,8 +1707,8 @@ async function getCleanCodeArticle() { Generare errori è una buona cosa. Vuol dire che l'esecuzione del tuo codice ha identificato precisamente quando nel tuo software qualcosa è andato storto e ti permette di interromperne l'esecuzione nello stack corrente terminando il processo (in Node), e notificandolo attraverso la console. ### Non ingnorare gli errori intercettati -Non fare niente con gli errori intercettati non ti da l'abilità di fixare o reagire a questi errori. Loggare gli errori nella console (`console.log`) non ti assicura di non perderti nel mare di log stampati in console. -Se invece inserisci il tuo codice all'interno del costrutto `try/catch` vuol dire che pensi che il tuo codice possa generare errori prima ancora di avere una soluzione. +Non fare niente con gli errori intercettati non rende possibile correggerli o reagire all'errore. Loggare gli errori nella console (`console.log`) non ti assicura di non perderti nel mare di log stampati in console. +Se invece inserisci il tuo codice all'interno del costrutto `try/catch` vuol dire riconoscere la possibilità che esista un errore nel caso mettere in piedi un modo per gestirlo. **Da evitare** ```javascript @@ -1772,12 +1770,12 @@ getdata() ## **Formattazione** -La formattazione è soggettiva. Come molte delle sopracitate, non esiste una regola rigida e veloce che devi seguire. Il punto principale è NON DISCUTERE sulla formattazione. -Ci sono un [sacco di strumenti](http://standardjs.com/rules.html) che automatizzano questo processo. Usane uno. È uno spreco di tempo e denaro per gli sviluppatori discutere sulla formattazione. +La formattazione è soggettiva. Come molte di quelle sopracitate, non esiste una regola assoluta e veloce che devi seguire. Il punto principale, però, è NON DISCUTERE della formattazione. +Ci sono un [sacco di strumenti](http://standardjs.com/rules.html) che automatizzano questo processo. Usane uno. È uno spreco di tempo e denaro per gli sviluppatori discutere della formattazione. ### Utilizza le maiuscole in modo consistente -JavaScript non è tipizzato, per questo l'uso delle maiuscole può darti indicazioni sulle tue variabili, funzioni, etc. Queste regole sono soggettive, per questo tu ed il tuo team potrete scegliere quella che vorrete. Il punto è: non importa quale regola sceglierete, l'importante è essere consistenti. +JavaScript non è tipizzato, per questo l'uso delle maiuscole può darti indicazioni sulle tue variabili, funzioni, etc. Queste regole sono soggettive, per questo tu ed il tuo team potrete scegliere quella che volete. Il punto è: non importa quale regola sceglierete, l'importante è essere consistenti. **Da evitare** @@ -1812,8 +1810,8 @@ class Alpaca {} **[⬆ torna su](#lista-dei-contenuti)** -### Function callers and callees should be close -Se una funzione ne richiama un'altra, mantieni queste funzioni verticalmente vicine nel sorgente. Idealmente, mantieni il chiamante subito sopra il chiamato. +### Richiami e dichiarazioni di funzioni dovrebbero essere vicini +Se una funzione ne richiama un'altra, mantieni queste funzioni verticalmente vicine nel sorgente. Idealmente, mantieni il richiamo subito sopra la dichiarazione. Generalmente tendiamo a leggere il codice dall'alto verso il basso, come un giornale. Proprio per questo manteniamolo leggibile seguendo questa modalità. **Da evitare** @@ -1956,7 +1954,7 @@ doStuff(); ``` **[⬆ torna su](#lista-dei-contenuti)** -### non utilizzare commenti giornalieri +### Non utilizzare i commenti come un diario Ricordati di usare sistemi di version control. Non c'è motivo per cui codice non utilizzato, codice commentato e specialmente commenti con riferimenti a date esistano nel tuo file. Usa `git log` per avere lo storico! From 64d352ff1e35c2733b9c1aa2030d83ea1c7d96c8 Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 13:53:13 +0200 Subject: [PATCH 034/170] merge --- README.md | 2022 ++--------------------------------------------------- 1 file changed, 54 insertions(+), 1968 deletions(-) diff --git a/README.md b/README.md index 0252f9e8..33aa78fd 100644 --- a/README.md +++ b/README.md @@ -1,2038 +1,124 @@ # clean-code-javascript +Software engineering principles, from Robert C. Martin's wonderful book *Clean Code*, adapted for JavaScript. -## Lista dei contenuti - 1. [Introduzione](#introduzione) - 2. [Variabili](#variabili) - 3. [Funzioni](#funzioni) - 4. [Ogetti e strutture dati](#objects-and-data-structures) - 5. [Classi](#Classi) - 6. [SOLID](#solid) - 7. [Test](#Test) - 8. [Concurrency](#concurrency) - 9. [Error Handling](#error-handling) - 10. [Formatting](#formatting) - 11. [Comments](#comments) - 12. [Translation](#translation) +## **Variables** +### Use meaningful and pronounceable variable names -## Introduzione -![Immagine umoristica che rappresenta quanto sia possibile stimare la qualità di un software attraverso il numero di parolacce espresse durante la lettura del codice](http://www.osnews.com/images/comics/wtfm.jpg) - -Principi di Ingegneria del Software, dal libro di Robert C. Martin -[*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adattati a JavaScript. - -Non si tratta di una guida stilistica, bensì una guida per cercare di produrre software -[leggibile, riutilizzabile e rifattorizzabile](https://github.com/ryanmcdermott/3rs-of-software-architecture) in JavaScript. - -Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code*. - -Il nostro lavoro come software engeneer esiste da soli 50 anni e stiamo ancora cercando di apprendere molto. Quando l'architettura del software godrà della stessa anzianità dell'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. - -Un ultima cosa: conoscere queste regole non farà di te immediatamente un developer migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. -Ogni singola parte di codice parte come bozza, inizialmente, per per poi prendere forma esattamente come una scultura di argilla. -Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice con i nostri colleghi. Ma non abbatterti tu la prima volta che il tuo codice sarà revisionato e richiederà miglioramenti: *Abbatti il codice!* - -## **Variabili** -### Utilizza nomi di variabili comprensibili e pronunciabili - -**Da evitare** +**Bad:** ```javascript -const yyyymmdstr = moment().format('YYYY/MM/DD'); +var yyyymmdstr = moment().format('YYYY/MM/DD'); ``` -**Bene:** +**Good**: ```javascript -const currentDate = moment().format('YYYY/MM/DD'); +var yearMonthDay = moment().format('YYYY/MM/DD'); ``` -**[⬆ torna su](#lista-dei-contenuti)** -### Usa la stessa semantica per lo stesso tipo di variabili +### Use the same vocabulary for the same type of variable -**Da evitare** +**Bad:** ```javascript getUserInfo(); getClientData(); getCustomerRecord(); ``` -**Bene:** +**Good**: ```javascript getUser(); ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Utilizza nomi che possano essere cercati -Leggiamo molto più codice di quanto non ne scriveremo mai. È importante che il codice che noi scriviamo sia leggibile e ricercabile. Nominando variabili che non assumono uno specifico contesto all'interno del nostro software, possiamo irritare chi lo legge. -Fai in modo che i nomi delle tue variabili siano ricercabili. -Strumenti come [buddy.js](https://github.com/danielstjules/buddy.js) e -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare, per esempio, costanti non rinominate. - -**Da evitare** -```javascript -// Cosa caspita significa 86400000? -setTimeout(blastOff, 86400000); - -``` - -**Bene:** -```javascript -// Dichiarala come costante in maiuscolo. -const MILLISECONDI_IN_UN_GIORNO = 86400000; - -setTimeout(blastOff, MILLISECONDI_IN_UN_GIORNO); - -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Utilizza nomi di variabili esplicartivi -**Da evitare** -```javascript -const address = 'One Infinite Loop, Cupertino 95014'; -const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; -saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]); -``` - -**Bene:** -```javascript -const address = 'One Infinite Loop, Cupertino 95014'; -const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; -const [, city, zipCode] = address.match(cityZipCodeRegex) || []; -saveCityZipCode(city, zipCode); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evita mappe mentali -Essere espliciti è meglio che non esserlo. - -**Da evitare** -```javascript -const locations = ['Austin', 'New York', 'San Francisco']; -locations.forEach((l) => { - doStuff(); - doSomeOtherStuff(); - // ... - // ... - // ... - // A cosa fa riferimento esattamente `l`? - dispatch(l); -}); -``` - -**Bene:** -```javascript -const locations = ['Austin', 'New York', 'San Francisco']; -locations.forEach((location) => { - doStuff(); - doSomeOtherStuff(); - // ... - // ... - // ... - dispatch(location); -}); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Non contestualizzare inutilmente - -Se il nome della tua classe/oggetto ti indica a cosa fa riferimento, non ripeterlo nei nomi delle sue proprietà o funzioni. - -**Da evitare** -```javascript -const Car = { - carMake: 'Honda', - carModel: 'Accord', - carColor: 'Blue' -}; - -function paintCar(car) { - car.carColor = 'Red'; -} -``` - -**Bene:** -```javascript -const Car = { - make: 'Honda', - model: 'Accord', - color: 'Blue' -}; - -function paintCar(car) { - car.color = 'Red'; -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Utilizza i valori di default (predefiniti), anzichè usare condizioni o valutazioni minime - -I valori di default, generalmente sono più chiari dei [valutazioni minime](https://it.wikipedia.org/wiki/Valutazione_a_corto_circuito). Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. -Tutti gli altri valori "falsy" come `''`, `""`, `false`, `null`, `0`, e -`NaN`, non saranno sostituiti da un valore predefinito. - -**Da evitare** -```javascript -function createMicrobrewery(name) { - const breweryName = name || 'Hipster Brew Co.'; - // ... -} - -``` - -**Bene:** -```javascript -function createMicrobrewery(name = 'Hipster Brew Co.') { - // ... -} - -``` -**[⬆ torna su](#lista-dei-contenuti)** - -## **Funzioni** -### Argomenti di una funzione (idealmente 2 o anche meno) - -Limitare il numero di argomenti di una funzione è incredibilmente importante perchè ti permette di testarla più facilmente. Avere più di 3 argomenti può portare ad un'esplosione di combinazioni da testare, che produrranno una lunga serie di casi da verificare. -1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. Nei casi in cui questo non sia del tutto vero, un oggetto può aiutare ad ovviare a questo problema. +## **Functions** +### Limit the amount of function parameters (2 or less) +Use an object if you are finding yourself needing a lot of parameters -Dal momento in cui JavaScript permette la creazione di oggetti al volo puoi usare un oggetto, se pensi che il tuo metodo richieda molti argomenti. - -Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi: - -1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà saranno utilizzate - -2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti inattesi. **Nota**: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. - -3. Un Linter può avvisarti che non stai utilizzando alcune delle proprietà del tuo oggetto, diversamente non sarebbe possibile. - -**Da evitare** +**Bad:** ```javascript function createMenu(title, body, buttonText, cancellable) { - // ... + ... } ``` -**Bene:** +**Good**: ```javascript -function createMenu({ title, body, buttonText, cancellable }) { - // ... -} - -createMenu({ +var menuConfig = { title: 'Foo', body: 'Bar', - buttonText: 'Baz', - cancellable: true -}); -``` -**[⬆ torna su](#lista-dei-contenuti)** - - -### Un metodo dovrebbe fare una sola cosa -Questa è di sicuro la regola più importante nell'ingegneria del software. Quando un metodo si occupa di più di un solo aspetto sarà più difficile da testare, comporre e ragioraci sopra. -Se è possibile far eseguire al metodo una sola azione sarà più facile il suo refactor e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. - -**Da evitare** -```javascript -function emailClients(clients) { - clients.forEach((client) => { - const clientRecord = database.lookup(client); - if (clientRecord.isActive()) { - email(client); - } - }); -} -``` - -**Bene:** -```javascript -function emailActiveClients(clients) { - clients - .filter(isActiveClient) - .forEach(email); -} - -function isActiveClient(client) { - const clientRecord = database.lookup(client); - return clientRecord.isActive(); -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### I nomi delle funzioni dovrebbero farti capire cosa fanno - -**Da evitare** -```javascript -function addToDate(date, month) { - // ... -} - -const date = new Date(); - -// Difficile da dire esattamente cosa viene aggiunto tramite questa funzione -addToDate(date, 1); -``` - -**Bene:** -```javascript -function addMonthToDate(month, date) { - // ... -} - -const date = new Date(); -addMonthToDate(1, date); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Le funzioni dovrebbero avere un solo livello di astrazione - -Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarle e testare più facilmente. - -**Da evitare** -```javascript -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... - }); -} -``` - -**Bene:** -```javascript -function parseBetterJSAlternative(code) { - const tokens = tokenize(code); - const ast = lexer(tokens); - ast.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 lexer(tokens) { - const ast = []; - tokens.forEach((token) => { - ast.push( /* ... */ ); - }); - - return ast; -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Rimuovi il codice duplicato -Fai del tuo meglio per evitare codice duplicato. Duplicare il codice è un male, perchè vuol dire che c'è più di un punto da modificare nel caso in cui dovessi cambiare alcune logiche. - -Immagina di avere un ristorante e di dover tener traccia del tuo magazzino: la riserva di pomodori, cipolle, aglio, spezie, etc. Se hai più di una lista in cui tieni traccia di queste quantità dovrai aggiornarle tutte ogni volta che servirai, per esempio, un piatto con dei pomodori. Al contrario, se dovessi avere una sola lista, avrai un solo posto un cui dovrai tenere traccia delle modifiche sulle quantità in magazzino. - -Generalmente si duplica il codice perchè ci sono due o tre piccole differenze tra una parte e l'altra del software. Questo permette di condividere le parti comuni del codice, ma allo stesso tempo avrai dei duplicati di parti che fanno la stessa cosa. -Rimuovere questi duplicati, significa creare un'astrazione che permette di gestire queste differenze attraverso un unico metodo/modulo/classe. - -Ottenere la sufficiente astrazione può essere complicato. Per questo dovresti seguire i principi SOLID, approfonditi nella sezione *Classi*. -Un'astrazione non ottimale potrebbe anche essere peggio del codice duplicato, per cui fai attenzione! Non ripeterti, altrimenti dovrai aggiornare tutte le occorrenze della stessa logica ogni volta che vorrai cambiare qualcosa. - -**Da evitare** -```javascript -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); - }); -} -``` - -**Bene:** -```javascript -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); - }); -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Estendi un oggetto con Object.assign - -**Da evitare** -```javascript -const menuConfig = { - title: null, - body: 'Bar', - buttonText: null, + buttonText: 'Baz' 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); -``` - -**Bene:** -```javascript -const menuConfig = { - title: 'Order', - // User did not include 'body' key - buttonText: 'Send', - cancellable: true -}; - function createMenu(config) { - config = Object.assign({ - title: 'Foo', - body: 'Bar', - buttonText: 'Baz', - cancellable: true - }, config); - - // config adesso sarà: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} - // ... + ... } -createMenu(menuConfig); ``` -**[⬆ torna su](#lista-dei-contenuti)** +### Don't use flags as function parameters +Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. -### Non usare valori flag (true/false) come parametri di una funzione - -Un valore di tipo flag indica che la tua funzione può eseguire più di una sola operazione. Una funzione dovrebbe eseguire una sola operazione. Separa la tua funzione se deve eseguire più di una operazione in base al parametro flag che riceve in input. - -**Da evitare** +**Bad:** ```javascript function createFile(name, temp) { if (temp) { - fs.create(`./temp/${name}`); + fs.create('./temp/' + name); } else { fs.create(name); } } -``` - -**Bene:** -```javascript -function createFile(name) { - fs.create(name); -} - -function createTempFile(name) { - createFile(`./temp/${name}`); -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evitare effetti inattesi (parte 1) - -Una funzione può generare un effetto collaterale se fa altro oltre a ricevere un valore e restituirne uno o più. L'effetto collaterale potrebbe essere scrivere su un file, modificare una variabile globale o accidentalmente girare tutti i tuoi soldi ad uno sconosciuto. - -Probabilmente, e occasionalmente, avrai bisogno di generare un effetto collaterale: come nell'esempio precedente magari proprio scrivere su un file. -Quello che dovrai fare è centralizzare il punto in cui lo fai. Non avere più funzioni e classi che fanno la stessa cosa, ma averne un servizio ed uno soltanto che se ne occupa. - -La questione più importante è evitare le insidie che stanno dietro ad errate manipolazioni di oggetti senza alcuna struttura, utilizzando strutture che possono essere modificate da qualunque parte. -Se riuscirai ad evitare che questo accada...sarai ben più felice della maggior parte degli altri programmatori. - -**Da evitare** -```javascript - -// Variable globale utilizzata dalla funzione seguente. -// Nel caso in cui dovessimo utilizzarla in un'altra funzione a questo punto si tratterebbe di un array e potrebbe generare un errore. - -let name = 'Ryan McDermott'; - -function splitIntoFirstAndLastName() { - name = name.split(' '); -} - -splitIntoFirstAndLastName(); - -console.log(name); // ['Ryan', 'McDermott']; -``` - -**Bene:** -```javascript -function splitIntoFirstAndLastName(name) { - return name.split(' '); -} - -const name = 'Ryan McDermott'; -const newName = splitIntoFirstAndLastName(name); - -console.log(name); // 'Ryan McDermott'; -console.log(newName); // ['Ryan', 'McDermott']; -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evitare effetti inattesi (parte 2) -In Javascript i valori primitivi sono passati come valori, mentre oggetti ed array vengono passati come reference. Nel caso di oggetti ed array, se la tua funzione modifica l'array contenente un carrello della spesa, per esempio, aggiungendo o rimuovendo un oggetto da acquistare, tutte le altre funzioni che utilizzeranno l'array `carrello` saranno condizionati da questa modifica. Questo potrebbe essere un bene ed un male allo stesso tempo. Immaginiamo una situazione in cui questo è un male: - -l'utente clicca sul tasto "Acquista", che richiamerà una funzione `acquista` che effettua una richiesta ed invia l'array `carrello` al server. Per via di una pessima connessione, il metodo `acquista` riproverà ad effettuare la richiesta al server. Cosa succede se nello stesso momento accidentalmemte l'utente clicca su "Aggiungi al carrello" su di un oggetto che non ha intenzione di acquistare prima che venga eseguita nuovamente la funzione? -Verrà inviata la richiesta con il nuovo oggetto accidentalmente aggiunto al carrello utilizzando la funzione `aggiungiOggettoAlCarrello`. - -Un'ottima soluzione è quella di di clonare sempre l'array `carrello`, modificarlo e restituire il clone. -Questo ci assicurerà che che nessun'altra funzione che gestisce il carrello subirà cambiamenti non voluti. - -Due precisazioni vanno fatte su questo approccio: - -1. Potrebbe essere che tu voglia realmente modificare l'oggetto in input, ma vedrai che utilizzando questo approccio ti accorgerai che questi casi sono veramente rari. La maggior parte delle volte dovrai utilizzare questo approccio per non generare effetti inattesi - -2. Clonare oggetti molto grandi potrebbe essere davvero dispendioso in termini di risorse. Fortunatamente questo non è un problema perchè esistono [ottime librerie](https://facebook.github.io/immutable-js/) che permettono di utilizzare questo approccio senza dispendio di memoria e più velocemente rispetto al dovelo fare manualmente. - -**Da evitare** -```javascript -const addItemToCart = (cart, item) => { - cart.push({ item, date: Date.now() }); -}; -``` - -**Bene:** -```javascript -const addItemToCart = (cart, item) => { - return [...cart, { item, date: Date.now() }]; -}; -``` - -**[⬆ torna su](#lista-dei-contenuti)** - -### Non aggiungere funzioni globali -Contaminare o aggiungere delle variabili globali è una pratica sconsigliata, in quanto potresti entrare in conflitto con altre librerie e chi utiliza le tue API potrebbe non accorgersene fintanto che non si trova in produzione, generando un'eccezione. -Facciamo un esempio pratico: supponiamo che tu voglia estendere il costrutto Array nativo di JavaScript aggiungendo il metodo `diff` che mostra le differenze tra due array. Come puoi fare? -Potresti scrivere il metodo utilizzando `Array.prototype`, che però potrebbe entrare in conflitto con con un'altra libreria che fa la stessa cosa. Cosa succederebbe se anche l'altra libreria utilizzasse `diff` per trovare le differenze tra due array? Ecco perchè è molto meglio utilizzare le classi ES2015/ES6 e semplicemente estendere `Array`. - -**Da evitare** -```javascript -Array.prototype.diff = function diff(comparisonArray) { - const hash = new Set(comparisonArray); - return this.filter(elem => !hash.has(elem)); -}; -``` - -**Bene:** -```javascript -class SuperArray extends Array { - diff(comparisonArray) { - const hash = new Set(comparisonArray); - return this.filter(elem => !hash.has(elem)); - } -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Preferisci la programmazione funzionale a quella imperativa - -*[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale)* - -*[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa)* - -Javascript non è un linguaggio funzionale alla stregua di [Haskell](https://www.haskell.org/), ma entrambi hanno qualcosa che li accomuna. -I linguaggi funzionali generalmente sono più puliti e facili da testare. -Preferisci questo stile se possibile. - -**Da evitare** -```javascript -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; -} -``` - -**Bene:** -```javascript -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 - .map(output => output.linesOfCode) - .reduce((totalLines, lines) => totalLines + lines); -``` -**[⬆ torna su](#lista-dei-contenuti)** -### Incapsula le condizioni - -**Da evitare** -```javascript -if (fsm.state === 'fetching' && isEmpty(listNode)) { - // ... -} ``` -**Bene:** +**Good**: ```javascript -function shouldShowSpinner(fsm, listNode) { - return fsm.state === 'fetching' && isEmpty(listNode); +function createTempFile(name) { + fs.create('./temp/' + name); } -if (shouldShowSpinner(fsmInstance, listNodeInstance)) { - // ... +function createFile(name) { + fs.create(name); } ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evita di verificare condizioni in negativo - -**Da evitare** -```javascript -function isDOMNodeNotPresent(node) { - // ... -} -if (!isDOMNodeNotPresent(node)) { - // ... -} -``` +## **Comments** +### Only comment things that have business logic complexity. +Comments are an apology, not a requirement. Good code *mostly* documents itself. -**Bene:** +**Bad:** ```javascript -function isDOMNodePresent(node) { - // ... -} - -if (isDOMNodePresent(node)) { - // ... -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evita le condizioni -Sembrerebbe un task impossibile. Ad un rapido sguardo molti sviluppatori potrebbero pensare "come posso pensare di far funzionare qualcosa senza utilizzare un `if`?" -La risposta è che puoi utilizzare il polimorfismo per ottenere lo stesso risultato in molti casi. -La seconda domanda generalmente è "Ottimo! Ma perchè dovrei farlo?". -La risposta è data in uno dei concetti precedentemente descritti: una funzione dovrebbe eseguire una sola operazione. -Quando hai una Classe con delle funzioni che utilizzano lo stato `if` stai dicendo all'utente che la tua funzione può fare più di una operazione. +function hashIt(data) { + // The hash + var hash = 0; + // Length of string + var length = data.length; -**Da evitare** -```javascript -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(); - } + // Loop through every character in data + for (var i = 0; i < length; i++) { + // Get character code. + var char = i.charCodeAt(i); + // Make the hash + hash = ((hash << 5) - hash) + char; + // Convert to 32-bit integer + hash = hash & hash; } } ``` -**Bene:** +**Good**: ```javascript -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(); - } -} -``` -**[⬆ torna su](#lista-dei-contenuti)** +function hashIt(data) { + var hash = 0; + var length = data.length; -### Evita la validazione dei tipi (parte 1) -JavaScript è un linguaggio non tipizzato, il che significa che le tue funzioni possono accettare qualunque tipo di argomento. -Qualche volta potresti essere tentato da tutta questa libertà e potresti essere altrettanto tentato di veririfcare il tipo di dato ricevuto nella tua funzione. -Ci sono molti modi per evitare di dover fare questo tipo di controllo. -Come prima cosa cerca scrivere API consistenti. + for (var i = 0; i < length; i++) { + var char = i.charCodeAt(i); + hash = ((hash << 5) - hash) + char; -**Da evitare** -```javascript -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')); + // Convert to 32-bit integer + hash = hash & hash; } } -``` -**Bene:** -```javascript -function travelToTexas(vehicle) { - vehicle.move(this.currentLocation, new Location('texas')); -} ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evita la validazione dei tipi (part 2) -Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di [TypeScript](https://www.typescriptlang.org/). -È una vlidissima alternativa al normale JavaScript che fornicsce una validazione di tipi statica utilizzando la sintassi JavaScript. -Il problema con la validazione manuale dei tipi utilizzando lo standard JavaScript è che richiede tanto codice extra che non compensa la scarsa leggibilità del codice ottenuto. -Mantieni pulito il tuo codice, scrivi dei test validi e cerca di fare delle buone revisioni del coidice. Altrimenti utilizza TypeScript (che come detto in precedenza è una validissima alternativa!) - -**Da evitare** -```javascript -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'); -} -``` - -**Bene:** -```javascript -function combine(val1, val2) { - return val1 + val2; -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Non ottimizzare eccessivamente -I browser moderni eseguono un sacco di ottimizzazione "sottobanco". Molte volte, quando cerchi di ottimizzare il tuo codice, stai perdendo tempo prezioso. [Ci sono tante guide](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) che aiutano a capire quali tipi di ottimizzazione sono superflui e quali no. Utilizza queste guide nel frattempo, fintanto che queste lacune non verranno colmate. - -**Da evitare** -```javascript - -// Nei vecchi browser ogni volta che in un ciclo verifichi `list.length` potrebbe -// essere dispendioso per via del suo ricalcolo. Nei browser moderni -// questa operazione è stata ottimizzata -for (let i = 0, len = list.length; i < len; i++) { - // ... -} -``` - -**Bene:** -```javascript -for (let i = 0; i < list.length; i++) { - // ... -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Rimuovi il codice inutilizzato -Il codice inutilizzato è dannoso quanto il codice duplicato. Non c'è motivo per cui tenerlo nel tuo codebase. Se realmente non viene utilizzato rimuovilo! -Sarà comunque presente nella storia del tuo file se utilizzi un sistema di versioning nel caso in cui dovesse servirti. - -**Da evitare** -```javascript -function oldRequestModule(url) { - // ... -} - -function newRequestModule(url) { - // ... -} - -const req = newRequestModule; -inventoryTracker('apples', req, 'www.inventory-awesome.io'); - -``` - -**Bene:** -```javascript -function newRequestModule(url) { - // ... -} - -const req = newRequestModule; -inventoryTracker('apples', req, 'www.inventory-awesome.io'); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -## **Ogetti e strutture dati** -### Utilizza getter e setter -Utilizzare getter e setter per acceedere ai dati di un oggetto può essere meglio che accedere direttamente alle sue proprietà. Ti starai sicuramente chiedendo il motivo. -Eccoti qualche motivo per cui utilizzare getter e setter: -* Quando hai bisogno di eseguire un'operazione sul dato recuperato, non devi andare a modificarlo ogni volta che accedi al dato nel tuo codice. -* Valida il dato nel momento in cui lo setti con `set` -* Incapsula la rappresentazione interna -* È più facile loggare e gestire gli errori quando imposti o recuperi un dato -* Puoi caricare i dati del tuo oggetto in modalità *lazy*, per esempio caricandoli dal server. - - -**Da evitare** -```javascript -function makeBankAccount() { - // ... - - return { - balance: 0, - // ... - }; -} - -const account = makeBankAccount(); -account.balance = 100; -``` - -**Bene:** -```javascript -function makeBankAccount() { - // questa proprietà è privata - let balance = 0; - - // un "getter", la rende pubblica restituendola in questo modo - function getBalance() { - return balance; - } - - // un "setter", la rende pubblica in questo modo - function setBalance(amount) { - // ... e la valida prima di impostarla - balance = amount; - } - - return { - // ... - getBalance, - setBalance, - }; -} - -const account = makeBankAccount(); -account.setBalance(100); -``` -**[⬆ torna su](#lista-dei-contenuti)** - - -### Imposta proprietà private in un oggetto -Può essere fatto attraverso le *closure* (per ES5 e versioni precedenti). - -**Da evitare** -```javascript - -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 -``` - -**Bene:** -```javascript -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 -``` -**[⬆ torna su](#lista-dei-contenuti)** - - -## **Classi** -### Utilizza le classi ES2015/ES6 piuttosto che le funzioni di ES5 -È molto difficile ottenere leggibilità sull'ereditarietà, costrutti e metodi in definizioni di oggetti in ES5. Se hai bisogno di ereditarietà (e bada bene, non è detto che tu ne abbia bisogno), utilizza il costrutto Class di ES2015/ES6. Altrimenti utilizza piccole funzioni fintanto che non avrai bisogno di gestire oggetti più complessi. - -**Da evitare** -```javascript -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() {}; -``` - -**Bene:** -```javascript -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() { /* ... */ } -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - - -### Concatena i metodi -Questo pattern è molto utilizzato in JavaScript e puoi trovarne applicazione in molte liberie come [jQuery](https://jquery.com/) e [Lodash](https://lodash.com/). Permette al tuo codice di essere maggiormente espressivo e meno verboso. -Proprio per questo motivo, insisto, utilizza la concatenazione dei metodi e guarda come può essere più pulito il tuo codice. Nei metodi della tua classe, semplicemente restituisci il riferimento `this` alla fine di ogni metodo, in modo da poter concatenare altri metodi. - - -**Da evitare** -```javascript -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(); -``` - -**Bene:** -```javascript -class Car { - constructor(make, model, color) { - this.make = make; - this.model = model; - this.color = color; - } - - setMake(make) { - this.make = make; - // NOTA: restituisci this per poter concatenare altri metodi. - return this; - } - - setModel(model) { - this.model = model; - // NOTA: restituisci this per poter concatenare altri metodi. - return this; - } - - setColor(color) { - this.color = color; - // NOTA: restituisci this per poter concatenare altri metodi. - return this; - } - - save() { - console.log(this.make, this.model, this.color); - // NOTA: restituisci this per poter concatenare altri metodi. - return this; - } -} - -const car = new Car('Ford','F-150','red') - .setColor('pink') - .save(); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Preferisci una struttura compositiva all'ereditarietà -Come dichiarato dalla Gang of four in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) dovresti preferire la strutturazione all'ereditarietà quando puoi. Ci sono validi motivi per utilizzare l'ereditarietà e altrettanto validi motivi per utilizzare la strutturazione. -Il punto principale di questo assunto è che mentalmente sei portato a preferire l'ereditarietà. Prova a pensare alla strutturazione per risolvere il tuo problema: tante volte è davvero la soluzione migliore. -Ti potresti chiedere: "Quando dovrei utilizzare l'ereditarietà?". Dipende dal problema che devi affrontare, ma c'è una discreta lista di suggerimenti che ti potrebbero aiutare a capire quando l'ereditarietà è meglio della strutturazione: - -1. L'estensione che stai mettendo in atto rappresenta una relazione di tipo "è-un" e non "ha-un" (Umano->Animale vs. Utente->DettagliUtente). -2. Puoi riutilizzare il codice dalla classe padre -3. Vuoi fare cambiamenti globali a tutte le classi estese tramite la classe di partenza. - -**Da evitare** -```javascript -class Employee { - constructor(name, email) { - this.name = name; - this.email = email; - } - - // ... -} - -// Male 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; - } - - // ... -} -``` - -**Bene:** -```javascript -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); - } - // ... -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -## **SOLID** -### Single Responsibility Principle (SRP) (Principio di singola responsabilità) -Come indicato in *Clean code*, "Non dovrebbe mai esserci più di un solo motivo per modificare una classe". La tentazione è sempre quella di fare un'unica classe con molte funzionalità, come quando vuoi portarti un unico bagaglio a bordo. -Il problema con questo approccio è che le tue classi non saranno concettualmente coese e ti potrebbero dare più di una ragione per modificarle in seguito. -Minimizzare il numero di volte in cui modificare una classe è importante. -È importante perchè quando ci sono troppe funzionalità in una classe è difficile capire che effetto avrà sulle classe che la estendono, nel caso in cui farai un cambiamento. - -**Da evitare** -```javascript -class UserSettings { - constructor(user) { - this.user = user; - } - - changeSettings(settings) { - if (this.verifyCredentials()) { - // ... - } - } - - verifyCredentials() { - // ... - } -} -``` - -**Bene:** -```javascript -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()) { - // ... - } - } -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Open/Closed Principle (OCP) (Principio aperto/chiuso) -Come dichiarato da Bertrand Meyer, "Le entità di un software (Classi, moduli, funzioni, etc.) dovrebbero essere aperte all'estensione, ma chiuse alla modifica. Cosa significa esattamente? -Quello che intende è che dovresti dare ai tuoi utilizzatori la possibilità di aggiungere nuove funzionalità, non modificando quelle esistenti. - -**Da evitare** -```javascript -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 -} -``` - -**Bene:** -```javascript -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 - }); - } -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Liskov Substitution Principle (LSP) (Principio di sostituzione di Liskov) -Questo nome sembra molto più spaventoso di quello che in realtà significa. -Formalmente la sua defnizione è "Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S (per esempio un oggetto di tipo S può sostituire un oggetto di tipo T) senza modificare alcuna della proprietà del software (correttezza, compito da svolgere, etc.)". Questa definizione suona comunque complessa. - -Forse una spiegazione più esaustiva potrebbe essere: "Se hai una classe *figlio* che estende una classe *genitore* allora le due classi possono essere intercambiate all'interno del codice senza generare errori o risultati inattesi". -Potrebbe esssere ancora poco chiaro, ma vediamo con un esempio (Quadrato/Rettangolo): matematicamente il Quadrato è un Rettangolo, ma se il tuo modello eredita una relazione di tipo "è-un", potresti avere presto qualche problema. - -**Da evitare** -```javascript -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(); // Sbagliato: Restituisce 25 anche - // per il quadrato. Dovrebbe essere 20. - rectangle.render(area); - }); -} - -const rectangles = [new Rectangle(), new Rectangle(), new Square()]; -renderLargeRectangles(rectangles); -``` - -**Bene:** -```javascript -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); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Interface Segregation Principle (ISP) (Principio di segregazione delle interfacce) - -JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante nonostante la sua mancanza di tipizzazione. - -ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [*duck-typing*](https://it.wikipedia.org/wiki/Duck_typing) -Un buon esempio in JavaScript potrebbe essere fatto per le classi che richiedono la deinizione di un set di proprietà molto grande. -Non utilizzare classi che richiedono la definizione di molte proprietà per essere istanziate è sicuramente un beneficio perchè spesso non tutte queste proprietà richiedono di essere impostate per utilizzare la classe. -Rendere questi parametri opzionali evita di avere un'interfaccia pesante. - - -**Da evitare** -```javascript -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() {} //Il più delle volte potremmo non dover animare questo oggetto - // ... -}); - -``` - -**Bene:** -```javascript -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() {} - } -}); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Dependency Inversion Principle (DIP) (Principio di inversione delle dipendenze) -Questo principio sostanzialmente indica due cose: -1. Moduli ad alto livello non dovrebbero dipendere da molidi a basso livello. Entrambi dovrebbero dipendere da moduli astratti. -2. L'astrazione non dovrebbe dipendere da classi concrete. Le classi concrete dovrebbero dipendere da astrazioni. - -A primo impatto questo concetto potrebbe essere difficile da capire, ma nel caso in cui tu abbia lavorato con AngularJS avrai sicuramente visto l'applicazione di questo principio nel concetto di *Dependency injection (DI)*. Nonostante non sia esattamente identico come concetto, DIP evita che i moduli di alto livello conoscano i dettagli dei moduli di basso livello pur utilizzandoli. -Uno dei benefici di questo utilizzo è che riduce la dipendenza tra due moduli. -La dipendenza tra due moduli è un concetto negativo, perchè ne rende difficile il refactor. -Come detto in precedenza, non essendoci il concetto di interfaccia in JavaScript, tutte le dipendenze sono contratte implicitamente. -Nell'esempio successivo, la dipendenza implicita è che tutte le istanze di `InventoryTracker` avranno un metodo `requestItems`. - -**Da evitare** -```javascript -class InventoryRequester { - constructor() { - this.REQ_METHODS = ['HTTP']; - } - - requestItem(item) { - // ... - } -} - -class InventoryTracker { - constructor(items) { - this.items = items; - - //Da evitare: abbiamo creato una dipendenza specifica per ogni istanza. - //Dovremmo fare in modo che requestItems dipenda dal metodo `request` - - this.requester = new InventoryRequester(); - } - - requestItems() { - this.items.forEach((item) => { - this.requester.requestItem(item); - }); - } -} - -const inventoryTracker = new InventoryTracker(['apples', 'bananas']); -inventoryTracker.requestItems(); -``` - -**Bene:** -```javascript -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) { - // ... - } -} - -//Avendo dichiarato la nostra dipendenza esternamente ed aggiunta dall'esterno, la possiamo -//sostituire facilemente con un'altra che utilizza WebSockets - -const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); -inventoryTracker.requestItems(); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -## **Test** -Testare è più importante che rilasciare. Se non hai test o non ne hai un numero adeguato, non saprai se ad ogni rilascio puoi rompere qualcosa. -Decidere quale sia il numero sufficiente di test dipende dal tuo team, ma cercare di coprire il 100% dei casi (per tutti gli stati ed i branch) vuol dire avere massima tranquillità durante i rilasci. -Questo significa che oltre ad utilizzare una suite di test valida, dovresti utilizzare anche un [buon strumento di copertura](http://gotwarlost.github.io/istanbul/). - -Non ci sono scuse per non scrivere test. C'è un'abbondanza di ottimi [framewerk per i test in JavaScript](http://jstherightway.org/#Test-tools) quindi cerca quello più adatto alle tue esigenze. -Quando tu ed il tuo team avrete individuato quello più giusto per voi, dovrete iniziare sempre a scrivere i test per ogni modulo/feature che introdurrete nel vostro software. -Se il vostro approccio preferito è quello del TestDrivenDevelopment (TDD) ottimo, ma assicurati di individuare i tuoi obiettivi prima di rilasciare ogni singola feature o eseguire il refactor di una esistente. - -### Un singolo comportamento per test - -**Da evitare** -```javascript -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); - }); -}); -``` - -**Bene:** -```javascript -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); - }); -}); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -## **Consequenzialità** -### utilizza le Promise, non funzioni di callback -Le funzioni di callback non sono sempre chiare e possono generare un eccessivo numero di nidificazioni. Con ES2015/ES6 sono nativamente e globalmente accessibili. Utilizzale! - -**Da evitare** -```javascript -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'); - } - }); - } -}); - -``` - -**Bene:** -```javascript -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); - }); - -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Async/Await sono addirittura più chiari delle Promise -Le Promise sono una valida e chiara alternativa alle funzioni di callback, ma ES2017/ES8 offrono anche async and await che possono essere addirittura una soluzione più migliore. -Tutto quello che devi fare non è niente altro che scrivere una funzione che abbia prefisso `async` e puoi scrivere la tua logica senza dover concatenare con la kyword `then`. -Utilizza questo approccio se hai la possibilità di utilizzare le feature ES2017/ES8! - -**Da evitare** -```javascript -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); - }); - -``` - -**Bene:** -```javascript -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); - } -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - - -## **Gestione degli errori** -Generare errori è una buona cosa. Vuol dire che l'esecuzione del tuo codice ha identificato precisamente quando nel tuo software qualcosa è andato storto e ti permette di interromperne l'esecuzione nello stack corrente terminando il processo (in Node), e notificandolo attraverso la console. - -### Non ingnorare gli errori intercettati -Non fare niente con gli errori intercettati non rende possibile correggerli o reagire all'errore. Loggare gli errori nella console (`console.log`) non ti assicura di non perderti nel mare di log stampati in console. -Se invece inserisci il tuo codice all'interno del costrutto `try/catch` vuol dire riconoscere la possibilità che esista un errore nel caso mettere in piedi un modo per gestirlo. - -**Da evitare** -```javascript -try { - functionThatMightThrow(); -} catch (error) { - console.log(error); -} -``` - -**Bene:** -```javascript -try { - functionThatMightThrow(); -} catch (error) { - // Un'ozione (più visibile del console.log): - console.error(error); - // Un'altra opzione: - notifyUserOfError(error); - // Un'altra opzione: - reportErrorToService(error); - // Oppure usale tutte e tre! -} -``` - -### Non ignorare le Promise quando vengono rigettate -Per la stessa ragione per cui non dovresti ignorare gli errori con `try/catch`. - -**Da evitare** -```javascript -getdata() - .then((data) => { - functionThatMightThrow(data); - }) - .catch((error) => { - console.log(error); - }); -``` - -**Bene:** -```javascript -getdata() - .then((data) => { - functionThatMightThrow(data); - }) - .catch((error) => { - // Un'ozione (più visibile del console.log): - console.error(error); - // Un'altra opzione: - notifyUserOfError(error); - // Un'altra opzione: - reportErrorToService(error); - // Oppure usale tutte e tre! - }); -``` - -**[⬆ torna su](#lista-dei-contenuti)** - - -## **Formattazione** - -La formattazione è soggettiva. Come molte di quelle sopracitate, non esiste una regola assoluta e veloce che devi seguire. Il punto principale, però, è NON DISCUTERE della formattazione. -Ci sono un [sacco di strumenti](http://standardjs.com/rules.html) che automatizzano questo processo. Usane uno. È uno spreco di tempo e denaro per gli sviluppatori discutere della formattazione. - -### Utilizza le maiuscole in modo consistente - -JavaScript non è tipizzato, per questo l'uso delle maiuscole può darti indicazioni sulle tue variabili, funzioni, etc. Queste regole sono soggettive, per questo tu ed il tuo team potrete scegliere quella che volete. Il punto è: non importa quale regola sceglierete, l'importante è essere consistenti. - - -**Da evitare** -```javascript -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 {} -``` - -**Bene:** -```javascript -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 {} -``` -**[⬆ torna su](#lista-dei-contenuti)** - - -### Richiami e dichiarazioni di funzioni dovrebbero essere vicini -Se una funzione ne richiama un'altra, mantieni queste funzioni verticalmente vicine nel sorgente. Idealmente, mantieni il richiamo subito sopra la dichiarazione. -Generalmente tendiamo a leggere il codice dall'alto verso il basso, come un giornale. Proprio per questo manteniamolo leggibile seguendo questa modalità. - -**Da evitare** -```javascript -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(); -``` - -**Bene:** -```javascript -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(); -``` - -**[⬆ torna su](#lista-dei-contenuti)** - -## **Commento** -### Commenta solo il codice che ha un alto livello di complessità -Commentare è una scusa, non un requisito. Un buon codice *spesso* si spiega da solo. -**Da evitare** -```javascript -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; - } -} -``` - -**Bene:** -```javascript - -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; - } -} - -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Non lasciare parti del tuo codice commentate all'interno dei sorgenti -I sistemi di versioning esistono per un motivo. Lascia il tuo codice vecchio alla storia. - -**Da evitare** -```javascript -doStuff(); -// doOtherStuff(); -// doSomeMoreStuff(); -// doSoMuchStuff(); -``` - -**Bene:** -```javascript -doStuff(); -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Non utilizzare i commenti come un diario -Ricordati di usare sistemi di version control. Non c'è motivo per cui codice non utilizzato, codice commentato e specialmente commenti con riferimenti a date esistano nel tuo file. -Usa `git log` per avere lo storico! - -**Da evitare** -```javascript -/** - * 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; -} -``` - -**Bene:** -```javascript -function combine(a, b) { - return a + b; -} -``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evita di specificare di cosa si tratta -Generalmente è solo fastidioso. Lascia che le tue funzioni e le tue variabili, insieme ad una corretta indentazioni ti diano una struttura visiva del tuo codice. - -**Da evitare** -```javascript -//////////////////////////////////////////////////////////////////////////////// -// Scope Model Instantiation -//////////////////////////////////////////////////////////////////////////////// -$scope.model = { - menu: 'foo', - nav: 'bar' -}; - -//////////////////////////////////////////////////////////////////////////////// -// Action setup -//////////////////////////////////////////////////////////////////////////////// -const actions = function() { - // ... -}; -``` - -**Bene:** -```javascript -$scope.model = { - menu: 'foo', - nav: 'bar' -}; - -const actions = function() { - // ... -}; -``` -**[⬆ torna su](#lista-dei-contenuti)** - -## Traduzioni - -Questa guida è disponibile in altre lingue: - - - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: - - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) - - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) - - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) - - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) - - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) - - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: - - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) - - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) - - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) - - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: - [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italiano**: - [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) - -**[⬆ torna su](#lista-dei-contenuti)** From f6ba6edaf585f1790e8c994ba4206258e4a8c009 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 21 Nov 2016 21:48:09 -0800 Subject: [PATCH 035/170] Default args --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 33aa78fd..47eba51c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ getUser(); ## **Functions** ### Limit the amount of function parameters (2 or less) -Use an object if you are finding yourself needing a lot of parameters +Use an object if you are finding yourself needing a lot of parameters. **Bad:** ```javascript @@ -54,6 +54,24 @@ function createMenu(config) { ``` +### Use default arguments instead of short circuiting +**Bad:** +```javascript +function writeForumComment(subject, body) { + subject = subject || 'No Subject'; + body = body || 'No text'; +} +``` + +**Good**: +```javascript +function writeForumComment(subject='No subject', body='No text') { + ... +} + +``` + + ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. From 739f00029771eed5205a38442c6a6ae7dc00306b Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 25 Nov 2016 14:54:58 -0800 Subject: [PATCH 036/170] Avoid side effects --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index 47eba51c..4b98f740 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,42 @@ function createFile(name) { } ``` + +### Avoid Side Effects +A function produces a side effect if it does anything other than take a value in +and return another value or values. A side effect could be writing to a file, +modifying some global variable, or accidentally wiring all your money to a +Nigerian prince. + +**Bad:** +```javascript +// 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. +var name = 'Ryan McDermott'; + +function splitIntoFirstAndLastName() { + name = name.split(' '); +} + +console.log(name); // ['Ryan', 'McDermott']; + +``` + +**Good**: +```javascript +function splitIntoFirstAndLastName(name) { + return name.split(' '); +} + +var name = 'Ryan McDermott' +var newName = splitIntoFirstAndLastName(name); + +console.log(name); // 'Ryan McDermott'; +console.log(newName); // ['Ryan', 'McDermott']; +``` + + + ## **Comments** ### Only comment things that have business logic complexity. Comments are an apology, not a requirement. Good code *mostly* documents itself. From 135c2486dc25448495acbbf837977377acbf39ba Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sat, 26 Nov 2016 15:31:57 -0800 Subject: [PATCH 037/170] Searchable names --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 4b98f740..674acf7e 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,26 @@ getCustomerRecord(); getUser(); ``` +### Use searchable names +We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By *not* naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. + +**Bad:** +```javascript +// What the heck is 525600 for? +for (var i = 0; i < 525600; i++) { + runCronJob(); +} +``` + +**Good**: +```javascript +// Declare them as capitalized `var` globals. +var MINUTES_IN_A_YEAR = 525600; +for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { + runCronJob(); +} +``` + ## **Functions** ### Limit the amount of function parameters (2 or less) Use an object if you are finding yourself needing a lot of parameters. From 74de217c4c270c687f478775340d35e10f2f08e7 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sat, 26 Nov 2016 15:39:37 -0800 Subject: [PATCH 038/170] Avoid mental mapping --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 674acf7e..5cd87045 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,36 @@ for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { } ``` +### Avoid Mental Mapping +Explicit is better than implicit. + +**Bad:** +```javascript +var locations = ['Austin', 'New York', 'San Francisco']; +locations.forEach((l) => { + doStuff(); + doSomeOtherStuff(); + ... + ... + ... + // Wait, what is `l` for again? + dispatch(l); +}); +``` + +**Good**: +```javascript +var locations = ['Austin', 'New York', 'San Francisco']; +locations.forEach((location) => { + doStuff(); + doSomeOtherStuff(); + ... + ... + ... + dispatch(location); +}); +``` + ## **Functions** ### Limit the amount of function parameters (2 or less) Use an object if you are finding yourself needing a lot of parameters. From 1ead9f707358a6673c99449c0dc89071c5eab2c2 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 27 Nov 2016 09:00:03 -0800 Subject: [PATCH 039/170] Commented code --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 5cd87045..1a9278ca 100644 --- a/README.md +++ b/README.md @@ -226,3 +226,19 @@ function hashIt(data) { } ``` + +### Don't leave commented code in your codebase +Version control exists for a reason. Leave old code in your history. + +**Bad:** +```javascript +doStuff(); +// doOtherStuff(); +// doSomeMoreStuff(); +// doSoMuchStuff(); +``` + +**Good**: +```javascript +doStuff(); +``` From b758ac9886a9fed6a075f435d660808e2143f00f Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 27 Nov 2016 09:22:27 -0800 Subject: [PATCH 040/170] Capitalization --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 1a9278ca..b640952e 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,41 @@ locations.forEach((location) => { }); ``` +### Use consistent capitalization +JavaScript is untyped, so capitalization tells you a lot about your variables, +functions, etc. These rules are subjective, so your team can choose whatever +they want. The point is, no matter what you all choose, just be consistent. + +**Bad:** +```javascript +var DAYS_IN_WEEK = 7; +var daysInMonth = 30; + +var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; + +function eraseDatabase() {} +function restore_database() {} + +class animal {} +class Alpaca {} +``` + +**Good**: +```javascript +var DAYS_IN_WEEK = 7; +var DAYS_IN_MONTH = 30; + +var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +var artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; + +function eraseDatabase() {} +function restoreDatabase() {} + +class Animal {} +class Alpaca {} +``` + ## **Functions** ### Limit the amount of function parameters (2 or less) Use an object if you are finding yourself needing a lot of parameters. From 82622c0f4cbb98f607a78f99bb5e045a64f62b82 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 27 Nov 2016 09:54:27 -0800 Subject: [PATCH 041/170] Don't add unneeded context --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index b640952e..7d6ad54f 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,36 @@ class Animal {} class Alpaca {} ``` +### Don't add unneeded context +If your class/object name tells you something, don't repeat that in your +variable name. + +**Bad:** +```javascript +var Car = { + carMake: 'Honda', + carModel: 'Accord', + carColor: 'Blue' +}; + +function paintCar(car) { + car.carColor = 'Red'; +} +``` + +**Good**: +```javascript +var Car = { + make: 'Honda', + model: 'Accord', + color: 'Blue' +}; + +function paintCar(car) { + car.color = 'Red'; +} +``` + ## **Functions** ### Limit the amount of function parameters (2 or less) Use an object if you are finding yourself needing a lot of parameters. From c62e62d6f364f30d5e58788b17fd97a13c03df31 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 4 Dec 2016 19:36:37 -0800 Subject: [PATCH 042/170] ES6 Classes --- README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d6ad54f..4e80d7ce 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,6 @@ function writeForumComment(subject='No subject', body='No text') { ``` - ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. @@ -213,7 +212,6 @@ function createFile(name) { } ``` - ### Avoid Side Effects A function produces a side effect if it does anything other than take a value in and return another value or values. A side effect could be writing to a file, @@ -247,7 +245,80 @@ console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` +## **Classes** +### Prefer ES6 classes over ES5 plain functions +It's very difficult to get readable class inheritance, construction, and method +definitions for classical ES5 classes. If you need inheritance (and be aware +that you might not), then prefer classes. However, prefer small functions over +classes until you find yourself needing larger and more complex objects. + +**Bad:** +```javascript +var Animal = function(age) { + if (!(this instanceof Animal)) { + throw new Error("Instantiate Animal with `new`"); + } + + this.age = age; +}; + +Animal.prototype.move = function() {}; + +var 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() {}; + +var 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() {}; +``` + +**Good:** +```javascript +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() {} +} +``` ## **Comments** ### Only comment things that have business logic complexity. From c78e34b0e3aa509954693801e5104d19fd554d8f Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 5 Dec 2016 22:29:33 -0800 Subject: [PATCH 043/170] No messing with globals --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/README.md b/README.md index 4e80d7ce..09aee84e 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,63 @@ console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` +### Don't write to global functions +Polluting globals is a bad practice in JavaScript because you could clash with another +library and the user of your API would be none-the-wiser until they get an +exception in production. Let's think about an example: what if you wanted to +extend JavaScript's native Array method to have a `diff` method that could +show the difference between two arrays? You could write your new function +to the `Array.prototype`, but it could clash with another library that tried +to do the same thing. What if that other library was just using `diff` to find +the difference between the first and last elements of an array? This is why it +would be much better to just use ES6 classes and simply extend the `Array` global. + +**Bad:** +```javascript +Array.prototype.diff = function(comparisonArray) { + var values = []; + var hash = {}; + + for (var i of comparisonArray) { + hash[i] = true; + } + + for (var i of this) { + if (!hash[i]) { + values.push(i); + } + } + + return values; +} +``` + +**Good:** +```javascript +class SuperArray extends Array { + constructor(...args) { + super(...args); + } + + diff(comparisonArray) { + var values = []; + var hash = {}; + + for (var i of comparisonArray) { + hash[i] = true; + } + + for (var i of this) { + if (!hash[i]) { + values.push(i); + } + } + + return values; + } +} +``` + ## **Classes** ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method From 6bae6e4a62aa8653a534d163b137a2d85bda9f51 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 5 Dec 2016 22:36:51 -0800 Subject: [PATCH 044/170] TOC --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 09aee84e..2e76a165 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # clean-code-javascript -Software engineering principles, from Robert C. Martin's wonderful book *Clean Code*, adapted for JavaScript. +Software engineering principles, from Robert C. Martin's wonderful book +*Clean Code*, adapted for JavaScript. This is not a style guide, it's something +much more. It's a guide to producing readable, reusable, and refactorable +JavaScript software. Enjoy! + +## Table of Contents + 1. [Variables](#variables) + 2. [Functions](#functions) + 3. [Classes](#classes) + 4. [Comments](#comments) ## **Variables** ### Use meaningful and pronounceable variable names From 86fc8c7228b350130db6babab4bc6384ec52ef86 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 5 Dec 2016 22:40:48 -0800 Subject: [PATCH 045/170] Test back to top --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2e76a165..4b6f1e6b 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ getCustomerRecord(); getUser(); ``` +**[⬆ back to top](#table-of-contents)** + ### Use searchable names We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By *not* naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. From 5302a3678d96706c386c04c8056069a1e2766ccf Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 5 Dec 2016 22:44:18 -0800 Subject: [PATCH 046/170] Back to top links --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b6f1e6b..89c35be2 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ var yyyymmdstr = moment().format('YYYY/MM/DD'); ```javascript var yearMonthDay = moment().format('YYYY/MM/DD'); ``` +**[⬆ back to top](#table-of-contents)** ### Use the same vocabulary for the same type of variable @@ -36,7 +37,6 @@ getCustomerRecord(); ```javascript getUser(); ``` - **[⬆ back to top](#table-of-contents)** ### Use searchable names @@ -58,6 +58,7 @@ for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { runCronJob(); } ``` +**[⬆ back to top](#table-of-contents)** ### Avoid Mental Mapping Explicit is better than implicit. @@ -88,6 +89,7 @@ locations.forEach((location) => { dispatch(location); }); ``` +**[⬆ back to top](#table-of-contents)** ### Use consistent capitalization JavaScript is untyped, so capitalization tells you a lot about your variables, @@ -123,6 +125,7 @@ function restoreDatabase() {} class Animal {} class Alpaca {} ``` +**[⬆ back to top](#table-of-contents)** ### Don't add unneeded context If your class/object name tells you something, don't repeat that in your @@ -153,6 +156,7 @@ function paintCar(car) { car.color = 'Red'; } ``` +**[⬆ back to top](#table-of-contents)** ## **Functions** ### Limit the amount of function parameters (2 or less) @@ -179,6 +183,7 @@ function createMenu(config) { } ``` +**[⬆ back to top](#table-of-contents)** ### Use default arguments instead of short circuiting **Bad:** @@ -187,6 +192,7 @@ function writeForumComment(subject, body) { subject = subject || 'No Subject'; body = body || 'No text'; } + ``` **Good**: @@ -196,6 +202,7 @@ function writeForumComment(subject='No subject', body='No text') { } ``` +**[⬆ back to top](#table-of-contents)** ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. @@ -209,7 +216,6 @@ function createFile(name, temp) { fs.create(name); } } - ``` **Good**: @@ -222,6 +228,7 @@ function createFile(name) { fs.create(name); } ``` +**[⬆ back to top](#table-of-contents)** ### Avoid Side Effects A function produces a side effect if it does anything other than take a value in @@ -255,6 +262,7 @@ var newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` +**[⬆ back to top](#table-of-contents)** ### Don't write to global functions Polluting globals is a bad practice in JavaScript because you could clash with another @@ -312,6 +320,7 @@ class SuperArray extends Array { } } ``` +**[⬆ back to top](#table-of-contents)** ## **Classes** ### Prefer ES6 classes over ES5 plain functions @@ -387,6 +396,7 @@ class Human extends Mammal { speak() {} } ``` +**[⬆ back to top](#table-of-contents)** ## **Comments** ### Only comment things that have business logic complexity. @@ -430,6 +440,7 @@ function hashIt(data) { } ``` +**[⬆ back to top](#table-of-contents)** ### Don't leave commented code in your codebase Version control exists for a reason. Leave old code in your history. @@ -446,3 +457,4 @@ doStuff(); ```javascript doStuff(); ``` +**[⬆ back to top](#table-of-contents)** From 0d7f5c696c9441358fca913a44e94195ab0b766f Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 8 Dec 2016 21:34:56 -0800 Subject: [PATCH 047/170] Use promises --- README.md | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 89c35be2..950af28d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ JavaScript software. Enjoy! 1. [Variables](#variables) 2. [Functions](#functions) 3. [Classes](#classes) - 4. [Comments](#comments) + 4. [Concurrency](#concurrency) + 5. [Comments](#comments) ## **Variables** ### Use meaningful and pronounceable variable names @@ -398,6 +399,47 @@ class Human extends Mammal { ``` **[⬆ back to top](#table-of-contents)** +## **Concurrency** +### Use Promises, not callbacks +Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, +Promises are a built-in global type. Use them! + +**Bad:** +```javascript +require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', function(err, response) { + if (err) { + console.error(err); + } + else { + require('fs').writeFile('article.html', response.body, function(err) { + if (err) { + console.error(err); + } else { + console.log('File written'); + } + }) + } +}) + +``` + +**Good**: +```javascript +require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') + .then(function(response) { + return require('fs-promise').writeFile('article.html', response); + }) + .then(function() { + console.log('File written'); + }) + .catch(function(err) { + console.log(err); + }) + +``` +**[⬆ back to top](#table-of-contents)** + + ## **Comments** ### Only comment things that have business logic complexity. Comments are an apology, not a requirement. Good code *mostly* documents itself. From 05f136b70df8d919755fa81f978acb62146e6dcc Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 8 Dec 2016 21:42:25 -0800 Subject: [PATCH 048/170] Async and Await --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index 950af28d..bc32dc2a 100644 --- a/README.md +++ b/README.md @@ -439,6 +439,45 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti ``` **[⬆ back to top](#table-of-contents)** +### Async/Await are even cleaner than Promises +Promises are a very clean alternative to callbacks, but ES7 brings async and await +which offer an even cleaner solution. All you need is a function that is prefixed +in an `async` keyword, and then you can write your logic imperatively without +a `then` chain of functions. Use this if you can take advantage of ES7 features +today! + +**Bad:** +```javascript +require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') + .then(function(response) { + return require('fs-promise').writeFile('article.html', response); + }) + .then(function() { + console.log('File written'); + }) + .catch(function(err) { + console.log(err); + }) + +``` + +**Good**: +```javascript +async function getCleanCodeArticle() { + try { + var request = await require('request-promise') + var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); + var fileHandle = await require('fs-promise'); + + await fileHandle.writeFile('article.html', response); + console.log('File written'); + } catch(err) { + console.log(err); + } + } +``` +**[⬆ back to top](#table-of-contents)** + ## **Comments** ### Only comment things that have business logic complexity. From 4b4746f1b9b49ea6480485c6ee7cadda02334677 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 8 Dec 2016 22:02:20 -0800 Subject: [PATCH 049/170] Use functional programming --- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bc32dc2a..218c0d3f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # clean-code-javascript -Software engineering principles, from Robert C. Martin's wonderful book -*Clean Code*, adapted for JavaScript. This is not a style guide, it's something -much more. It's a guide to producing readable, reusable, and refactorable -JavaScript software. Enjoy! +Software engineering principles, from Robert C. Martin's book +[*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), +adapted for JavaScript. This is not a style guide. It's a guide to producing +readable, reusable, and refactorable software in JavaScript. Enjoy! ## Table of Contents 1. [Variables](#variables) @@ -323,6 +323,62 @@ class SuperArray extends Array { ``` **[⬆ back to top](#table-of-contents)** +### Favor functional programming over imperative programming +If Haskell were an IPA then JavaScript would be an O'Douls. That is to say, +JavaScript isn't a functional language in the way that Haskell is, but it has +a functional flavor to it. Functional languages are cleaner and easier to test. +Favor this style of programming when you can. + +**Bad:** +```javascript +const programmerOutput = [ + { + name: 'Uncle Bobby', + linesOfCode: 500 + }, { + name: 'Suzie Q', + linesOfCode: 1500 + }, { + name: 'Jimmy Gosling', + linesOfCode: 150 + }, { + name: 'Gracie Hopper', + linesOfCode: 1000 + } +]; + +var totalOutput = 0; + +for (var i = 0; i < programmerOutput.length; i++) { + totalOutput += programmerOutput[i].linesOfCode; +} +``` + +**Good**: +```javascript +const programmerOutput = [ + { + name: 'Uncle Bobby', + linesOfCode: 500 + }, { + name: 'Suzie Q', + linesOfCode: 1500 + }, { + name: 'Jimmy Gosling', + linesOfCode: 150 + }, { + name: 'Gracie Hopper', + linesOfCode: 1000 + } +]; + +var totalOutput = programmerOutput + .map((programmer) => programmer.linesOfCode) + .reduce((acc, linesOfCode) => acc + linesOfCode, 0); +``` +**[⬆ back to top](#table-of-contents)** + + ## **Classes** ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method From 6bfc5d70401aef23213d49c43da247be08f72517 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 9 Dec 2016 19:43:01 -0800 Subject: [PATCH 050/170] Explain more about side effects --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 218c0d3f..1ee22fc5 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,16 @@ and return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a Nigerian prince. +Now, you do need to have side effects in a program on occasion. Like the previous +example, you might need to write to a file. What you want to do is to +centralize where you are doing this. Don't have several functions and classes +that write to a particular file. Have one service that does it. One and only one. + +The main point is to avoid common pitfalls like sharing state between objects +without any structure, using mutable data types that can be written to by anything, +and not centralizing where your side effects occur. If you can do this, you will +be happier than the vast majority of other programmers. + **Bad:** ```javascript // Global variable referenced by following function. From 58ccd78d10b470eb1e7111e85bb50e51cc627b62 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 9 Dec 2016 19:51:41 -0800 Subject: [PATCH 051/170] Object.assign --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/README.md b/README.md index 1ee22fc5..e81e383a 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,52 @@ function writeForumComment(subject='No subject', body='No text') { ``` **[⬆ back to top](#table-of-contents)** +### Set default objects with Object.assign + +**Bad:** +```javascript +var 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.title || 'Baz' + config.cancellable = config.cancellable === undefined ? config.cancellable : true; + +} + +createMenu(menuConfig); +``` + +**Good**: +```javascript +var menuConfig = { + title: null, + body: 'Bar', + buttonText: null + cancellable: true +} + +function createMenu(config) { + Object.assign(config, { + title: 'Foo', + body: 'Bar', + buttonText: 'Baz', + cancellable: true + }); +} + +createMenu(menuConfig); +``` +**[⬆ back to top](#table-of-contents)** + + + ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. From d72c99f0073c06c2c34bc86832dc447c88c0b518 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 11 Dec 2016 20:46:28 -0800 Subject: [PATCH 052/170] Avoid conditionals --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index e81e383a..fc7b4c74 100644 --- a/README.md +++ b/README.md @@ -434,6 +434,64 @@ var totalOutput = programmerOutput ``` **[⬆ back to top](#table-of-contents)** +### Avoid conditionals +This seems like an impossible task. Upon first hearing this, most people say, +"how am I supposed to do anything without an `if` statement?" The answer is that +you can use polymorphism to achieve the same task in many cases. The second +question is usually, "well that's great but why would I want to do that?" The +answer is a previous clean code concept we learned: a function should only do +one thing. When you have classes and functions that have `if` statements, you +are telling your user that your function does more than one thing. Remember, +just do one thing. + +**Bad:** +```javascript +class Airplane { + //... + getCruisingAltitude() { + switch (this.type) { + case '777': + return getMaxAltitude() - getPassengerCount(); + case 'Air Force One': + return getMaxAltitude(); + case 'Cesna': + return getMaxAltitude() - getFuelExpenditure(); + } + } +} +``` + +**Good**: +```javascript +class Airplane { + //... +} + +class Boeing777 extends Airplane { + //... + getCruisingAltitude() { + return getMaxAltitude() - getPassengerCount(); + } +} + +class AirForceOne extends Airplane { + //... + getCruisingAltitude() { + return getMaxAltitude(); + } +} + +class Cesna extends Airplane { + //... + getCruisingAltitude() { + return getMaxAltitude() - getFuelExpenditure(); + } +} +``` + +maxAltitude = plane.getMaxAltitude(); +**[⬆ back to top](#table-of-contents)** + ## **Classes** ### Prefer ES6 classes over ES5 plain functions From e82719d723350d938690860519f16cdf47e1df58 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Tue, 13 Dec 2016 21:51:02 -0800 Subject: [PATCH 053/170] Functions should do one thing --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index fc7b4c74..72845207 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,48 @@ function createMenu(config) { ``` **[⬆ back to top](#table-of-contents)** + +### Functions should do one thing +This is by far the most important rule in software engineering. When functions +do more than one thing, they are harder to compose, test, and reason about. +When you can isolate a function to just one action, they can be refactored +easily and your code will read much cleaner. If you take nothing else away from +this guide other than this, you'll be ahead of many developers. + +**Bad:** +```javascript +function emailClients(clients) { + clients.forEach(client => { + let clientRecord = database.lookup(client); + if (clientRecord.isActive()) { + email(client); + } + }); +} +``` + +**Good**: +```javascript +function emailClients(clients) { + clients.forEach(client => { + emailClientIfNeeded(); + }); +} + +function emailClientIfNeeded(client) { + if (isClientActive(client)) { + email(client); + } +} + +function isClientActive(client) { + let clientRecord = database.lookup(client); + return clientRecord.isActive(); +} +``` +**[⬆ back to top](#table-of-contents)** + + ### Use default arguments instead of short circuiting **Bad:** ```javascript From db6eece21cf22507db9c4b555b0e777fc6c6c066 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Tue, 13 Dec 2016 22:04:16 -0800 Subject: [PATCH 054/170] No duplicate code --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/README.md b/README.md index 72845207..4a063a86 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,72 @@ function isClientActive(client) { ``` **[⬆ back to top](#table-of-contents)** +### Remove duplicate code +Never ever, ever, under any circumstance, have duplicate code. There's no reason +for it and it's quite possibly the worst sin you can commit as a professional +developer. Duplicate code means there's more than one place to alter something +if you need to change some logic. JavaScript is untyped, so it makes having +generic functions quite easy. Take advantage of that! + +**Bad:** +```javascript +function showDeveloperList(developers) { + developers.forEach(developers => { + var expectedSalary = developer.calculateExpectedSalary(); + var experience = developer.getExperience(); + var githubLink = developer.getGithubLink(); + var data = { + expectedSalary: expectedSalary, + experience: experience, + githubLink: githubLink + }; + + render(data); + }); +} + +function showManagerList(managers) { + managers.forEach(manager => { + var expectedSalary = manager.calculateExpectedSalary(); + var experience = manager.getExperience(); + var portfolio = manager.getMBAProjects(); + var data = { + expectedSalary: expectedSalary, + experience: experience, + portfolio: portfolio + }; + + render(data); + }); +} +``` + +**Good**: +```javascript +function showList(employees) { + employees.forEach(employee => { + var expectedSalary = employee.calculateExpectedSalary(); + var experience = employee.getExperience(); + var portfolio; + + if (employee.type === 'manager') { + portfolio = employee.getMBAProjects(); + } else { + portfolio = employee.getGithubLink(); + } + + var favoriteManagerBooks = getMBAList() + var data = { + expectedSalary: expectedSalary, + experience: experience, + portfolio: portfolio + }; + + render(data); + }); +} +``` +**[⬆ back to top](#table-of-contents)** ### Use default arguments instead of short circuiting **Bad:** From de899e6183e94d91c6c6360e8dec1d3f3a0a8ca2 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 15 Dec 2016 22:53:49 -0800 Subject: [PATCH 055/170] Avoid type-checking part 1 --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a063a86..3f7f786f 100644 --- a/README.md +++ b/README.md @@ -596,11 +596,37 @@ class Cesna extends Airplane { } } ``` +**[⬆ back to top](#table-of-contents)** + -maxAltitude = plane.getMaxAltitude(); +### Avoid type-checking (part 1) +JavaScript is untyped, which means your functions can take any type of argument. +Sometimes you are bitten by this freedom and it becomes tempting to do +type-checking in your functions. There are many ways to avoid having to do this. +The first thing to consider is consistent APIs. + +**Bad:** +```javascript +function travelToTexas(vehicle) { + if (obj instanceof Bicylce) { + vehicle.peddle(this.currentLocation, new Location('texas')); + } else if (obj instanceof Car) { + vehicle.drive(this.currentLocation, new Location('texas')); + } +} +``` + +**Good**: +```javascript +function travelToTexas(vehicle) { + vehicle.move(this.currentLocation, new Location('texas')); +} +``` **[⬆ back to top](#table-of-contents)** + + ## **Classes** ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method From 286a3f6a9584f9f5566ea3202c6de40d01546f5e Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 18 Dec 2016 22:32:13 -0800 Subject: [PATCH 056/170] Avoid type-checking (part 2) --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 3f7f786f..dfa84c3b 100644 --- a/README.md +++ b/README.md @@ -624,6 +624,36 @@ function travelToTexas(vehicle) { ``` **[⬆ back to top](#table-of-contents)** +### Avoid type-checking (part 2) +If you are working with basic primitive values like strings, integers, and arrays, +and you can't use polymorphism but you still feel the need to type-check, +you should consider using TypeScript. It is an excellent alternative to normal +JavaScript, as it provides you with static typing on top of standard JavaScript +syntax. The problem with manually type-checking normal JavaScript is that +doing it well requires so much extra verbiage that the faux "type-safety" you get +doesn't make up for the lost readability. Keep your JavaScript, clean, write +good tests, and have good code reviews. Otherwise, do all of that but with +TypeScript (which, like I said, is a great alternative!). + +**Bad:** +```javascript +function combine(val1, val2) { + if (typeof val1 == "number" && typeof val2 == "number" || + typeof val1 == "string" && typeof val2 == "string") { + return val1 + val2; + } else { + throw new Error('Must be of type String or Number'); + } +} +``` + +**Good**: +```javascript +function combine(val1, val2) { + return val1 + val2; +} +``` +**[⬆ back to top](#table-of-contents)** From c7bcc09b60b2107d051e2ee3c8238795a014570f Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 19 Dec 2016 22:56:44 -0800 Subject: [PATCH 057/170] Method chaining --- README.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/README.md b/README.md index dfa84c3b..4ce51772 100644 --- a/README.md +++ b/README.md @@ -733,6 +733,90 @@ class Human extends Mammal { ``` **[⬆ back to top](#table-of-contents)** + +### Use method chaining +Against the advice of Clean Code, this is one place where we will have to differ. +It has been argued that method chaining is unclean and violates the (Law of Demeter) +[https://en.wikipedia.org/wiki/Law_of_Demeter]. Maybe it's true, but this pattern +is very useful in JavaScript and you see it in many libraries such as jQuery and +Lodash. It allows your code to be expressive, and less verbose. For that reason, +I say, use method chaining and take a look at how clean your code will be. +In your class functions, simply return `this` at the end of every function, and +you can chain further class methods onto it. + +**Bad:** +```javascript +class Car { + constructor() { + this.make = 'Honda'; + this.model = 'Accord'; + this.color = 'white'; + } + + setMake(make) { + this.name = name; + } + + setModel(model) { + this.model = model; + } + + setColor(color) { + this.color = color; + } + + save() { + console.log(this.make, this.model, this.color); + } +} + +let car = new Car(); +car.setColor('pink'); +car.setMake('Ford'); +car.setModel('F-150') +car.save(); +``` + +**Good**: +```javascript +class Car { + constructor() { + this.make = 'Honda'; + this.model = 'Accord'; + this.color = 'white'; + } + + setMake(make) { + this.name = name; + // 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); + } +} + +let car = new Car() + .setColor('pink') + .setMake('Ford') + .setModel('F-150') + .save(); +``` +**[⬆ back to top](#table-of-contents)** + ## **Concurrency** ### Use Promises, not callbacks Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, From 6788e1d0fd783895b110a817b504f7d42cf7f6cd Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 19 Dec 2016 22:59:05 -0800 Subject: [PATCH 058/170] Fix link --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4ce51772..f6cd3272 100644 --- a/README.md +++ b/README.md @@ -736,13 +736,12 @@ class Human extends Mammal { ### Use method chaining Against the advice of Clean Code, this is one place where we will have to differ. -It has been argued that method chaining is unclean and violates the (Law of Demeter) -[https://en.wikipedia.org/wiki/Law_of_Demeter]. Maybe it's true, but this pattern -is very useful in JavaScript and you see it in many libraries such as jQuery and -Lodash. It allows your code to be expressive, and less verbose. For that reason, -I say, use method chaining and take a look at how clean your code will be. -In your class functions, simply return `this` at the end of every function, and -you can chain further class methods onto it. +It has been argued that method chaining is unclean and violates the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter). +Maybe it's true, but this pattern is very useful in JavaScript and you see it in +many libraries such as jQuery and Lodash. It allows your code to be expressive, +and less verbose. For that reason, I say, use method chaining and take a look at +how clean your code will be. In your class functions, simply return `this` at +the end of every function, and you can chain further class methods onto it. **Bad:** ```javascript From 6a705edf7500307eff7bd7ba14cac57c9871f65e Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Tue, 20 Dec 2016 22:52:38 -0800 Subject: [PATCH 059/170] No journal comments --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index f6cd3272..fbf23b8d 100644 --- a/README.md +++ b/README.md @@ -956,3 +956,28 @@ doStuff(); doStuff(); ``` **[⬆ back to top](#table-of-contents)** + +### Don't have journal comments +Remember, use version control! There's no need for dead code, commented code, +and especially journal comments. Use `git log` to get history! + +**Bad:** +```javascript +/** + * 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; +} +``` + +**Good**: +```javascript +function combine(a, b) { + return a + b; +} +``` +**[⬆ back to top](#table-of-contents)** From 5a2e6029935e4e43dc468756c8706a0f55b649a8 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 22 Dec 2016 23:47:58 -0800 Subject: [PATCH 060/170] Use getters and setters --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fbf23b8d..e1835589 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ readable, reusable, and refactorable software in JavaScript. Enjoy! ## Table of Contents 1. [Variables](#variables) 2. [Functions](#functions) - 3. [Classes](#classes) - 4. [Concurrency](#concurrency) - 5. [Comments](#comments) + 3. [Objects and Data Structures](#objects-and-data-structures) + 4. [Classes](#classes) + 5. [Concurrency](#concurrency) + 6. [Comments](#comments) ## **Variables** ### Use meaningful and pronounceable variable names @@ -655,6 +656,57 @@ function combine(val1, val2) { ``` **[⬆ back to top](#table-of-contents)** +## **Objects and Data Structures** +### Use getters and setters +JavaScript doesn't have interfaces or types so it is very hard to enforce this +pattern, because we don't have keywords like `public` and `private`. As it is, +using getters and setters to access data on objects if far better than simply +looking for a property on an object. "Why?" you might ask. Well, here's an +unorganized list of reasons why: + +1. When you want to do more beyond getting an object property, you don't have +to look up and change every accessor in your codebase. +2. Makes adding validation simple when doing a `set`. +3. Encapsulates the internal representation. +4. Easy to add logging and error handling when getting and setting. +5. Inheriting this class, you can override default functionality. +6. You can lazy load your object's properties, let's say getting it from a +server. + + +**Bad:** +```javascript +class BankAccount { + constructor() { + this.balance = 1000; + } +} + +let bankAccount = new BankAccount(); +// Buy shoes... +bankAccount.balance = bankAccount.balance - 100; +``` + +**Good**: +```javascript +class BankAccount { + constructor() { + this.balance = 1000; + } + + // It doesn't have to be prefixed with `get` or `set` to be a getter/setter + withdraw(amount) { + if (verifyAmountCanBeDeducted(amount)) { + this.balance -= amount; + } + } +} + +let bankAccount = new BankAccount(); +// Buy shoes... +bankAccount.withdraw(100); +``` +**[⬆ back to top](#table-of-contents)** ## **Classes** From cff6a33888879814de3b8655453f1c5e2b173fea Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 23 Dec 2016 13:10:49 -0800 Subject: [PATCH 061/170] SRP --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/README.md b/README.md index e1835589..a8dbbc9e 100644 --- a/README.md +++ b/README.md @@ -710,6 +710,63 @@ bankAccount.withdraw(100); ## **Classes** +### Classes should obey Single Responsibility Principle (SRP) +As stated in Clean Code, "There should never be more than one reason for a class +to change". It's tempting to jam-pack a class with a lot of functionality, like +when you can only take one suitcase on your flight. The issue with this is +that your class won't be conceptually cohesive and it will give it many reasons +to change. Minimizing the amount of times you need to change a class is important. +It's important because if too much functioanlity is in one class and you modify a piece of it, +it can be difficult to understand how that will affect other dependent modules in +your codebase. + +**Bad:** +```javascript +class UserSettings { + constructor(user) { + this.user = user; + } + + changeSettings(settings) { + if (this.verifyCredentials(user)) { + // ... + } + } + + verifyCredentials(user) { + // ... + } +} +``` + +**Good**: +```javascript +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()) { + // ... + } + } +} +``` +**[⬆ back to top](#table-of-contents)** + ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware From 5cdb13b1fd97003a56cea432361f9f7067cc7640 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 29 Dec 2016 14:03:41 -0800 Subject: [PATCH 062/170] Add Formatting section --- README.md | 76 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a8dbbc9e..dcbb6f59 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ readable, reusable, and refactorable software in JavaScript. Enjoy! 3. [Objects and Data Structures](#objects-and-data-structures) 4. [Classes](#classes) 5. [Concurrency](#concurrency) - 6. [Comments](#comments) + 6. [Formatting](#formatting) + 7. [Comments](#comments) ## **Variables** ### Use meaningful and pronounceable variable names @@ -93,41 +94,6 @@ locations.forEach((location) => { ``` **[⬆ back to top](#table-of-contents)** -### Use consistent capitalization -JavaScript is untyped, so capitalization tells you a lot about your variables, -functions, etc. These rules are subjective, so your team can choose whatever -they want. The point is, no matter what you all choose, just be consistent. - -**Bad:** -```javascript -var DAYS_IN_WEEK = 7; -var daysInMonth = 30; - -var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; - -function eraseDatabase() {} -function restore_database() {} - -class animal {} -class Alpaca {} -``` - -**Good**: -```javascript -var DAYS_IN_WEEK = 7; -var DAYS_IN_MONTH = 30; - -var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -var artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; - -function eraseDatabase() {} -function restoreDatabase() {} - -class Animal {} -class Alpaca {} -``` -**[⬆ back to top](#table-of-contents)** ### Don't add unneeded context If your class/object name tells you something, don't repeat that in your @@ -1005,6 +971,44 @@ async function getCleanCodeArticle() { **[⬆ back to top](#table-of-contents)** +## **Formatting** +### Use consistent capitalization +JavaScript is untyped, so capitalization tells you a lot about your variables, +functions, etc. These rules are subjective, so your team can choose whatever +they want. The point is, no matter what you all choose, just be consistent. + +**Bad:** +```javascript +var DAYS_IN_WEEK = 7; +var daysInMonth = 30; + +var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; + +function eraseDatabase() {} +function restore_database() {} + +class animal {} +class Alpaca {} +``` + +**Good**: +```javascript +var DAYS_IN_WEEK = 7; +var DAYS_IN_MONTH = 30; + +var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +var artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; + +function eraseDatabase() {} +function restoreDatabase() {} + +class Animal {} +class Alpaca {} +``` +**[⬆ back to top](#table-of-contents)** + + ## **Comments** ### Only comment things that have business logic complexity. Comments are an apology, not a requirement. Good code *mostly* documents itself. From 30c3a4c1deb6771340181e9460d1c0e5c0b236ba Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 29 Dec 2016 14:22:56 -0800 Subject: [PATCH 063/170] Vertical distance --- README.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dcbb6f59..a0a45011 100644 --- a/README.md +++ b/README.md @@ -623,28 +623,28 @@ function combine(val1, val2) { **[⬆ back to top](#table-of-contents)** ## **Objects and Data Structures** -### Use getters and setters +### Use getters and setters JavaScript doesn't have interfaces or types so it is very hard to enforce this pattern, because we don't have keywords like `public` and `private`. As it is, using getters and setters to access data on objects if far better than simply looking for a property on an object. "Why?" you might ask. Well, here's an unorganized list of reasons why: -1. When you want to do more beyond getting an object property, you don't have +1. When you want to do more beyond getting an object property, you don't have to look up and change every accessor in your codebase. -2. Makes adding validation simple when doing a `set`. +2. Makes adding validation simple when doing a `set`. 3. Encapsulates the internal representation. -4. Easy to add logging and error handling when getting and setting. +4. Easy to add logging and error handling when getting and setting. 5. Inheriting this class, you can override default functionality. -6. You can lazy load your object's properties, let's say getting it from a -server. +6. You can lazy load your object's properties, let's say getting it from a +server. **Bad:** ```javascript class BankAccount { constructor() { - this.balance = 1000; + this.balance = 1000; } } @@ -657,7 +657,7 @@ bankAccount.balance = bankAccount.balance - 100; ```javascript class BankAccount { constructor() { - this.balance = 1000; + this.balance = 1000; } // It doesn't have to be prefixed with `get` or `set` to be a getter/setter @@ -692,7 +692,7 @@ class UserSettings { constructor(user) { this.user = user; } - + changeSettings(settings) { if (this.verifyCredentials(user)) { // ... @@ -723,7 +723,7 @@ class UserSettings { this.user = user; this.auth = new UserAuth(user) } - + changeSettings(settings) { if (this.auth.verifyCredentials()) { // ... @@ -972,6 +972,13 @@ async function getCleanCodeArticle() { ## **Formatting** +Formatting is subjective. Like many rules herein, there is no hard and fast +rule that you must follow. The main point is DO NOT ARGUE over formatting. +There are [tons of tools](http://standardjs.com/rules.html) to automate this. +Use one! For things that don't fall under the purview of automatic formatting +(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here +for some guidance. + ### Use consistent capitalization JavaScript is untyped, so capitalization tells you a lot about your variables, functions, etc. These rules are subjective, so your team can choose whatever @@ -1009,6 +1016,91 @@ class Alpaca {} **[⬆ back to top](#table-of-contents)** +### Function callers and callees should be close +If a function calls another, keep those functions vertically close in the source +file. Ideally, keep the caller right above the callee. We tend to read code from +top-to-bottom, like a newspaper. Because of this, make your code read that way. + +**Bad:** +```javascript +class PerformanceReview { + constructor(employee) { + this.employee = employee; + } + + lookupPeers() { + return db.lookup(this.employee, 'peers'); + } + + lookupMananger() { + return db.lookup(this.employee, 'manager'); + } + + getPeerReviews() { + let peers = this.lookupPeers(); + // ... + } + + perfReview() { + getPeerReviews(); + getManagerReview(); + getSelfReview(); + } + + getManagerReview() { + let manager = this.lookupManager(); + } + + getSelfReview() { + // ... + } +} + +let review = new PerformanceReview(user); +review.perfReview(); +``` + +**Good**: +```javascript +class PerformanceReview { + constructor(employee) { + this.employee = employee; + } + + perfReview() { + getPeerReviews(); + getManagerReview(); + getSelfReview(); + } + + getPeerReviews() { + let peers = this.lookupPeers(); + // ... + } + + lookupPeers() { + return db.lookup(this.employee, 'peers'); + } + + getManagerReview() { + let manager = this.lookupManager(); + } + + lookupMananger() { + return db.lookup(this.employee, 'manager'); + } + + getSelfReview() { + // ... + } +} + +let review = new PerformanceReview(employee); +review.perfReview(); +``` + +**[⬆ back to top](#table-of-contents)** + ## **Comments** ### Only comment things that have business logic complexity. Comments are an apology, not a requirement. Good code *mostly* documents itself. From 1530ed7ec404723ba0751fca7b106e6c30aaf32d Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 29 Dec 2016 14:24:05 -0800 Subject: [PATCH 064/170] Fix formatting problems --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a0a45011..5fd6ddbe 100644 --- a/README.md +++ b/README.md @@ -644,11 +644,12 @@ server. ```javascript class BankAccount { constructor() { - this.balance = 1000; + this.balance = 1000; } } let bankAccount = new BankAccount(); + // Buy shoes... bankAccount.balance = bankAccount.balance - 100; ``` @@ -657,18 +658,19 @@ bankAccount.balance = bankAccount.balance - 100; ```javascript class BankAccount { constructor() { - this.balance = 1000; + this.balance = 1000; } // It doesn't have to be prefixed with `get` or `set` to be a getter/setter withdraw(amount) { - if (verifyAmountCanBeDeducted(amount)) { - this.balance -= amount; - } + if (verifyAmountCanBeDeducted(amount)) { + this.balance -= amount; + } } } let bankAccount = new BankAccount(); + // Buy shoes... bankAccount.withdraw(100); ``` From 8548008df774298af92c7e8bc3e2a7700780025d Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 29 Dec 2016 14:31:50 -0800 Subject: [PATCH 065/170] No legal comments --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index 5fd6ddbe..e26f187b 100644 --- a/README.md +++ b/README.md @@ -1188,3 +1188,45 @@ function combine(a, b) { } ``` **[⬆ back to top](#table-of-contents)** + +### Avoid legal comments in source files +That's what your `LICENSE` file at the top of your source tree is for. + +**Bad:** +```javascript +/* +The MIT License (MIT) + +Copyright (c) 2016 Ryan McDermott + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE +*/ + +function calculateBill() { + // ... +} +``` + +**Good**: +```javascript +function calculateBill() { + // ... +} +``` +**[⬆ back to top](#table-of-contents)** From 309bd04d953418f6fcc3b267d4cb64347ac8854c Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 29 Dec 2016 14:32:46 -0800 Subject: [PATCH 066/170] Drive home point of formatting --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e26f187b..15c65990 100644 --- a/README.md +++ b/README.md @@ -977,7 +977,9 @@ async function getCleanCodeArticle() { Formatting is subjective. Like many rules herein, there is no hard and fast rule that you must follow. The main point is DO NOT ARGUE over formatting. There are [tons of tools](http://standardjs.com/rules.html) to automate this. -Use one! For things that don't fall under the purview of automatic formatting +Use one! It's a waste of time and money for engineers to argue over formatting. + +For things that don't fall under the purview of automatic formatting (indentation, tabs vs. spaces, double vs. single quotes, etc.) look here for some guidance. From 8a403a27452f2076c58ffb274405666fc7820d53 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 30 Dec 2016 17:32:38 -0800 Subject: [PATCH 067/170] Prefer composition over inheritance --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/README.md b/README.md index 15c65990..483914af 100644 --- a/README.md +++ b/README.md @@ -893,6 +893,73 @@ let car = new Car() ``` **[⬆ back to top](#table-of-contents)** +### Prefer composition over inheritance +As stated famously in the [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns), +you should prefer composition over inheritance where you can. There are lots of +good reasons to use inheritance and lots of good reasons to use composition. +The main point for this maxim is that if your mind instinctively goes for +inheritance, try to think if composition could model your problem better. In some +cases it can. Inheritance is great for preventing + +You might be wondering then, "when should I use inheritance?" It +depends on your problem at hand, but this is a decent list of when inheritance +makes more sense than composition: + +1. Your inheritance represents an "is-a" relationship and not a "has-a" +relationship (Animal->Human vs. User->UserDetails). +2. You can reuse code from the base classes (Humans can move like all animals). +3. You want to make global changes to derived classes by changing a base class. +(Change the caloric expenditure of all animals when they move). + +**Bad:** +```javascript +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; + } + + // ... +} +``` + +**Good**: +```javascript +class Employee { + constructor(name, email) { + this.name = name; + this.email = email; + + } + + setTaxData(ssn, salary) { + this.taxData = new EmployeeTaxData(ssn, salary); + } + // ... +} + +class EmployeeTaxData { + constructor(ssn, salary) { + this.ssn = ssn; + this.salary = salary; + } + + // ... +} +``` +**[⬆ back to top](#table-of-contents)** + ## **Concurrency** ### Use Promises, not callbacks Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, From a984bd1b1de0788b84f04a5cde43182cadefb780 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 30 Dec 2016 17:57:11 -0800 Subject: [PATCH 068/170] Use private members --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index 483914af..24307776 100644 --- a/README.md +++ b/README.md @@ -677,6 +677,46 @@ bankAccount.withdraw(100); **[⬆ back to top](#table-of-contents)** +### Make objects have private members +This can be accomplished through closures (for ES5 and below). + +**Bad:** +```javascript + +var Employee = function(name) { + this.name = name; +} + +Employee.prototype.getName = function() { + return this.name; +} + +var 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 +``` + +**Good**: +```javascript +var Employee = (function() { + function Employee(name) { + this.getName = function() { + return name; + }; + } + + return Employee; +}()); + +var 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: John Doe +``` +**[⬆ back to top](#table-of-contents)** + + ## **Classes** ### Classes should obey Single Responsibility Principle (SRP) As stated in Clean Code, "There should never be more than one reason for a class From 1c4ba5f8532ada25f1afdcaa38b24557fec3d24b Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 30 Dec 2016 18:04:31 -0800 Subject: [PATCH 069/170] Don't overoptimize --- README.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 24307776..543247b0 100644 --- a/README.md +++ b/README.md @@ -325,7 +325,6 @@ createMenu(menuConfig); **[⬆ back to top](#table-of-contents)** - ### Don't use flags as function parameters Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. @@ -379,7 +378,6 @@ function splitIntoFirstAndLastName() { } console.log(name); // ['Ryan', 'McDermott']; - ``` **Good**: @@ -622,6 +620,32 @@ function combine(val1, val2) { ``` **[⬆ back to top](#table-of-contents)** +### Don't over-optimize +Modern browsers do a lot of optimization under-the-hood at runtime. A lot of +times, if you are optimizing then you are just wasting your time. [There are good +resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) +for seeing where optimization is lacking. Target those in the meantime, until +they are fixed if they can be. + +**Bad:** +```javascript + +// On old browsers, each iteration would be costly because `len` would be +// recomputed. In modern browsers, this is optimized. +for (var i = 0, len = list.length; i < len; i++) { + // ... +} +``` + +**Good**: +```javascript +for (var i = 0; i < list.length; i++) { + // ... +} +``` +**[⬆ back to top](#table-of-contents)** + + ## **Objects and Data Structures** ### Use getters and setters JavaScript doesn't have interfaces or types so it is very hard to enforce this From f5f2110abe987c595d8078487bcf6807069a0d53 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Fri, 30 Dec 2016 18:17:57 -0800 Subject: [PATCH 070/170] Short-circuit don't use conditionals --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 543247b0..fa28bd52 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,6 @@ locations.forEach((location) => { ``` **[⬆ back to top](#table-of-contents)** - ### Don't add unneeded context If your class/object name tells you something, don't repeat that in your variable name. @@ -126,6 +125,28 @@ function paintCar(car) { ``` **[⬆ back to top](#table-of-contents)** +### Short-circuiting is cleaner than conditionals + +**Bad:** +```javascript +function createMicrobrewery(name) { + var breweryName; + if (name) { + breweryName = name; + } else { + breweryName = 'Hipster Brew Co.'; + } +} +``` + +**Good**: +```javascript +function createMicrobrewery(name) { + var breweryName = name || 'Hipster Brew Co.' +} +``` +**[⬆ back to top](#table-of-contents)** + ## **Functions** ### Limit the amount of function parameters (2 or less) Use an object if you are finding yourself needing a lot of parameters. From 0c0aec0233fd1e337ec2bf20bf4371ee57f938f9 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sat, 31 Dec 2016 11:06:14 -0800 Subject: [PATCH 071/170] Open/Closed Principle --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fa28bd52..3017e779 100644 --- a/README.md +++ b/README.md @@ -763,7 +763,7 @@ console.log('Employee name: ' + employee.getName()); // Employee name: John Doe ## **Classes** -### Classes should obey Single Responsibility Principle (SRP) +### Single Responsibility Principle (SRP) As stated in Clean Code, "There should never be more than one reason for a class to change". It's tempting to jam-pack a class with a lot of functionality, like when you can only take one suitcase on your flight. The issue with this is @@ -820,6 +820,47 @@ class UserSettings { ``` **[⬆ back to top](#table-of-contents)** +### Open/Closed Principle (OCP) +As stated by Bertrand Meyer, "software entities (classes, modules, functions, +etc.) should be open for extension, but closed for modification." What does that +mean though? This principle basically states that you should allow users to +extend the functionality of your module without having to open up the `.js` +source code file and manually manipulate it. + +**Bad:** +```javascript +class AjaxRequester { + constructor() { + // What if we wanted another HTTP Method, like DELETE? We would have to + // open this file up and modify this and put it in manually. + this.HTTP_METHODS = ['POST', 'PUT', 'GET']; + } + + get(url) { + // ... + } + +} +``` + +**Good**: +```javascript +class AjaxRequester { + constructor() { + this.HTTP_METHODS = ['POST', 'PUT', 'GET']; + } + + get(url) { + // ... + } + + addHTTPMethod(method) { + this.HTTP_METHODS.push(method); + } +} +``` +**[⬆ back to top](#table-of-contents)** + ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware From 13a950e2a518a6fbf7dbf2d641fef2a298b1a3c1 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sat, 31 Dec 2016 20:03:47 -0800 Subject: [PATCH 072/170] Liskov Substitution Principle (LSP) --- README.md | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/README.md b/README.md index 3017e779..59ace9b5 100644 --- a/README.md +++ b/README.md @@ -861,6 +861,144 @@ class AjaxRequester { ``` **[⬆ back to top](#table-of-contents)** + +### Liskov Substitution Principle (LSP) +This is a scary term for a very simple concept. It's formally defined as "If S +is a subtype of T, then objects of type T may be replaced with objects of type S +(i.e., objects of type S may substitute objects of type T) without altering any +of the desirable properties of that program (correctness, task performed, +etc.)." That's an even scarier definition. + +The best explanation for this is if you have a parent class and a child class, +then the base class and child class can be used interchangeably without getting +incorrect results. This might still be confusing, so let's take a look at the +classic Square-Rectangle example. Mathematically, a square is a rectangle, but +if you model it using the "is-a" relationship via inheritance, you quickly +get into trouble. + +**Bad:** +```javascript +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 { + constructor() { + super(); + } + + 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); + let area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20. + rectangle.render(area); + }) +} + +let rectangles = [new Rectangle(), new Rectangle(), new Square()]; +renderLargeRectangles(rectangles); +``` + +**Good**: +```javascript +class Shape { + constructor() {} + + setColor(color) { + // ... + } + + render(area) { + // ... + } +} + +class Rectangle extends Shape { + constructor() { + super(); + this.width = 0; + this.height = 0; + } + + setWidth(width) { + this.width = width; + } + + setHeight(height) { + this.height = height; + } + + getArea() { + return this.width * this.height; + } +} + +class Square extends Shape { + constructor() { + super(); + this.length = 0; + } + + setLength(length) { + this.length = length; + } +} + +function renderLargeShapes(shapes) { + shapes.forEach((shape) => { + switch (shape.constructor.name) { + case 'Square': + shape.setLength(5); + case 'Rectangle': + shape.setWidth(4); + shape.setHeight(5); + } + + let area = shape.getArea(); + shape.render(area); + }) +} + +let shapes = [new Rectangle(), new Rectangle(), new Square()]; +renderLargeShapes(); +``` +**[⬆ back to top](#table-of-contents)** + ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware From 6cbba6add4da6ba8c3c94903cda1b7b71b453a72 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 1 Jan 2017 21:13:23 -0800 Subject: [PATCH 073/170] Interface Segregation Principle (ISP) --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/README.md b/README.md index 59ace9b5..0cc9dcd3 100644 --- a/README.md +++ b/README.md @@ -999,6 +999,80 @@ renderLargeShapes(); ``` **[⬆ back to top](#table-of-contents)** +### Interface Segregation Principle (ISP) +JavaScript doesn't have interfaces so this principle doesn't apply as strictly +as others. However, it's important and relevant even with JavaScript's lack of +type system. + +ISP states that "Clients should not be forced to depend upon interfaces that +they do not use." Interfaces are implicit contracts in JavaScript because of +duck typing. + +A good example to look at that demonstrates this principle in JavaScript is for +classes that require large settings objects. Not requiring clients to setup +huge amounts of options is beneficial, because most of the time they won't need +all of the settings. Making them optional helps prevent having a "fat interface". + +**Bad:** +```javascript +class DOMTraverser { + constructor(settings) { + this.settings = settings; + this.setup(); + } + + setup() { + this.rootNode = this.settings.rootNode; + this.animationModule.setup(); + } + + traverse() { + // ... + } +} + +let $ = new DOMTraverser({ + rootNode: document.getElementsByTagName('body'), + animationModule: function() {} // Most of the time, we won't need to animate when traversing. + // ... +}); + +``` + +**Good**: +```javascript +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() { + // ... + } +} + +let $ = new DOMTraverser({ + rootNode: document.getElementsByTagName('body'), + options: { + animationModule: function() {} + } +}); +``` +**[⬆ back to top](#table-of-contents)** + ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware From 781f76d7d160342c6e04a52a12b935b923dca5d3 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 11:45:08 -0800 Subject: [PATCH 074/170] Dependency Inversion Principle (DIP) --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/README.md b/README.md index 0cc9dcd3..bfe08994 100644 --- a/README.md +++ b/README.md @@ -1073,6 +1073,97 @@ let $ = new DOMTraverser({ ``` **[⬆ back to top](#table-of-contents)** +### Dependency Inversion Principle (DIP) +This principle states two essential things: +1. High-level modules should not depend on low-level modules. Both should +depend on abstractions. +2. 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 Angular.js, +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. + +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. + +**Bad:** +```javascript +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); + }); + } +} + +class InventoryRequester { + constructor() { + this.REQ_METHODS = ['HTTP']; + } + + requestItem(item) { + // ... + } +} + +let inventoryTracker = new InventoryTracker(['apples', 'bananas']); +inventoryTracker.requestItems(); +``` + +**Good**: +```javascript +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. +let inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); +inventoryTracker.requestItems(); +``` +**[⬆ back to top](#table-of-contents)** + ### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware From e8d55dfa4df9cd2f9dcf45660d821ac2dfd47d19 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 12:14:59 -0800 Subject: [PATCH 075/170] Drive home point of DIP and decoupling --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bfe08994..1ace48d9 100644 --- a/README.md +++ b/README.md @@ -1084,11 +1084,15 @@ This can be hard to understand at first, but if you've worked with Angular.js, 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. +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. +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. **Bad:** ```javascript From cf41bcde15704ebee60dea94b5b377fdeafa2f30 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 12:29:11 -0800 Subject: [PATCH 076/170] Add testing section --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1ace48d9..c0ef7da3 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ readable, reusable, and refactorable software in JavaScript. Enjoy! 2. [Functions](#functions) 3. [Objects and Data Structures](#objects-and-data-structures) 4. [Classes](#classes) - 5. [Concurrency](#concurrency) - 6. [Formatting](#formatting) - 7. [Comments](#comments) + 5. [Testing](#testing) + 6. [Concurrency](#concurrency) + 7. [Formatting](#formatting) + 8. [Comments](#comments) ## **Variables** ### Use meaningful and pronounceable variable names @@ -1393,6 +1394,21 @@ class EmployeeTaxData { ``` **[⬆ back to top](#table-of-contents)** +## **Testing** +Testing is more important than shipping. If you have 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. + +There's no excuse to not write tests. There's [plenty of good JS test frameworks] +(http://jstherightway.org/#testing-tools), 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. + ## **Concurrency** ### Use Promises, not callbacks Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, From 3db31e30d260d95007cc4112fb520edde2a5c57f Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 12:32:04 -0800 Subject: [PATCH 077/170] Add coverage tool note --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c0ef7da3..05fcd62a 100644 --- a/README.md +++ b/README.md @@ -1399,7 +1399,9 @@ Testing is more important than shipping. If you have 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. +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](http://gotwarlost.github.io/istanbul/). There's no excuse to not write tests. There's [plenty of good JS test frameworks] (http://jstherightway.org/#testing-tools), so find one that your team prefers. From d3ecccf2e60ac1783d370203935bd0f587dc2e92 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 14:43:33 -0800 Subject: [PATCH 078/170] Add proper introduction --- README.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 05fcd62a..4e57b454 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,7 @@ # clean-code-javascript -Software engineering principles, from Robert C. Martin's book -[*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adapted for JavaScript. This is not a style guide. It's a guide to producing -readable, reusable, and refactorable software in JavaScript. Enjoy! ## Table of Contents + 0. [Introduction](#introduction) 1. [Variables](#variables) 2. [Functions](#functions) 3. [Objects and Data Structures](#objects-and-data-structures) @@ -14,6 +11,33 @@ readable, reusable, and refactorable software in JavaScript. Enjoy! 7. [Formatting](#formatting) 8. [Comments](#comments) +## Introduction +![Humorous image of software quality estimation as a count of how many expletives +you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) + +Software engineering principles, from Robert C. Martin's book +[*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), +adapted for JavaScript. This is not a style guide. It's a guide to producing +readable, reusable, and refactorable software in JavaScript. + +Not every principle herein has to be strictly followed, and even less will be +universally agreed upon. These are guidelines and nothing more, but they are +ones codified over many years of collective experience by the authors of +*Clean Code*. + +Our craft of software engineering is just a bit over 50 years old, and we are +still learning a lot. When software architecture is as old as architecture +itself, maybe then we will have harder rules to follow. For now, let these +guidelines serve as a touchstone by which to assess the quality of the +JavaScript code that you and your team produce. + +One more thing: knowing these won't immediately make you a better software +developer, and working with them for many years doesn't mean you won't make +mistakes. Every piece of code starts as a first draft, like wet clay getting +shaped into its final form. Finally, we chisel away the imperfections when +we review it with our peers. Don't beat yourself up for first drafts that need +improvement. Beat up the code instead! + ## **Variables** ### Use meaningful and pronounceable variable names From 68107087dbf1d0253f8ca6dc10337f2d737ef5a0 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 14:57:34 -0800 Subject: [PATCH 079/170] Use explanatory variables --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e57b454..2df093f6 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,10 @@ getUser(); **[⬆ back to top](#table-of-contents)** ### Use searchable names -We will read more code than we will ever write. It's important that the code we do write is readable and searchable. By *not* naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. +We will read more code than we will ever write. It's important that the code we +do write is readable and searchable. By *not* naming variables that end up +being meaningful for understanding our program, we hurt our readers. +Make your names searchable. **Bad:** ```javascript @@ -88,6 +91,23 @@ for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { ``` **[⬆ back to top](#table-of-contents)** +### Use explanatory variables +**Bad:** +```javascript +let cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; +saveCityState(cityStateRegex.match(cityStateRegex)[0], cityStateRegex.match(cityStateRegex)[0]); +``` + +**Good**: +```javascript +let cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; +let match = cityStateRegex.match(cityStateRegex) +let city = match[1]; +let state = match[2]; +saveCityState(city, state); +``` +**[⬆ back to top](#table-of-contents)** + ### Avoid Mental Mapping Explicit is better than implicit. From f190fedbc6061167d75c5bb139ab194b371b748c Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 15:02:29 -0800 Subject: [PATCH 080/170] Avoid positional markers --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 2df093f6..a612729e 100644 --- a/README.md +++ b/README.md @@ -1753,6 +1753,41 @@ function combine(a, b) { ``` **[⬆ back to top](#table-of-contents)** +### Avoid positional markers +They usually just add noise. Let the functions and variable names along with the +proper indentation and formatting give the visual structure to your code. + +**Bad:** +```javascript +//////////////////////////////////////////////////////////////////////////////// +// Scope Model Instantiation +//////////////////////////////////////////////////////////////////////////////// +let $scope.model = { + menu: 'foo', + nav: 'bar' +}; + +//////////////////////////////////////////////////////////////////////////////// +// Action setup +//////////////////////////////////////////////////////////////////////////////// +let actions = function() { + // ... +} +``` + +**Good**: +```javascript +let $scope.model = { + menu: 'foo', + nav: 'bar' +}; + +let actions = function() { + // ... +} +``` +**[⬆ back to top](#table-of-contents)** + ### Avoid legal comments in source files That's what your `LICENSE` file at the top of your source tree is for. From 8d22ceda036ae5852eb951ea899b2bf5b9fb79e7 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 15:07:54 -0800 Subject: [PATCH 081/170] Remove dead code --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a612729e..9951d815 100644 --- a/README.md +++ b/README.md @@ -629,7 +629,6 @@ class Cesna extends Airplane { ``` **[⬆ back to top](#table-of-contents)** - ### Avoid type-checking (part 1) JavaScript is untyped, which means your functions can take any type of argument. Sometimes you are bitten by this freedom and it becomes tempting to do @@ -711,6 +710,36 @@ for (var i = 0; i < list.length; i++) { ``` **[⬆ back to top](#table-of-contents)** +### Remove dead code +Dead code is just as bad as duplicate code. There's no reason to keep it in +your codebase. If it's not being called, get rid of it! It will still be safe +in your version history if you still need it. + +**Bad:** +```javascript +function oldRequestModule(url) { + // ... +} + +function newRequestModule(url) { + // ... +} + +var req = newRequestModule; +inventoryTracker('apples', req, 'www.inventory-awesome.io'); + +``` + +**Good**: +```javascript +function newRequestModule(url) { + // ... +} + +var req = newRequestModule; +inventoryTracker('apples', req, 'www.inventory-awesome.io'); +``` +**[⬆ back to top](#table-of-contents)** ## **Objects and Data Structures** ### Use getters and setters From c72690d81e8c75084bce31765ef018fa2b79cef1 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 15:22:24 -0800 Subject: [PATCH 082/170] Function names should say what they do --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 9951d815..908380bc 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,31 @@ function isClientActive(client) { ``` **[⬆ back to top](#table-of-contents)** +### Function names should say what they do + +**Bad:** +```javascript +function dateAdd(date, month) { + // ... +} + +let date = new Date(); + +// It's hard to to tell from the function name what is added +dateAdd(date, 1); +``` + +**Good**: +```javascript +function dateAddMonth(date, month) { + // ... +} + +let date = new Date(); +dateAddMonth(date, 1); +``` +**[⬆ back to top](#table-of-contents)** + ### Remove duplicate code Never ever, ever, under any circumstance, have duplicate code. There's no reason for it and it's quite possibly the worst sin you can commit as a professional From ecfe2030ac62ef791a98d6c2f073e55f1bbd9f2a Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 15:27:55 -0800 Subject: [PATCH 083/170] Avoid negative conditionals --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 908380bc..95343531 100644 --- a/README.md +++ b/README.md @@ -598,6 +598,31 @@ var totalOutput = programmerOutput ``` **[⬆ back to top](#table-of-contents)** +### Avoid negative conditionals + +**Bad:** +```javascript +function isDOMNodeNotPresent(node) { + // ... +} + +if (!isDOMNodeNotPresent(node)) { + // ... +} +``` + +**Good**: +```javascript +function isDOMNodePresent(node) { + // ... +} + +if (isDOMNodePresent(node)) { + // ... +} +``` +**[⬆ back to top](#table-of-contents)** + ### Avoid conditionals This seems like an impossible task. Upon first hearing this, most people say, "how am I supposed to do anything without an `if` statement?" The answer is that From 15bd7caab627db17de260e7b046ce49657a37022 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 15:31:14 -0800 Subject: [PATCH 084/170] Encapsulate conditionals --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 95343531..7136f163 100644 --- a/README.md +++ b/README.md @@ -598,6 +598,27 @@ var totalOutput = programmerOutput ``` **[⬆ back to top](#table-of-contents)** +### Encapsulate conditionals + +**Bad:** +```javascript +if (fsm.state === 'fetching' && isEmpty(listNode)) { + /// ... +} +``` + +**Good**: +```javascript +function shouldShowSpinner() { + return fsm.state === 'fetching' && isEmpty(listNode); +} + +if (shouldShowSpinner()) { + // ... +} +``` +**[⬆ back to top](#table-of-contents)** + ### Avoid negative conditionals **Bad:** From 3dfab883819f587d82a669867f116648b98cc00e Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 15:56:47 -0800 Subject: [PATCH 085/170] Single concept per test --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7136f163..3bad26c6 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # clean-code-javascript ## Table of Contents - 0. [Introduction](#introduction) - 1. [Variables](#variables) - 2. [Functions](#functions) - 3. [Objects and Data Structures](#objects-and-data-structures) - 4. [Classes](#classes) - 5. [Testing](#testing) - 6. [Concurrency](#concurrency) - 7. [Formatting](#formatting) - 8. [Comments](#comments) + 1. [Introduction](#introduction) + 2. [Variables](#variables) + 3. [Functions](#functions) + 4. [Objects and Data Structures](#objects-and-data-structures) + 5. [Classes](#classes) + 6. [Testing](#testing) + 7. [Concurrency](#concurrency) + 8. [Formatting](#formatting) + 9. [Comments](#comments) ## Introduction ![Humorous image of software quality estimation as a count of how many expletives @@ -1555,6 +1555,57 @@ Test Driven Development (TDD), that is great, but the main point is to just make sure you are reaching your coverage goals before launching any feature, or refactoring an existing one. +### Single concept per test + +**Bad:** +```javascript +const assert = require('assert'); + +describe('MakeMomentJSGreatAgain', function() { + it('handles date boundaries', function() { + let date; + + date = new MakeMomentJSGreatAgain('1/1/2015'); + date.addDays(30); + date.shouldEqual('1/31/2015'); + + 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); + }); +}); +``` + +**Good**: +```javascript +const assert = require('assert'); + +describe('MakeMomentJSGreatAgain', function() { + it('handles 30-day months', function() { + let date = new MakeMomentJSGreatAgain('1/1/2015'); + date.addDays(30); + date.shouldEqual('1/31/2015'); + }); + + it('handles leap year', function() { + let date = new MakeMomentJSGreatAgain('2/1/2016'); + date.addDays(28); + assert.equal('02/29/2016', date); + }); + + it('handles non-leap year', function() { + let date = new MakeMomentJSGreatAgain('2/1/2015'); + date.addDays(28); + assert.equal('03/01/2015', date); + }); +}); +``` +**[⬆ back to top](#table-of-contents)** + ## **Concurrency** ### Use Promises, not callbacks Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, From dbc02d32c18893645b8eb79dc6a4a18779b8ff09 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 16:00:44 -0800 Subject: [PATCH 086/170] Add first subsection marker --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bad26c6..3cf8169b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ we review it with our peers. Don't beat yourself up for first drafts that need improvement. Beat up the code instead! ## **Variables** -### Use meaningful and pronounceable variable names + +### [1.1](#variables--meaningful) Use meaningful and pronounceable variable names **Bad:** ```javascript From 95e3ec7f96539846c5df91e74a1947b074228ee4 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 16:09:49 -0800 Subject: [PATCH 087/170] Attempt another GHFM link --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3cf8169b..21ea614f 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,7 @@ we review it with our peers. Don't beat yourself up for first drafts that need improvement. Beat up the code instead! ## **Variables** - -### [1.1](#variables--meaningful) Use meaningful and pronounceable variable names +### [1.1][] Use meaningful and pronounceable variable names **Bad:** ```javascript From 75ec41ad32782e5fff4690295232b265004ea32e Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 16:11:23 -0800 Subject: [PATCH 088/170] Remove subsection anchors, favor automatic marking --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 21ea614f..3bad26c6 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ we review it with our peers. Don't beat yourself up for first drafts that need improvement. Beat up the code instead! ## **Variables** -### [1.1][] Use meaningful and pronounceable variable names +### Use meaningful and pronounceable variable names **Bad:** ```javascript From 59330daffeb062b88b507a0a455bb32cf592ea4e Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 16:23:38 -0800 Subject: [PATCH 089/170] Drive home pont of limiting function arguments --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3bad26c6..faa54489 100644 --- a/README.md +++ b/README.md @@ -193,8 +193,21 @@ function createMicrobrewery(name) { **[⬆ back to top](#table-of-contents)** ## **Functions** -### Limit the amount of function parameters (2 or less) -Use an object if you are finding yourself needing a lot of parameters. +### Function arguments (2 or less ideally) +Limiting the amount of function parameters is incredibly important because it +makes testing your function easier. Having more than three leads to a +combinatorial explosion where you have to test tons of different cases with +each separate argument. + +Zero arguments is the ideal case. One or two arguments is ok, and three should +be avoided. Anything more than that should be consolidated. Usually, if you have +more than two arguments then your function is trying to do too much. In cases +where it's not, most of the time a higher-level object will suffice as an +argument. + +Since JavaScript allows us to make objects on the fly, without a lot of class +boilerplate, you can use an object if you are finding yourself needing a +lot of arguments. **Bad:** ```javascript From 7d6ec5388e0e1b28c7324afbb5871cb9778d1acd Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Mon, 2 Jan 2017 16:34:54 -0800 Subject: [PATCH 090/170] Functions should be at one level of abstraction --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/README.md b/README.md index faa54489..40b39bb6 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,74 @@ dateAddMonth(date, 1); ``` **[⬆ back to top](#table-of-contents)** +### Functions should only be one level of abstraction +When you have more than one level of abstraction your function is usually +doing too much. Splitting up functions leads to reusability and easier +testing. + +**Bad:** +```javascript +function parseBetterJSAlternative(code) { + let REGEXES = [ + // ... + ]; + + let statements = code.split(' '); + let tokens; + REGEXES.forEach((REGEX) => { + statements.forEach((statement) => { + // ... + }) + }); + + let ast; + tokens.forEach((token) => { + // lex... + }); + + ast.forEach((node) => { + // parse... + }) +} +``` + +**Good**: +```javascript +function tokenize() { + let REGEXES = [ + // ... + ]; + + let statements = code.split(' '); + let tokens; + REGEXES.forEach((REGEX) => { + statements.forEach((statement) => { + // ... + }) + }); + + return tokens; +} + +function lexer() { + let ast; + tokens.forEach((token) => { + // lex... + }); + + return ast; +} + +function parseBetterJSAlternative(code) { + let tokens = tokenize(code); + let ast = lexer(ast); + ast.forEach((node) => { + // parse... + }) +} +``` +**[⬆ back to top](#table-of-contents)** + ### Remove duplicate code Never ever, ever, under any circumstance, have duplicate code. There's no reason for it and it's quite possibly the worst sin you can commit as a professional From 0732925aac2cbd743eabc7cff88f8c32dc7d861a Mon Sep 17 00:00:00 2001 From: Chris Andrejewski Date: Wed, 4 Jan 2017 01:28:51 -0500 Subject: [PATCH 091/170] fix "do one thing" code example The world almost just ended right here. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40b39bb6..c6978534 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ function emailClients(clients) { ```javascript function emailClients(clients) { clients.forEach(client => { - emailClientIfNeeded(); + emailClientIfNeeded(client); }); } From 7fffb04e39ec0122430a7614bb18b7db538380f8 Mon Sep 17 00:00:00 2001 From: Mario Tacke Date: Wed, 4 Jan 2017 09:40:56 -0800 Subject: [PATCH 092/170] (docs) Correct spelling errors and remove unfinished sentence. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c6978534..dd8229b1 100644 --- a/README.md +++ b/README.md @@ -445,7 +445,7 @@ function writeForumComment(subject, body) { **Good**: ```javascript -function writeForumComment(subject='No subject', body='No text') { +function writeForumComment(subject = 'No subject', body = 'No text') { ... } @@ -790,7 +790,7 @@ The first thing to consider is consistent APIs. **Bad:** ```javascript function travelToTexas(vehicle) { - if (obj instanceof Bicylce) { + if (obj instanceof Bicycle) { vehicle.peddle(this.currentLocation, new Location('texas')); } else if (obj instanceof Car) { vehicle.drive(this.currentLocation, new Location('texas')); @@ -897,7 +897,7 @@ inventoryTracker('apples', req, 'www.inventory-awesome.io'); ### Use getters and setters JavaScript doesn't have interfaces or types so it is very hard to enforce this pattern, because we don't have keywords like `public` and `private`. As it is, -using getters and setters to access data on objects if far better than simply +using getters and setters to access data on objects is far better than simply looking for a property on an object. "Why?" you might ask. Well, here's an unorganized list of reasons why: @@ -1558,7 +1558,7 @@ you should prefer composition over inheritance where you can. There are lots of good reasons to use inheritance and lots of good reasons to use composition. The main point for this maxim is that if your mind instinctively goes for inheritance, try to think if composition could model your problem better. In some -cases it can. Inheritance is great for preventing +cases it can. You might be wondering then, "when should I use inheritance?" It depends on your problem at hand, but this is a decent list of when inheritance From 773922d82e5ea47f16ca163e96d75409c340897d Mon Sep 17 00:00:00 2001 From: Chiara Felice Sant Cassia Date: Wed, 4 Jan 2017 20:55:55 +0100 Subject: [PATCH 093/170] Remove duplicate code "good" example - Remove unnecessary, misleading statement --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index dd8229b1..a5d4e0f2 100644 --- a/README.md +++ b/README.md @@ -420,7 +420,6 @@ function showList(employees) { portfolio = employee.getGithubLink(); } - var favoriteManagerBooks = getMBAList() var data = { expectedSalary: expectedSalary, experience: experience, From a7747d629abf24638cf6aeb124c5996725bba8ae Mon Sep 17 00:00:00 2001 From: Jake Harclerode Date: Wed, 4 Jan 2017 13:46:30 -0800 Subject: [PATCH 094/170] add missing commas in objects --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a5d4e0f2..00150f98 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ function createMenu(title, body, buttonText, cancellable) { var menuConfig = { title: 'Foo', body: 'Bar', - buttonText: 'Baz' + buttonText: 'Baz', cancellable: true } @@ -458,7 +458,7 @@ function writeForumComment(subject = 'No subject', body = 'No text') { var menuConfig = { title: null, body: 'Bar', - buttonText: null + buttonText: null, cancellable: true } @@ -478,7 +478,7 @@ createMenu(menuConfig); var menuConfig = { title: null, body: 'Bar', - buttonText: null + buttonText: null, cancellable: true } From 931c84841cfffec5aee99cd7d10c1b37d82c0290 Mon Sep 17 00:00:00 2001 From: Jake Harclerode Date: Wed, 4 Jan 2017 13:40:24 -0800 Subject: [PATCH 095/170] Use explanatory variables bad and good examples should match ([1] + [2] instead of [0]) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00150f98..a5e1fc2a 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { **Bad:** ```javascript let cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; -saveCityState(cityStateRegex.match(cityStateRegex)[0], cityStateRegex.match(cityStateRegex)[0]); +saveCityState(cityStateRegex.match(cityStateRegex)[1], cityStateRegex.match(cityStateRegex)[2]); ``` **Good**: From 0856eb86602f1b59ab43cfc2b9d15f7fc304eed4 Mon Sep 17 00:00:00 2001 From: Adam Laycock Date: Wed, 4 Jan 2017 13:56:39 -0700 Subject: [PATCH 096/170] Add getArea method where it is necessary. Without this, the `shape.getArea()` call below will cause an error. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a5e1fc2a..1d46827b 100644 --- a/README.md +++ b/README.md @@ -1202,6 +1202,10 @@ class Square extends Shape { setLength(length) { this.length = length; } + + getArea() { + return this.length * this.length; + } } function renderLargeShapes(shapes) { From 76b4038194ef1ebeed219d1af837f194d13d8094 Mon Sep 17 00:00:00 2001 From: Adam Laycock Date: Wed, 4 Jan 2017 14:03:49 -0700 Subject: [PATCH 097/170] Add missing function argument in LSP section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d46827b..7c037da8 100644 --- a/README.md +++ b/README.md @@ -1224,7 +1224,7 @@ function renderLargeShapes(shapes) { } let shapes = [new Rectangle(), new Rectangle(), new Square()]; -renderLargeShapes(); +renderLargeShapes(shapes); ``` **[⬆ back to top](#table-of-contents)** From 06f8296b8dd3ac9346aa2118227a771df503aaf2 Mon Sep 17 00:00:00 2001 From: Gao Jia Siang Date: Thu, 5 Jan 2017 10:29:34 +0800 Subject: [PATCH 098/170] Update README.md Filled and replace argument names in ### Functions should only be one level of abstraction 308. fill code argument 350. fill tokens argument 361. replace ast into tokens --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7c037da8..3e7f2d97 100644 --- a/README.md +++ b/README.md @@ -331,7 +331,7 @@ function parseBetterJSAlternative(code) { **Good**: ```javascript -function tokenize() { +function tokenize(code) { let REGEXES = [ // ... ]; @@ -347,7 +347,7 @@ function tokenize() { return tokens; } -function lexer() { +function lexer(tokens) { let ast; tokens.forEach((token) => { // lex... @@ -358,7 +358,7 @@ function lexer() { function parseBetterJSAlternative(code) { let tokens = tokenize(code); - let ast = lexer(ast); + let ast = lexer(tokens); ast.forEach((node) => { // parse... }) From 041574cde3b38869e022bfe636e9f162dd43f0ca Mon Sep 17 00:00:00 2001 From: Gao Jia Siang Date: Thu, 5 Jan 2017 11:12:55 +0800 Subject: [PATCH 099/170] Update README.md Must pass parameters to the conditional function or it might cause variable pollution. 692 add fsm, listNode arguments 696 add fsmInstance, listNodeInstance parameters --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e7f2d97..23e7d31b 100644 --- a/README.md +++ b/README.md @@ -689,11 +689,11 @@ if (fsm.state === 'fetching' && isEmpty(listNode)) { **Good**: ```javascript -function shouldShowSpinner() { +function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); } -if (shouldShowSpinner()) { +if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... } ``` From 46aa24166a63528c311790cd865e838361842f47 Mon Sep 17 00:00:00 2001 From: "G. Kay Lee" Date: Thu, 5 Jan 2017 12:42:09 +0800 Subject: [PATCH 100/170] Update README.md Fix #2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23e7d31b..cc975557 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ var menuConfig = { cancellable: true } -function createMenu(config) { +function createMenu(menuConfig) { ... } From acfc98fc69d96e840cef10803f14a7d7ebd82fc6 Mon Sep 17 00:00:00 2001 From: "G. Kay Lee" Date: Thu, 5 Jan 2017 13:11:08 +0800 Subject: [PATCH 101/170] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cc975557..8e718bcd 100644 --- a/README.md +++ b/README.md @@ -1918,7 +1918,7 @@ function hashIt(data) { // Loop through every character in data for (var i = 0; i < length; i++) { // Get character code. - var char = i.charCodeAt(i); + var char = data.charCodeAt(i); // Make the hash hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer @@ -1935,7 +1935,7 @@ function hashIt(data) { var length = data.length; for (var i = 0; i < length; i++) { - var char = i.charCodeAt(i); + var char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer From 6b5508758d9d26dc29417da1b5528639aa3783f2 Mon Sep 17 00:00:00 2001 From: Henry Harrison Date: Thu, 5 Jan 2017 16:36:36 +0200 Subject: [PATCH 102/170] Update README.md fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e718bcd..cce38949 100644 --- a/README.md +++ b/README.md @@ -994,7 +994,7 @@ to change". It's tempting to jam-pack a class with a lot of functionality, like when you can only take one suitcase on your flight. The issue with this is that your class won't be conceptually cohesive and it will give it many reasons to change. Minimizing the amount of times you need to change a class is important. -It's important because if too much functioanlity is in one class and you modify a piece of it, +It's important because if too much functionality is in one class and you modify a piece of it, it can be difficult to understand how that will affect other dependent modules in your codebase. From 28740cc50f819914d55b365403ad08b8574764d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Go=C3=9Fe?= Date: Thu, 5 Jan 2017 13:05:48 +0100 Subject: [PATCH 103/170] fix(avoid-type-checking-part-1): fix variable name --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cce38949..24c1cc7a 100644 --- a/README.md +++ b/README.md @@ -789,9 +789,9 @@ The first thing to consider is consistent APIs. **Bad:** ```javascript function travelToTexas(vehicle) { - if (obj instanceof Bicycle) { + if (vehicle instanceof Bicycle) { vehicle.peddle(this.currentLocation, new Location('texas')); - } else if (obj instanceof Car) { + } else if (vehicle instanceof Car) { vehicle.drive(this.currentLocation, new Location('texas')); } } From 70aa57fad3099d2e12d152741e2d070289e5013f Mon Sep 17 00:00:00 2001 From: Rico Ruszewski Date: Thu, 5 Jan 2017 08:49:32 +0100 Subject: [PATCH 104/170] Some minor property missmatch Property title is assigned to property buttonText. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24c1cc7a..ab091395 100644 --- a/README.md +++ b/README.md @@ -465,7 +465,7 @@ var menuConfig = { function createMenu(config) { config.title = config.title || 'Foo' config.body = config.body || 'Bar' - config.buttonText = config.title || 'Baz' + config.buttonText = config.buttonText || 'Baz' config.cancellable = config.cancellable === undefined ? config.cancellable : true; } From dd185ca603b14e0b02d93a252c66345cda8e29c5 Mon Sep 17 00:00:00 2001 From: Federico Brigante Date: Wed, 4 Jan 2017 23:30:38 -0800 Subject: [PATCH 105/170] Add missing function call to "no side effects" --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ab091395..8acf479a 100644 --- a/README.md +++ b/README.md @@ -548,6 +548,8 @@ function splitIntoFirstAndLastName() { name = name.split(' '); } +splitIntoFirstAndLastName(); + console.log(name); // ['Ryan', 'McDermott']; ``` From a35666a4b34e31096047d556a8b3e776990c1a40 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Thu, 5 Jan 2017 07:28:49 -0800 Subject: [PATCH 106/170] Fix Object.assign example --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8acf479a..97831300 100644 --- a/README.md +++ b/README.md @@ -476,19 +476,22 @@ createMenu(menuConfig); **Good**: ```javascript var menuConfig = { - title: null, - body: 'Bar', - buttonText: null, + title: 'Order', + // User did not include 'body' key + buttonText: 'Send', cancellable: true } function createMenu(config) { - Object.assign(config, { + config = Object.assign({}, { title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true - }); + }, config); + + // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // ... } createMenu(menuConfig); @@ -1204,7 +1207,7 @@ class Square extends Shape { setLength(length) { this.length = length; } - + getArea() { return this.length * this.length; } From feaa4e16abfe6bb616e8eadf5e0c13ac28996fc3 Mon Sep 17 00:00:00 2001 From: Iheanyi Ekechukwu Date: Thu, 5 Jan 2017 15:42:16 -0500 Subject: [PATCH 107/170] Update README.md Hey there. As a Software Engineer who happens to be Nigerian, I think there are better ways of explaining a side effect than perpetuating stereotypes about a specific group of people. So I just made a small fix to make it more appropriate. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97831300..8a39ef96 100644 --- a/README.md +++ b/README.md @@ -529,7 +529,7 @@ function createFile(name) { A function produces a side effect if it does anything other than take a value in and return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a -Nigerian prince. +stranger. Now, you do need to have side effects in a program on occasion. Like the previous example, you might need to write to a file. What you want to do is to From 39c75cf433115b508bab350d7ea1b3c41797899d Mon Sep 17 00:00:00 2001 From: tonytran1 Date: Thu, 5 Jan 2017 15:02:48 -0800 Subject: [PATCH 108/170] Use constants when variable values do not change --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a39ef96..0c083952 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,24 @@ var yearMonthDay = moment().format('YYYY/MM/DD'); ``` **[⬆ back to top](#table-of-contents)** +### Use ES6 constants when variable values do not change +In the bad example, the variable can be changed. +When you declare a constant, the variable should stay +the same throughout the program. + + +**Bad:** +```javascript +var FIRST_US_PRESIDENT = "George Washington"; +``` + +**Good**: +```javascript +const FIRST_US_PRESIDENT = "George Washington"; +``` +**[⬆ back to top](#table-of-contents)** + + ### Use the same vocabulary for the same type of variable **Bad:** @@ -1951,7 +1969,7 @@ function hashIt(data) { ``` **[⬆ back to top](#table-of-contents)** -### Don't leave commented code in your codebase +### Don't leave commented out code in your codebase Version control exists for a reason. Leave old code in your history. **Bad:** From 6873385d1afc1b1dffef8b5f2189b5a99ed00131 Mon Sep 17 00:00:00 2001 From: Jordan Rome Date: Thu, 5 Jan 2017 20:46:08 -0500 Subject: [PATCH 109/170] Update: promise catch logic Use `console.error` at least as `console.log` essentially turns a thrown error into a muted console log. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0c083952..69b1e4eb 100644 --- a/README.md +++ b/README.md @@ -1747,7 +1747,7 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti console.log('File written'); }) .catch(function(err) { - console.log(err); + console.error(err); }) ``` @@ -1770,7 +1770,7 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti console.log('File written'); }) .catch(function(err) { - console.log(err); + console.error(err); }) ``` From 3f973412fb8afb0e33cff4515422142228c1680b Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Thu, 5 Jan 2017 21:32:53 +0000 Subject: [PATCH 110/170] Remove redundant object from Object.assign example In the "good" `Object.assign` example, the initial new object is redundant as the defaults are also specified in a new object. This is a bit pedantic, and doesn't change the semantics of the example, just removes an unnecessary object creation. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69b1e4eb..19375f87 100644 --- a/README.md +++ b/README.md @@ -501,7 +501,7 @@ var menuConfig = { } function createMenu(config) { - config = Object.assign({}, { + config = Object.assign({ title: 'Foo', body: 'Bar', buttonText: 'Baz', From de317862ac5e049b2672317859d9f3510f57055e Mon Sep 17 00:00:00 2001 From: Anton Paras Date: Thu, 5 Jan 2017 19:39:42 -0800 Subject: [PATCH 111/170] Fix semantic mistake The original phrase "As stated in the Gang of Four..." is semantically incorrect. - The "Gang of Four" is a group of authors: Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides. - They wrote a famous book: Design Patterns. - Here, Ryan McDermott is referring to the software-design principle "you should prefer composition over inheritance." - This principle was stated in the book, Design Patterns. - It was not stated in the Gang of Four -- the authors themselves. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19375f87..d0769786 100644 --- a/README.md +++ b/README.md @@ -1579,7 +1579,7 @@ let car = new Car() **[⬆ back to top](#table-of-contents)** ### Prefer composition over inheritance -As stated famously in the [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns), +As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, you should prefer composition over inheritance where you can. There are lots of good reasons to use inheritance and lots of good reasons to use composition. The main point for this maxim is that if your mind instinctively goes for From 9fc2093abfef6c49f9921729f72af65580513c2e Mon Sep 17 00:00:00 2001 From: Christopher Nikkel Date: Thu, 5 Jan 2017 20:58:01 -0800 Subject: [PATCH 112/170] use const in rule "Use explanatory variables" per preceding rule "ES6 constants when variable values do not change" --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d0769786..17e17fcd 100644 --- a/README.md +++ b/README.md @@ -112,16 +112,16 @@ for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { ### Use explanatory variables **Bad:** ```javascript -let cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; +const cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; saveCityState(cityStateRegex.match(cityStateRegex)[1], cityStateRegex.match(cityStateRegex)[2]); ``` **Good**: ```javascript -let cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; -let match = cityStateRegex.match(cityStateRegex) -let city = match[1]; -let state = match[2]; +const cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; +const match = cityStateRegex.match(cityStateRegex) +const city = match[1]; +const state = match[2]; saveCityState(city, state); ``` **[⬆ back to top](#table-of-contents)** From 992b1b00526303f059b4211f56e3954fef940d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Scho=CC=88nwald?= Date: Fri, 6 Jan 2017 22:59:42 +0100 Subject: [PATCH 113/170] Change var and let to const where necessary --- README.md | 223 +++++++++++++++++++++++++++--------------------------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 17e17fcd..036ce79f 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,12 @@ improvement. Beat up the code instead! **Bad:** ```javascript -var yyyymmdstr = moment().format('YYYY/MM/DD'); +const yyyymmdstr = moment().format('YYYY/MM/DD'); ``` **Good**: ```javascript -var yearMonthDay = moment().format('YYYY/MM/DD'); +const yearMonthDay = moment().format('YYYY/MM/DD'); ``` **[⬆ back to top](#table-of-contents)** @@ -94,16 +94,16 @@ Make your names searchable. **Bad:** ```javascript // What the heck is 525600 for? -for (var i = 0; i < 525600; i++) { +for (let i = 0; i < 525600; i++) { runCronJob(); } ``` **Good**: ```javascript -// Declare them as capitalized `var` globals. -var MINUTES_IN_A_YEAR = 525600; -for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { +// Declare them as capitalized `const` globals. +const MINUTES_IN_A_YEAR = 525600; +for (let i = 0; i < MINUTES_IN_A_YEAR; i++) { runCronJob(); } ``` @@ -131,7 +131,7 @@ Explicit is better than implicit. **Bad:** ```javascript -var locations = ['Austin', 'New York', 'San Francisco']; +const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { doStuff(); doSomeOtherStuff(); @@ -145,7 +145,7 @@ locations.forEach((l) => { **Good**: ```javascript -var locations = ['Austin', 'New York', 'San Francisco']; +const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { doStuff(); doSomeOtherStuff(); @@ -163,7 +163,7 @@ variable name. **Bad:** ```javascript -var Car = { +const Car = { carMake: 'Honda', carModel: 'Accord', carColor: 'Blue' @@ -176,7 +176,7 @@ function paintCar(car) { **Good**: ```javascript -var Car = { +const Car = { make: 'Honda', model: 'Accord', color: 'Blue' @@ -193,7 +193,7 @@ function paintCar(car) { **Bad:** ```javascript function createMicrobrewery(name) { - var breweryName; + let breweryName; if (name) { breweryName = name; } else { @@ -205,7 +205,7 @@ function createMicrobrewery(name) { **Good**: ```javascript function createMicrobrewery(name) { - var breweryName = name || 'Hipster Brew Co.' + const breweryName = name || 'Hipster Brew Co.' } ``` **[⬆ back to top](#table-of-contents)** @@ -236,7 +236,7 @@ function createMenu(title, body, buttonText, cancellable) { **Good**: ```javascript -var menuConfig = { +const menuConfig = { title: 'Foo', body: 'Bar', buttonText: 'Baz', @@ -262,7 +262,7 @@ this guide other than this, you'll be ahead of many developers. ```javascript function emailClients(clients) { clients.forEach(client => { - let clientRecord = database.lookup(client); + const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } @@ -285,7 +285,7 @@ function emailClientIfNeeded(client) { } function isClientActive(client) { - let clientRecord = database.lookup(client); + const clientRecord = database.lookup(client); return clientRecord.isActive(); } ``` @@ -299,7 +299,7 @@ function dateAdd(date, month) { // ... } -let date = new Date(); +const date = new Date(); // It's hard to to tell from the function name what is added dateAdd(date, 1); @@ -311,7 +311,7 @@ function dateAddMonth(date, month) { // ... } -let date = new Date(); +const date = new Date(); dateAddMonth(date, 1); ``` **[⬆ back to top](#table-of-contents)** @@ -324,19 +324,19 @@ testing. **Bad:** ```javascript function parseBetterJSAlternative(code) { - let REGEXES = [ + const REGEXES = [ // ... ]; - let statements = code.split(' '); - let tokens; + const statements = code.split(' '); + const tokens = []; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { // ... }) }); - let ast; + const ast = []; tokens.forEach((token) => { // lex... }); @@ -350,15 +350,15 @@ function parseBetterJSAlternative(code) { **Good**: ```javascript function tokenize(code) { - let REGEXES = [ + const REGEXES = [ // ... ]; - let statements = code.split(' '); - let tokens; + const statements = code.split(' '); + const tokens = []; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { - // ... + tokens.push( // ... ); }) }); @@ -366,20 +366,20 @@ function tokenize(code) { } function lexer(tokens) { - let ast; + const ast = []; tokens.forEach((token) => { - // lex... + ast.push( // ... ); }); return ast; } function parseBetterJSAlternative(code) { - let tokens = tokenize(code); - let ast = lexer(tokens); + const tokens = tokenize(code); + const ast = lexer(tokens); ast.forEach((node) => { // parse... - }) + }) } ``` **[⬆ back to top](#table-of-contents)** @@ -395,10 +395,10 @@ generic functions quite easy. Take advantage of that! ```javascript function showDeveloperList(developers) { developers.forEach(developers => { - var expectedSalary = developer.calculateExpectedSalary(); - var experience = developer.getExperience(); - var githubLink = developer.getGithubLink(); - var data = { + const expectedSalary = developer.calculateExpectedSalary(); + const experience = developer.getExperience(); + const githubLink = developer.getGithubLink(); + const data = { expectedSalary: expectedSalary, experience: experience, githubLink: githubLink @@ -410,10 +410,10 @@ function showDeveloperList(developers) { function showManagerList(managers) { managers.forEach(manager => { - var expectedSalary = manager.calculateExpectedSalary(); - var experience = manager.getExperience(); - var portfolio = manager.getMBAProjects(); - var data = { + const expectedSalary = manager.calculateExpectedSalary(); + const experience = manager.getExperience(); + const portfolio = manager.getMBAProjects(); + const data = { expectedSalary: expectedSalary, experience: experience, portfolio: portfolio @@ -428,17 +428,16 @@ function showManagerList(managers) { ```javascript function showList(employees) { employees.forEach(employee => { - var expectedSalary = employee.calculateExpectedSalary(); - var experience = employee.getExperience(); - var portfolio; + const expectedSalary = employee.calculateExpectedSalary(); + const experience = employee.getExperience(); + + let portfolio = employee.getGithubLink(); if (employee.type === 'manager') { portfolio = employee.getMBAProjects(); - } else { - portfolio = employee.getGithubLink(); } - var data = { + const data = { expectedSalary: expectedSalary, experience: experience, portfolio: portfolio @@ -473,7 +472,7 @@ function writeForumComment(subject = 'No subject', body = 'No text') { **Bad:** ```javascript -var menuConfig = { +const menuConfig = { title: null, body: 'Bar', buttonText: null, @@ -493,7 +492,7 @@ createMenu(menuConfig); **Good**: ```javascript -var menuConfig = { +const menuConfig = { title: 'Order', // User did not include 'body' key buttonText: 'Send', @@ -563,7 +562,7 @@ be happier than the vast majority of other programmers. ```javascript // 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. -var name = 'Ryan McDermott'; +let name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name = name.split(' '); @@ -580,8 +579,8 @@ function splitIntoFirstAndLastName(name) { return name.split(' '); } -var name = 'Ryan McDermott' -var newName = splitIntoFirstAndLastName(name); +const name = 'Ryan McDermott' +const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; @@ -602,14 +601,14 @@ would be much better to just use ES6 classes and simply extend the `Array` globa **Bad:** ```javascript Array.prototype.diff = function(comparisonArray) { - var values = []; - var hash = {}; + const values = []; + const hash = {}; - for (var i of comparisonArray) { + for (let i of comparisonArray) { hash[i] = true; } - for (var i of this) { + for (let i of this) { if (!hash[i]) { values.push(i); } @@ -627,14 +626,14 @@ class SuperArray extends Array { } diff(comparisonArray) { - var values = []; - var hash = {}; + const values = []; + const hash = {}; - for (var i of comparisonArray) { + for (let i of comparisonArray) { hash[i] = true; } - for (var i of this) { + for (let i of this) { if (!hash[i]) { values.push(i); } @@ -670,9 +669,9 @@ const programmerOutput = [ } ]; -var totalOutput = 0; +let totalOutput = 0; -for (var i = 0; i < programmerOutput.length; i++) { +for (let i = 0; i < programmerOutput.length; i++) { totalOutput += programmerOutput[i].linesOfCode; } ``` @@ -695,7 +694,7 @@ const programmerOutput = [ } ]; -var totalOutput = programmerOutput +const totalOutput = programmerOutput .map((programmer) => programmer.linesOfCode) .reduce((acc, linesOfCode) => acc + linesOfCode, 0); ``` @@ -871,14 +870,14 @@ they are fixed if they can be. // On old browsers, each iteration would be costly because `len` would be // recomputed. In modern browsers, this is optimized. -for (var i = 0, len = list.length; i < len; i++) { +for (let i = 0, len = list.length; i < len; i++) { // ... } ``` **Good**: ```javascript -for (var i = 0; i < list.length; i++) { +for (let i = 0; i < list.length; i++) { // ... } ``` @@ -899,7 +898,7 @@ function newRequestModule(url) { // ... } -var req = newRequestModule; +const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` @@ -910,7 +909,7 @@ function newRequestModule(url) { // ... } -var req = newRequestModule; +const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` **[⬆ back to top](#table-of-contents)** @@ -941,7 +940,7 @@ class BankAccount { } } -let bankAccount = new BankAccount(); +const bankAccount = new BankAccount(); // Buy shoes... bankAccount.balance = bankAccount.balance - 100; @@ -962,7 +961,7 @@ class BankAccount { } } -let bankAccount = new BankAccount(); +const bankAccount = new BankAccount(); // Buy shoes... bankAccount.withdraw(100); @@ -976,7 +975,7 @@ This can be accomplished through closures (for ES5 and below). **Bad:** ```javascript -var Employee = function(name) { +const Employee = function(name) { this.name = name; } @@ -984,7 +983,7 @@ Employee.prototype.getName = function() { return this.name; } -var employee = new Employee('John Doe'); +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 @@ -992,7 +991,7 @@ console.log('Employee name: ' + employee.getName()); // Employee name: undefined **Good**: ```javascript -var Employee = (function() { +const Employee = (function() { function Employee(name) { this.getName = function() { return name; @@ -1002,7 +1001,7 @@ var Employee = (function() { return Employee; }()); -var employee = new Employee('John Doe'); +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: John Doe @@ -1241,12 +1240,12 @@ function renderLargeShapes(shapes) { shape.setHeight(5); } - let area = shape.getArea(); + const area = shape.getArea(); shape.render(area); }) } -let shapes = [new Rectangle(), new Rectangle(), new Square()]; +const shapes = [new Rectangle(), new Rectangle(), new Square()]; renderLargeShapes(shapes); ``` **[⬆ back to top](#table-of-contents)** @@ -1283,7 +1282,7 @@ class DOMTraverser { } } -let $ = new DOMTraverser({ +const $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), animationModule: function() {} // Most of the time, we won't need to animate when traversing. // ... @@ -1316,9 +1315,9 @@ class DOMTraverser { } } -let $ = new DOMTraverser({ +const $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), - options: { + options: { animationModule: function() {} } }); @@ -1374,7 +1373,7 @@ class InventoryRequester { } } -let inventoryTracker = new InventoryTracker(['apples', 'bananas']); +const inventoryTracker = new InventoryTracker(['apples', 'bananas']); inventoryTracker.requestItems(); ``` @@ -1415,7 +1414,7 @@ class InventoryRequesterV2 { // By constructing our dependencies externally and injecting them, we can easily // substitute our request module for a fancy new one that uses WebSockets. -let inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); +const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); inventoryTracker.requestItems(); ``` **[⬆ back to top](#table-of-contents)** @@ -1428,7 +1427,7 @@ classes until you find yourself needing larger and more complex objects. **Bad:** ```javascript -var Animal = function(age) { +const Animal = function(age) { if (!(this instanceof Animal)) { throw new Error("Instantiate Animal with `new`"); } @@ -1438,7 +1437,7 @@ var Animal = function(age) { Animal.prototype.move = function() {}; -var Mammal = function(age, furColor) { +const Mammal = function(age, furColor) { if (!(this instanceof Mammal)) { throw new Error("Instantiate Mammal with `new`"); } @@ -1451,7 +1450,7 @@ Mammal.prototype = Object.create(Animal.prototype); Mammal.prototype.constructor = Mammal; Mammal.prototype.liveBirth = function() {}; -var Human = function(age, furColor, languageSpoken) { +const Human = function(age, furColor, languageSpoken) { if (!(this instanceof Human)) { throw new Error("Instantiate Human with `new`"); } @@ -1531,7 +1530,7 @@ class Car { } } -let car = new Car(); +const car = new Car(); car.setColor('pink'); car.setMake('Ford'); car.setModel('F-150') @@ -1570,7 +1569,7 @@ class Car { } } -let car = new Car() +const car = new Car() .setColor('pink') .setMake('Ford') .setModel('F-150') @@ -1693,19 +1692,19 @@ const assert = require('assert'); describe('MakeMomentJSGreatAgain', function() { it('handles 30-day months', function() { - let date = new MakeMomentJSGreatAgain('1/1/2015'); + const date = new MakeMomentJSGreatAgain('1/1/2015'); date.addDays(30); date.shouldEqual('1/31/2015'); }); it('handles leap year', function() { - let date = new MakeMomentJSGreatAgain('2/1/2016'); + cosnt date = new MakeMomentJSGreatAgain('2/1/2016'); date.addDays(28); assert.equal('02/29/2016', date); }); it('handles non-leap year', function() { - let date = new MakeMomentJSGreatAgain('2/1/2015'); + cosnt date = new MakeMomentJSGreatAgain('2/1/2015'); date.addDays(28); assert.equal('03/01/2015', date); }); @@ -1779,9 +1778,9 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti ```javascript async function getCleanCodeArticle() { try { - var request = await require('request-promise') - var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); - var fileHandle = await require('fs-promise'); + const request = await require('request-promise') + const response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); + const fileHandle = await require('fs-promise'); await fileHandle.writeFile('article.html', response); console.log('File written'); @@ -1810,11 +1809,11 @@ they want. The point is, no matter what you all choose, just be consistent. **Bad:** ```javascript -var DAYS_IN_WEEK = 7; -var daysInMonth = 30; +const DAYS_IN_WEEK = 7; +const daysInMonth = 30; -var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; +const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restore_database() {} @@ -1825,11 +1824,11 @@ class Alpaca {} **Good**: ```javascript -var DAYS_IN_WEEK = 7; -var DAYS_IN_MONTH = 30; +const DAYS_IN_WEEK = 7; +const DAYS_IN_MONTH = 30; -var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -var artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; +const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +const artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restoreDatabase() {} @@ -1861,7 +1860,7 @@ class PerformanceReview { } getPeerReviews() { - let peers = this.lookupPeers(); + const peers = this.lookupPeers(); // ... } @@ -1872,7 +1871,7 @@ class PerformanceReview { } getManagerReview() { - let manager = this.lookupManager(); + const manager = this.lookupManager(); } getSelfReview() { @@ -1898,7 +1897,7 @@ class PerformanceReview { } getPeerReviews() { - let peers = this.lookupPeers(); + const peers = this.lookupPeers(); // ... } @@ -1907,7 +1906,7 @@ class PerformanceReview { } getManagerReview() { - let manager = this.lookupManager(); + const manager = this.lookupManager(); } lookupMananger() { @@ -1933,15 +1932,15 @@ Comments are an apology, not a requirement. Good code *mostly* documents itself. ```javascript function hashIt(data) { // The hash - var hash = 0; + let hash = 0; // Length of string - var length = data.length; + const length = data.length; // Loop through every character in data - for (var i = 0; i < length; i++) { + for (let i = 0; i < length; i++) { // Get character code. - var char = data.charCodeAt(i); + const char = data.charCodeAt(i); // Make the hash hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer @@ -1954,11 +1953,11 @@ function hashIt(data) { ```javascript function hashIt(data) { - var hash = 0; - var length = data.length; + let hash = 0; + const length = data.length; - for (var i = 0; i < length; i++) { - var char = data.charCodeAt(i); + for (let i = 0; i < length; i++) { + const char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer @@ -2020,7 +2019,7 @@ proper indentation and formatting give the visual structure to your code. //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation //////////////////////////////////////////////////////////////////////////////// -let $scope.model = { +const $scope.model = { menu: 'foo', nav: 'bar' }; @@ -2028,19 +2027,19 @@ let $scope.model = { //////////////////////////////////////////////////////////////////////////////// // Action setup //////////////////////////////////////////////////////////////////////////////// -let actions = function() { +const actions = function() { // ... } ``` **Good**: ```javascript -let $scope.model = { +const $scope.model = { menu: 'foo', nav: 'bar' }; -let actions = function() { +const actions = function() { // ... } ``` From 5da0302c73523a90a3c4de2a14bec76d503b6e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Scho=CC=88nwald?= Date: Sat, 7 Jan 2017 15:06:29 +0100 Subject: [PATCH 114/170] Fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 036ce79f..7616bd7c 100644 --- a/README.md +++ b/README.md @@ -1698,13 +1698,13 @@ describe('MakeMomentJSGreatAgain', function() { }); it('handles leap year', function() { - cosnt date = new MakeMomentJSGreatAgain('2/1/2016'); + const date = new MakeMomentJSGreatAgain('2/1/2016'); date.addDays(28); assert.equal('02/29/2016', date); }); it('handles non-leap year', function() { - cosnt date = new MakeMomentJSGreatAgain('2/1/2015'); + const date = new MakeMomentJSGreatAgain('2/1/2015'); date.addDays(28); assert.equal('03/01/2015', date); }); From e35533fdd6383ba94a42b01f86c39945e7ce764b Mon Sep 17 00:00:00 2001 From: Stewart Rand Date: Fri, 6 Jan 2017 14:34:14 -0400 Subject: [PATCH 115/170] Correct spelling of Cessna airplane --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7616bd7c..77d73dc2 100644 --- a/README.md +++ b/README.md @@ -766,7 +766,7 @@ class Airplane { return getMaxAltitude() - getPassengerCount(); case 'Air Force One': return getMaxAltitude(); - case 'Cesna': + case 'Cessna': return getMaxAltitude() - getFuelExpenditure(); } } @@ -793,7 +793,7 @@ class AirForceOne extends Airplane { } } -class Cesna extends Airplane { +class Cessna extends Airplane { //... getCruisingAltitude() { return getMaxAltitude() - getFuelExpenditure(); From fe03625698adb5772927a6e3c8f2578d5449abe5 Mon Sep 17 00:00:00 2001 From: Acidio Alan Date: Fri, 6 Jan 2017 11:45:17 -0200 Subject: [PATCH 116/170] Adjusted the commentthat show the result --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 77d73dc2..d5e6d500 100644 --- a/README.md +++ b/README.md @@ -507,7 +507,7 @@ function createMenu(config) { cancellable: true }, config); - // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config now equals: {title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true} // ... } From a74ba88c966161b813d49cda986ea03aeb6a18aa Mon Sep 17 00:00:00 2001 From: Jordalgo Date: Fri, 6 Jan 2017 11:41:13 -0500 Subject: [PATCH 117/170] Add: Error Handling Section --- README.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d5e6d500..db58b0d6 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,9 @@ 5. [Classes](#classes) 6. [Testing](#testing) 7. [Concurrency](#concurrency) - 8. [Formatting](#formatting) - 9. [Comments](#comments) + 8. [Error Handling](#error-handling) + 9. [Formatting](#formatting) + 10. [Comments](#comments) ## Introduction ![Humorous image of software quality estimation as a count of how many expletives @@ -1792,6 +1793,79 @@ async function getCleanCodeArticle() { **[⬆ back to top](#table-of-contents)** +## **Error Handling** +Thrown errors are a good thing! They mean the runtime has successfully +identified when something in your program has gone wrong and it's letting +you know by stopping function execution on the current stack, killing the +process (in Node), and notifying you in the console with a stack trace. + +### Don't ignore caught errors +Doing nothing with a caught error doesn't give you the ability to ever fix +or react to said error. Logging the error to the console (`console.log`) +isn't much better as often times it can get lost in a sea of things printed +to the console. If you wrap any bit of code in a `try/catch` it means you +think an error may occur there and therefore you should have a plan, +or create a code path, for when it occurs. + +**Bad:** +```javascript +try { + functionThatMightThrow(); +} catch (error) { + console.log(error); +} +``` + +**Good:** +```javascript +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! +} +``` + +### Don't ignore rejected promises +For the same reason you shouldn't ignore caught errors +from `try/catch`. + +**Bad:** +```javascript +getdata() +.then(data => { + functionThatMightThrow(data); +}) +.catch(error => { + console.log(error); +}); +``` + +**Good:** +```javascript +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! +}); +``` + +**[⬆ back to top](#table-of-contents)** + + ## **Formatting** Formatting is subjective. Like many rules herein, there is no hard and fast rule that you must follow. The main point is DO NOT ARGUE over formatting. From 509260bac42f804d82ea3087b2a7b3d5f12fc569 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Sat, 7 Jan 2017 17:35:08 +0200 Subject: [PATCH 118/170] fix indentation in a code example --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db58b0d6..018b194e 100644 --- a/README.md +++ b/README.md @@ -1786,9 +1786,9 @@ async function getCleanCodeArticle() { await fileHandle.writeFile('article.html', response); console.log('File written'); } catch(err) { - console.log(err); - } + console.log(err); } +} ``` **[⬆ back to top](#table-of-contents)** From 9272fa29f3eba4d77bfb5d727e88587e2a96cade Mon Sep 17 00:00:00 2001 From: rodu Date: Sat, 7 Jan 2017 11:10:13 +0000 Subject: [PATCH 119/170] Improvement on email client function In the section "Functions should do one thing" this change uses the Array.filter method to get the active clients, then emailing those with the forEach loop. --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 018b194e..8bc941da 100644 --- a/README.md +++ b/README.md @@ -274,15 +274,9 @@ function emailClients(clients) { **Good**: ```javascript function emailClients(clients) { - clients.forEach(client => { - emailClientIfNeeded(client); - }); -} - -function emailClientIfNeeded(client) { - if (isClientActive(client)) { - email(client); - } + clients + .filter(isClientActive) + .forEach(email); } function isClientActive(client) { From deef735a8d0e610f16c6dca597380575f36f73a5 Mon Sep 17 00:00:00 2001 From: "Daniel St. Jules" Date: Fri, 6 Jan 2017 21:49:27 -0800 Subject: [PATCH 120/170] Add mention of jsinspect for duplicate code detection --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bc941da..69b1fafa 100644 --- a/README.md +++ b/README.md @@ -384,7 +384,9 @@ Never ever, ever, under any circumstance, have duplicate code. There's no reason for it and it's quite possibly the worst sin you can commit as a professional developer. Duplicate code means there's more than one place to alter something if you need to change some logic. JavaScript is untyped, so it makes having -generic functions quite easy. Take advantage of that! +generic functions quite easy. Take advantage of that! Tools like +[jsinpect](https://github.com/danielstjules/jsinspect) can help you find duplicate +code eligible for refactoring. **Bad:** ```javascript From 3adcc2c42da13dccd19c7a78adff07c2ecd27290 Mon Sep 17 00:00:00 2001 From: Jhonatan Salguero Villa Date: Fri, 6 Jan 2017 23:52:14 -0500 Subject: [PATCH 121/170] return true on save method to continue chaining The save method isn't returning this, so the car variable is equal to undefined instead of a Car object. BTW, Thanks alot for this guide. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 69b1fafa..d241f216 100644 --- a/README.md +++ b/README.md @@ -1563,6 +1563,8 @@ class Car { save() { console.log(this.make, this.model, this.color); + // NOTE: Returning this for chaining + return this; } } From 54796aad8cc68a2210c965515341b41b27327e0a Mon Sep 17 00:00:00 2001 From: Dara Hak Date: Sat, 7 Jan 2017 14:58:18 +0100 Subject: [PATCH 122/170] Mention async functions as part of ES2017 instead of ES7 (ES2016) + switch to "ES/ES" when mentioning ECMAScript versions. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d241f216..f757f078 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ const yearMonthDay = moment().format('YYYY/MM/DD'); ``` **[⬆ back to top](#table-of-contents)** -### Use ES6 constants when variable values do not change +### Use ES2015/ES6 constants when variable values do not change In the bad example, the variable can be changed. When you declare a constant, the variable should stay the same throughout the program. @@ -593,7 +593,7 @@ show the difference between two arrays? You could write your new function to the `Array.prototype`, but it could clash with another library that tried to do the same thing. What if that other library was just using `diff` to find the difference between the first and last elements of an array? This is why it -would be much better to just use ES6 classes and simply extend the `Array` global. +would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. **Bad:** ```javascript @@ -1416,7 +1416,7 @@ inventoryTracker.requestItems(); ``` **[⬆ back to top](#table-of-contents)** -### Prefer ES6 classes over ES5 plain functions +### Prefer ES2015/ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware that you might not), then prefer classes. However, prefer small functions over @@ -1713,7 +1713,7 @@ describe('MakeMomentJSGreatAgain', function() { ## **Concurrency** ### Use Promises, not callbacks -Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, +Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, Promises are a built-in global type. Use them! **Bad:** @@ -1752,10 +1752,10 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti **[⬆ back to top](#table-of-contents)** ### Async/Await are even cleaner than Promises -Promises are a very clean alternative to callbacks, but ES7 brings async and await +Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await which offer an even cleaner solution. All you need is a function that is prefixed in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of functions. Use this if you can take advantage of ES7 features +a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features today! **Bad:** From b49352608adc2156fdea66a51729b2568760f15f Mon Sep 17 00:00:00 2001 From: Duncan Bay Date: Sat, 7 Jan 2017 10:51:44 +0700 Subject: [PATCH 123/170] fix output in Object.assign() example Something looked off about this so I did a quick check in node. You must have been distracted at the time of writing :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f757f078..7c95c784 100644 --- a/README.md +++ b/README.md @@ -504,7 +504,7 @@ function createMenu(config) { cancellable: true }, config); - // config now equals: {title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true} + // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } From fc0f5c7303f0c0ed087284936c67db3a4522bfd6 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 8 Jan 2017 09:51:29 -0800 Subject: [PATCH 124/170] Remove quip about JavaScript and Haskell being like beer types/brands --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7c95c784..4207a576 100644 --- a/README.md +++ b/README.md @@ -643,7 +643,6 @@ class SuperArray extends Array { **[⬆ back to top](#table-of-contents)** ### Favor functional programming over imperative programming -If Haskell were an IPA then JavaScript would be an O'Douls. That is to say, JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages are cleaner and easier to test. Favor this style of programming when you can. From d8c12c0c485f14e3618b2ff0dd0489fb4d4b8919 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 8 Jan 2017 09:53:54 -0800 Subject: [PATCH 125/170] Fix method chaining subsection typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4207a576..5ee8369b 100644 --- a/README.md +++ b/README.md @@ -1510,7 +1510,7 @@ class Car { } setMake(make) { - this.name = name; + this.make = make; } setModel(model) { @@ -1543,7 +1543,7 @@ class Car { } setMake(make) { - this.name = name; + this.make = make; // NOTE: Returning this for chaining return this; } From c05459838481f77733e1cc8806aaade2ce8ebb26 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 8 Jan 2017 09:59:06 -0800 Subject: [PATCH 126/170] Remove ES6 constants subsection We need a better explanation for when to prefer `const` over var/let, as this has confused many readers. --- README.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/README.md b/README.md index 5ee8369b..c7bded3d 100644 --- a/README.md +++ b/README.md @@ -53,24 +53,6 @@ const yearMonthDay = moment().format('YYYY/MM/DD'); ``` **[⬆ back to top](#table-of-contents)** -### Use ES2015/ES6 constants when variable values do not change -In the bad example, the variable can be changed. -When you declare a constant, the variable should stay -the same throughout the program. - - -**Bad:** -```javascript -var FIRST_US_PRESIDENT = "George Washington"; -``` - -**Good**: -```javascript -const FIRST_US_PRESIDENT = "George Washington"; -``` -**[⬆ back to top](#table-of-contents)** - - ### Use the same vocabulary for the same type of variable **Bad:** From 41b9ee43eebe98be4491a2f385e0f638554d600c Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 8 Jan 2017 10:04:02 -0800 Subject: [PATCH 127/170] Remove subsection regarding legal comments in source files --- README.md | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/README.md b/README.md index c7bded3d..cfda1701 100644 --- a/README.md +++ b/README.md @@ -2097,45 +2097,3 @@ const actions = function() { } ``` **[⬆ back to top](#table-of-contents)** - -### Avoid legal comments in source files -That's what your `LICENSE` file at the top of your source tree is for. - -**Bad:** -```javascript -/* -The MIT License (MIT) - -Copyright (c) 2016 Ryan McDermott - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE -*/ - -function calculateBill() { - // ... -} -``` - -**Good**: -```javascript -function calculateBill() { - // ... -} -``` -**[⬆ back to top](#table-of-contents)** From 0c12d9b70dcc665b1c7811a96b3bf079b7792a92 Mon Sep 17 00:00:00 2001 From: Jhonatan Salguero Villa Date: Sat, 7 Jan 2017 00:16:13 -0500 Subject: [PATCH 128/170] use this to access object methods --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cfda1701..fe551458 100644 --- a/README.md +++ b/README.md @@ -1918,9 +1918,9 @@ class PerformanceReview { } perfReview() { - getPeerReviews(); - getManagerReview(); - getSelfReview(); + this.getPeerReviews(); + this.getManagerReview(); + this.getSelfReview(); } getManagerReview() { @@ -1944,9 +1944,9 @@ class PerformanceReview { } perfReview() { - getPeerReviews(); - getManagerReview(); - getSelfReview(); + this.getPeerReviews(); + this.getManagerReview(); + this.getSelfReview(); } getPeerReviews() { From 99b7116c244fa0b7beaa8e656dc7384a60d28093 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 8 Jan 2017 10:14:38 -0800 Subject: [PATCH 129/170] Refactor flags exmaple to be DRY --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fe551458..5ce9226d 100644 --- a/README.md +++ b/README.md @@ -511,13 +511,13 @@ function createFile(name, temp) { **Good**: ```javascript -function createTempFile(name) { - fs.create('./temp/' + name); -} - function createFile(name) { fs.create(name); } + +function createTempFile(name) { + createFile('./temp/' + name); +} ``` **[⬆ back to top](#table-of-contents)** From 68300c2c3b735bf6d9aca90bcbe0fd71b07076af Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Sat, 7 Jan 2017 15:14:26 +0200 Subject: [PATCH 130/170] fix some typos in the README.md --- README.md | 344 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 204 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 5ce9226d..781d367c 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,33 @@ improvement. Beat up the code instead! **Bad:** ```javascript -const yyyymmdstr = moment().format('YYYY/MM/DD'); +var yyyymmdstr = moment().format('YYYY/MM/DD'); ``` **Good**: ```javascript -const yearMonthDay = moment().format('YYYY/MM/DD'); +var yearMonthDay = moment().format('YYYY/MM/DD'); ``` **[⬆ back to top](#table-of-contents)** +### Use ES6 constants when variable values do not change +In the bad example, the variable can be changed. +When you declare a constant, the variable should stay +the same throughout the program. + + +**Bad:** +```javascript +var FIRST_US_PRESIDENT = "George Washington"; +``` + +**Good**: +```javascript +const FIRST_US_PRESIDENT = "George Washington"; +``` +**[⬆ back to top](#table-of-contents)** + + ### Use the same vocabulary for the same type of variable **Bad:** @@ -77,16 +95,16 @@ Make your names searchable. **Bad:** ```javascript // What the heck is 525600 for? -for (let i = 0; i < 525600; i++) { +for (var i = 0; i < 525600; i++) { runCronJob(); } ``` **Good**: ```javascript -// Declare them as capitalized `const` globals. -const MINUTES_IN_A_YEAR = 525600; -for (let i = 0; i < MINUTES_IN_A_YEAR; i++) { +// Declare them as capitalized `var` globals. +var MINUTES_IN_A_YEAR = 525600; +for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { runCronJob(); } ``` @@ -114,7 +132,7 @@ Explicit is better than implicit. **Bad:** ```javascript -const locations = ['Austin', 'New York', 'San Francisco']; +var locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { doStuff(); doSomeOtherStuff(); @@ -128,7 +146,7 @@ locations.forEach((l) => { **Good**: ```javascript -const locations = ['Austin', 'New York', 'San Francisco']; +var locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { doStuff(); doSomeOtherStuff(); @@ -146,7 +164,7 @@ variable name. **Bad:** ```javascript -const Car = { +var Car = { carMake: 'Honda', carModel: 'Accord', carColor: 'Blue' @@ -159,7 +177,7 @@ function paintCar(car) { **Good**: ```javascript -const Car = { +var Car = { make: 'Honda', model: 'Accord', color: 'Blue' @@ -176,7 +194,7 @@ function paintCar(car) { **Bad:** ```javascript function createMicrobrewery(name) { - let breweryName; + var breweryName; if (name) { breweryName = name; } else { @@ -188,7 +206,7 @@ function createMicrobrewery(name) { **Good**: ```javascript function createMicrobrewery(name) { - const breweryName = name || 'Hipster Brew Co.' + var breweryName = name || 'Hipster Brew Co.' } ``` **[⬆ back to top](#table-of-contents)** @@ -219,7 +237,7 @@ function createMenu(title, body, buttonText, cancellable) { **Good**: ```javascript -const menuConfig = { +var menuConfig = { title: 'Foo', body: 'Bar', buttonText: 'Baz', @@ -245,7 +263,7 @@ this guide other than this, you'll be ahead of many developers. ```javascript function emailClients(clients) { clients.forEach(client => { - const clientRecord = database.lookup(client); + let clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } @@ -256,13 +274,19 @@ function emailClients(clients) { **Good**: ```javascript function emailClients(clients) { - clients - .filter(isClientActive) - .forEach(email); + clients.forEach(client => { + emailClientIfNeeded(client); + }); +} + +function emailClientIfNeeded(client) { + if (isClientActive(client)) { + email(client); + } } function isClientActive(client) { - const clientRecord = database.lookup(client); + let clientRecord = database.lookup(client); return clientRecord.isActive(); } ``` @@ -276,7 +300,7 @@ function dateAdd(date, month) { // ... } -const date = new Date(); +let date = new Date(); // It's hard to to tell from the function name what is added dateAdd(date, 1); @@ -288,7 +312,7 @@ function dateAddMonth(date, month) { // ... } -const date = new Date(); +let date = new Date(); dateAddMonth(date, 1); ``` **[⬆ back to top](#table-of-contents)** @@ -301,19 +325,19 @@ testing. **Bad:** ```javascript function parseBetterJSAlternative(code) { - const REGEXES = [ + let REGEXES = [ // ... ]; - const statements = code.split(' '); - const tokens = []; + let statements = code.split(' '); + let tokens; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { // ... }) }); - const ast = []; + let ast; tokens.forEach((token) => { // lex... }); @@ -327,15 +351,15 @@ function parseBetterJSAlternative(code) { **Good**: ```javascript function tokenize(code) { - const REGEXES = [ + let REGEXES = [ // ... ]; - const statements = code.split(' '); - const tokens = []; + let statements = code.split(' '); + let tokens; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { - tokens.push( // ... ); + // ... }) }); @@ -343,17 +367,17 @@ function tokenize(code) { } function lexer(tokens) { - const ast = []; + let ast; tokens.forEach((token) => { - ast.push( // ... ); + // lex... }); return ast; } function parseBetterJSAlternative(code) { - const tokens = tokenize(code); - const ast = lexer(tokens); + let tokens = tokenize(code); + let ast = lexer(tokens); ast.forEach((node) => { // parse... }) @@ -366,18 +390,16 @@ Never ever, ever, under any circumstance, have duplicate code. There's no reason for it and it's quite possibly the worst sin you can commit as a professional developer. Duplicate code means there's more than one place to alter something if you need to change some logic. JavaScript is untyped, so it makes having -generic functions quite easy. Take advantage of that! Tools like -[jsinpect](https://github.com/danielstjules/jsinspect) can help you find duplicate -code eligible for refactoring. +generic functions quite easy. Take advantage of that! **Bad:** ```javascript function showDeveloperList(developers) { developers.forEach(developers => { - const expectedSalary = developer.calculateExpectedSalary(); - const experience = developer.getExperience(); - const githubLink = developer.getGithubLink(); - const data = { + var expectedSalary = developer.calculateExpectedSalary(); + var experience = developer.getExperience(); + var githubLink = developer.getGithubLink(); + var data = { expectedSalary: expectedSalary, experience: experience, githubLink: githubLink @@ -389,10 +411,10 @@ function showDeveloperList(developers) { function showManagerList(managers) { managers.forEach(manager => { - const expectedSalary = manager.calculateExpectedSalary(); - const experience = manager.getExperience(); - const portfolio = manager.getMBAProjects(); - const data = { + var expectedSalary = manager.calculateExpectedSalary(); + var experience = manager.getExperience(); + var portfolio = manager.getMBAProjects(); + var data = { expectedSalary: expectedSalary, experience: experience, portfolio: portfolio @@ -407,16 +429,17 @@ function showManagerList(managers) { ```javascript function showList(employees) { employees.forEach(employee => { - const expectedSalary = employee.calculateExpectedSalary(); - const experience = employee.getExperience(); - - let portfolio = employee.getGithubLink(); + var expectedSalary = employee.calculateExpectedSalary(); + var experience = employee.getExperience(); + var portfolio; if (employee.type === 'manager') { portfolio = employee.getMBAProjects(); + } else { + portfolio = employee.getGithubLink(); } - const data = { + var data = { expectedSalary: expectedSalary, experience: experience, portfolio: portfolio @@ -451,7 +474,7 @@ function writeForumComment(subject = 'No subject', body = 'No text') { **Bad:** ```javascript -const menuConfig = { +var menuConfig = { title: null, body: 'Bar', buttonText: null, @@ -471,7 +494,7 @@ createMenu(menuConfig); **Good**: ```javascript -const menuConfig = { +var menuConfig = { title: 'Order', // User did not include 'body' key buttonText: 'Send', @@ -486,7 +509,7 @@ function createMenu(config) { cancellable: true }, config); - // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config now equals: {title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true} // ... } @@ -511,12 +534,12 @@ function createFile(name, temp) { **Good**: ```javascript -function createFile(name) { - fs.create(name); +function createTempFile(name) { + fs.create('./temp/' + name); } -function createTempFile(name) { - createFile('./temp/' + name); +function createFile(name) { + fs.create(name); } ``` **[⬆ back to top](#table-of-contents)** @@ -541,7 +564,7 @@ be happier than the vast majority of other programmers. ```javascript // 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 name = 'Ryan McDermott'; +var name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name = name.split(' '); @@ -558,8 +581,8 @@ function splitIntoFirstAndLastName(name) { return name.split(' '); } -const name = 'Ryan McDermott' -const newName = splitIntoFirstAndLastName(name); +var name = 'Ryan McDermott' +var newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; @@ -575,19 +598,19 @@ show the difference between two arrays? You could write your new function to the `Array.prototype`, but it could clash with another library that tried to do the same thing. What if that other library was just using `diff` to find the difference between the first and last elements of an array? This is why it -would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. +would be much better to just use ES6 classes and simply extend the `Array` global. **Bad:** ```javascript Array.prototype.diff = function(comparisonArray) { - const values = []; - const hash = {}; + var values = []; + var hash = {}; - for (let i of comparisonArray) { + for (var i of comparisonArray) { hash[i] = true; } - for (let i of this) { + for (var i of this) { if (!hash[i]) { values.push(i); } @@ -605,14 +628,14 @@ class SuperArray extends Array { } diff(comparisonArray) { - const values = []; - const hash = {}; + var values = []; + var hash = {}; - for (let i of comparisonArray) { + for (var i of comparisonArray) { hash[i] = true; } - for (let i of this) { + for (var i of this) { if (!hash[i]) { values.push(i); } @@ -625,6 +648,7 @@ class SuperArray extends Array { **[⬆ back to top](#table-of-contents)** ### Favor functional programming over imperative programming +If Haskell were an IPA then JavaScript would be an O'Douls. That is to say, JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages are cleaner and easier to test. Favor this style of programming when you can. @@ -647,9 +671,9 @@ const programmerOutput = [ } ]; -let totalOutput = 0; +var totalOutput = 0; -for (let i = 0; i < programmerOutput.length; i++) { +for (var i = 0; i < programmerOutput.length; i++) { totalOutput += programmerOutput[i].linesOfCode; } ``` @@ -672,7 +696,7 @@ const programmerOutput = [ } ]; -const totalOutput = programmerOutput +var totalOutput = programmerOutput .map((programmer) => programmer.linesOfCode) .reduce((acc, linesOfCode) => acc + linesOfCode, 0); ``` @@ -812,7 +836,7 @@ you should consider using TypeScript. It is an excellent alternative to normal JavaScript, as it provides you with static typing on top of standard JavaScript syntax. The problem with manually type-checking normal JavaScript is that doing it well requires so much extra verbiage that the faux "type-safety" you get -doesn't make up for the lost readability. Keep your JavaScript, clean, write +doesn't make up for the lost readability. Keep your JavaScript clean, write good tests, and have good code reviews. Otherwise, do all of that but with TypeScript (which, like I said, is a great alternative!). @@ -846,16 +870,16 @@ they are fixed if they can be. **Bad:** ```javascript -// On old browsers, each iteration would be costly because `len` would be -// recomputed. In modern browsers, this is optimized. -for (let i = 0, len = list.length; i < len; i++) { +// On old browsers, each iteration with uncached `list.length` would be costly +// because of `list.length` recomputation. In modern browsers, this is optimized. +for (var i = 0, len = list.length; i < len; i++) { // ... } ``` **Good**: ```javascript -for (let i = 0; i < list.length; i++) { +for (var i = 0; i < list.length; i++) { // ... } ``` @@ -876,7 +900,7 @@ function newRequestModule(url) { // ... } -const req = newRequestModule; +var req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` @@ -887,7 +911,7 @@ function newRequestModule(url) { // ... } -const req = newRequestModule; +var req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` **[⬆ back to top](#table-of-contents)** @@ -918,7 +942,7 @@ class BankAccount { } } -const bankAccount = new BankAccount(); +let bankAccount = new BankAccount(); // Buy shoes... bankAccount.balance = bankAccount.balance - 100; @@ -939,7 +963,7 @@ class BankAccount { } } -const bankAccount = new BankAccount(); +let bankAccount = new BankAccount(); // Buy shoes... bankAccount.withdraw(100); @@ -953,7 +977,7 @@ This can be accomplished through closures (for ES5 and below). **Bad:** ```javascript -const Employee = function(name) { +var Employee = function(name) { this.name = name; } @@ -961,7 +985,7 @@ Employee.prototype.getName = function() { return this.name; } -const employee = new Employee('John Doe'); +var 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 @@ -969,7 +993,7 @@ console.log('Employee name: ' + employee.getName()); // Employee name: undefined **Good**: ```javascript -const Employee = (function() { +var Employee = (function() { function Employee(name) { this.getName = function() { return name; @@ -979,7 +1003,7 @@ const Employee = (function() { return Employee; }()); -const employee = new Employee('John Doe'); +var 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: John Doe @@ -1218,12 +1242,12 @@ function renderLargeShapes(shapes) { shape.setHeight(5); } - const area = shape.getArea(); + let area = shape.getArea(); shape.render(area); }) } -const shapes = [new Rectangle(), new Rectangle(), new Square()]; +let shapes = [new Rectangle(), new Rectangle(), new Square()]; renderLargeShapes(shapes); ``` **[⬆ back to top](#table-of-contents)** @@ -1260,7 +1284,7 @@ class DOMTraverser { } } -const $ = new DOMTraverser({ +let $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), animationModule: function() {} // Most of the time, we won't need to animate when traversing. // ... @@ -1293,7 +1317,7 @@ class DOMTraverser { } } -const $ = new DOMTraverser({ +let $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), options: { animationModule: function() {} @@ -1351,7 +1375,7 @@ class InventoryRequester { } } -const inventoryTracker = new InventoryTracker(['apples', 'bananas']); +let inventoryTracker = new InventoryTracker(['apples', 'bananas']); inventoryTracker.requestItems(); ``` @@ -1392,12 +1416,12 @@ class InventoryRequesterV2 { // 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()); +let inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); inventoryTracker.requestItems(); ``` **[⬆ back to top](#table-of-contents)** -### Prefer ES2015/ES6 classes over ES5 plain functions +### Prefer ES6 classes over ES5 plain functions It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware that you might not), then prefer classes. However, prefer small functions over @@ -1405,7 +1429,7 @@ classes until you find yourself needing larger and more complex objects. **Bad:** ```javascript -const Animal = function(age) { +var Animal = function(age) { if (!(this instanceof Animal)) { throw new Error("Instantiate Animal with `new`"); } @@ -1415,7 +1439,7 @@ const Animal = function(age) { Animal.prototype.move = function() {}; -const Mammal = function(age, furColor) { +var Mammal = function(age, furColor) { if (!(this instanceof Mammal)) { throw new Error("Instantiate Mammal with `new`"); } @@ -1428,7 +1452,7 @@ Mammal.prototype = Object.create(Animal.prototype); Mammal.prototype.constructor = Mammal; Mammal.prototype.liveBirth = function() {}; -const Human = function(age, furColor, languageSpoken) { +var Human = function(age, furColor, languageSpoken) { if (!(this instanceof Human)) { throw new Error("Instantiate Human with `new`"); } @@ -1492,7 +1516,7 @@ class Car { } setMake(make) { - this.make = make; + this.name = name; } setModel(model) { @@ -1508,7 +1532,7 @@ class Car { } } -const car = new Car(); +let car = new Car(); car.setColor('pink'); car.setMake('Ford'); car.setModel('F-150') @@ -1525,7 +1549,7 @@ class Car { } setMake(make) { - this.make = make; + this.name = name; // NOTE: Returning this for chaining return this; } @@ -1544,12 +1568,10 @@ class Car { save() { console.log(this.make, this.model, this.color); - // NOTE: Returning this for chaining - return this; } } -const car = new Car() +let car = new Car() .setColor('pink') .setMake('Ford') .setModel('F-150') @@ -1672,19 +1694,19 @@ const assert = require('assert'); describe('MakeMomentJSGreatAgain', function() { it('handles 30-day months', function() { - const date = new MakeMomentJSGreatAgain('1/1/2015'); + let date = new MakeMomentJSGreatAgain('1/1/2015'); date.addDays(30); date.shouldEqual('1/31/2015'); }); it('handles leap year', function() { - const date = new MakeMomentJSGreatAgain('2/1/2016'); + let date = new MakeMomentJSGreatAgain('2/1/2016'); date.addDays(28); assert.equal('02/29/2016', date); }); it('handles non-leap year', function() { - const date = new MakeMomentJSGreatAgain('2/1/2015'); + let date = new MakeMomentJSGreatAgain('2/1/2015'); date.addDays(28); assert.equal('03/01/2015', date); }); @@ -1694,7 +1716,7 @@ describe('MakeMomentJSGreatAgain', function() { ## **Concurrency** ### Use Promises, not callbacks -Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, +Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, Promises are a built-in global type. Use them! **Bad:** @@ -1733,10 +1755,10 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti **[⬆ back to top](#table-of-contents)** ### Async/Await are even cleaner than Promises -Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await +Promises are a very clean alternative to callbacks, but ES7 brings async and await which offer an even cleaner solution. All you need is a function that is prefixed in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features +a `then` chain of functions. Use this if you can take advantage of ES7 features today! **Bad:** @@ -1758,16 +1780,16 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti ```javascript async function getCleanCodeArticle() { try { - const request = await require('request-promise') - const response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); - const fileHandle = await require('fs-promise'); + var request = await require('request-promise') + var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); + var fileHandle = await require('fs-promise'); await fileHandle.writeFile('article.html', response); console.log('File written'); } catch(err) { - console.log(err); + console.log(err); + } } -} ``` **[⬆ back to top](#table-of-contents)** @@ -1862,11 +1884,11 @@ they want. The point is, no matter what you all choose, just be consistent. **Bad:** ```javascript -const DAYS_IN_WEEK = 7; -const daysInMonth = 30; +var DAYS_IN_WEEK = 7; +var daysInMonth = 30; -const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; +var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restore_database() {} @@ -1877,11 +1899,11 @@ class Alpaca {} **Good**: ```javascript -const DAYS_IN_WEEK = 7; -const DAYS_IN_MONTH = 30; +var DAYS_IN_WEEK = 7; +var DAYS_IN_MONTH = 30; -const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -const artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; +var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +var artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restoreDatabase() {} @@ -1913,18 +1935,18 @@ class PerformanceReview { } getPeerReviews() { - const peers = this.lookupPeers(); + let peers = this.lookupPeers(); // ... } perfReview() { - this.getPeerReviews(); - this.getManagerReview(); - this.getSelfReview(); + getPeerReviews(); + getManagerReview(); + getSelfReview(); } getManagerReview() { - const manager = this.lookupManager(); + let manager = this.lookupManager(); } getSelfReview() { @@ -1944,13 +1966,13 @@ class PerformanceReview { } perfReview() { - this.getPeerReviews(); - this.getManagerReview(); - this.getSelfReview(); + getPeerReviews(); + getManagerReview(); + getSelfReview(); } getPeerReviews() { - const peers = this.lookupPeers(); + let peers = this.lookupPeers(); // ... } @@ -1959,7 +1981,7 @@ class PerformanceReview { } getManagerReview() { - const manager = this.lookupManager(); + let manager = this.lookupManager(); } lookupMananger() { @@ -1985,15 +2007,15 @@ Comments are an apology, not a requirement. Good code *mostly* documents itself. ```javascript function hashIt(data) { // The hash - let hash = 0; + var hash = 0; // Length of string - const length = data.length; + var length = data.length; // Loop through every character in data - for (let i = 0; i < length; i++) { + for (var i = 0; i < length; i++) { // Get character code. - const char = data.charCodeAt(i); + var char = data.charCodeAt(i); // Make the hash hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer @@ -2006,11 +2028,11 @@ function hashIt(data) { ```javascript function hashIt(data) { - let hash = 0; - const length = data.length; + var hash = 0; + var length = data.length; - for (let i = 0; i < length; i++) { - const char = data.charCodeAt(i); + for (var i = 0; i < length; i++) { + var char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer @@ -2072,7 +2094,7 @@ proper indentation and formatting give the visual structure to your code. //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation //////////////////////////////////////////////////////////////////////////////// -const $scope.model = { +let $scope.model = { menu: 'foo', nav: 'bar' }; @@ -2080,19 +2102,61 @@ const $scope.model = { //////////////////////////////////////////////////////////////////////////////// // Action setup //////////////////////////////////////////////////////////////////////////////// -const actions = function() { +let actions = function() { // ... } ``` **Good**: ```javascript -const $scope.model = { +let $scope.model = { menu: 'foo', nav: 'bar' }; -const actions = function() { +let actions = function() { + // ... +} +``` +**[⬆ back to top](#table-of-contents)** + +### Avoid legal comments in source files +That's what your `LICENSE` file at the top of your source tree is for. + +**Bad:** +```javascript +/* +The MIT License (MIT) + +Copyright (c) 2016 Ryan McDermott + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE +*/ + +function calculateBill() { + // ... +} +``` + +**Good**: +```javascript +function calculateBill() { // ... } ``` From c5c23b59bd437520285f4a317ad0631ce121892f Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 13:54:38 +0200 Subject: [PATCH 131/170] italian translation --- README.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 781d367c..ba90f9b9 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ saveCityState(cityStateRegex.match(cityStateRegex)[1], cityStateRegex.match(city **Good**: ```javascript const cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; -const match = cityStateRegex.match(cityStateRegex) +const match = cityStateRegex.match(cityStateRegex); const city = match[1]; const state = match[2]; saveCityState(city, state); @@ -334,7 +334,7 @@ function parseBetterJSAlternative(code) { REGEXES.forEach((REGEX) => { statements.forEach((statement) => { // ... - }) + }); }); let ast; @@ -344,7 +344,7 @@ function parseBetterJSAlternative(code) { ast.forEach((node) => { // parse... - }) + }); } ``` @@ -359,8 +359,13 @@ function tokenize(code) { let tokens; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { +<<<<<<< HEAD // ... }) +======= + tokens.push( // ... ); + }); +>>>>>>> fadfb57... Fill missing commas }); return tokens; @@ -380,7 +385,7 @@ function parseBetterJSAlternative(code) { let ast = lexer(tokens); ast.forEach((node) => { // parse... - }) + }); } ``` **[⬆ back to top](#table-of-contents)** @@ -1057,7 +1062,7 @@ class UserAuth { class UserSettings { constructor(user) { this.user = user; - this.auth = new UserAuth(user) + this.auth = new UserAuth(user); } changeSettings(settings) { @@ -1176,7 +1181,7 @@ function renderLargeRectangles(rectangles) { rectangle.setHeight(5); let area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20. rectangle.render(area); - }) + }); } let rectangles = [new Rectangle(), new Rectangle(), new Square()]; @@ -1244,7 +1249,7 @@ function renderLargeShapes(shapes) { let area = shape.getArea(); shape.render(area); - }) + }); } let shapes = [new Rectangle(), new Rectangle(), new Square()]; @@ -1535,7 +1540,7 @@ class Car { let car = new Car(); car.setColor('pink'); car.setMake('Ford'); -car.setModel('F-150') +car.setModel('F-150'); car.save(); ``` @@ -1732,9 +1737,9 @@ require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', func } else { console.log('File written'); } - }) + }); } -}) +}); ``` @@ -1749,7 +1754,7 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti }) .catch(function(err) { console.error(err); - }) + }); ``` **[⬆ back to top](#table-of-contents)** @@ -1772,7 +1777,7 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti }) .catch(function(err) { console.error(err); - }) + }); ``` @@ -1780,9 +1785,15 @@ require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Marti ```javascript async function getCleanCodeArticle() { try { +<<<<<<< HEAD var request = await require('request-promise') var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); var fileHandle = await require('fs-promise'); +======= + const request = await require('request-promise'); + const response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); + const fileHandle = await require('fs-promise'); +>>>>>>> fadfb57... Fill missing commas await fileHandle.writeFile('article.html', response); console.log('File written'); From aea143ea035e13bf17280cb9992384d2a913ee0d Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 13:59:03 +0200 Subject: [PATCH 132/170] italian translation --- README.md | 2232 +++++++++++++++++++++++++---------------------------- 1 file changed, 1048 insertions(+), 1184 deletions(-) diff --git a/README.md b/README.md index ba90f9b9..7b141c6e 100644 --- a/README.md +++ b/README.md @@ -1,170 +1,144 @@ # clean-code-javascript -## Table of Contents - 1. [Introduction](#introduction) - 2. [Variables](#variables) - 3. [Functions](#functions) - 4. [Objects and Data Structures](#objects-and-data-structures) - 5. [Classes](#classes) - 6. [Testing](#testing) - 7. [Concurrency](#concurrency) - 8. [Error Handling](#error-handling) - 9. [Formatting](#formatting) - 10. [Comments](#comments) - -## Introduction -![Humorous image of software quality estimation as a count of how many expletives -you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) - -Software engineering principles, from Robert C. Martin's book +## Lista dei contenuti + 1. [Introduzione](#introduzione) + 2. [Variabili](#variabili) + 3. [Funzioni](#funzioni) + 4. [Ogetti e strutture dati](#objects-and-data-structures) + 5. [Classi](#Classi) + 6. [SOLID](#solid) + 7. [Test](#Test) + 8. [Concurrency](#concurrency) + 9. [Error Handling](#error-handling) + 10. [Formatting](#formatting) + 11. [Comments](#comments) + 12. [Translation](#translation) + +## Introduzione +![Immagine umoristica che rappresenta quanto sia possibile stimare la qualità di un software attraverso il numero di parolacce espresse durante la lettura del codice](http://www.osnews.com/images/comics/wtfm.jpg) + +Principi di Ingegneria del Software, dal libro di Robert C. Martin [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adapted for JavaScript. This is not a style guide. It's a guide to producing -readable, reusable, and refactorable software in JavaScript. +adattati a JavaScript. -Not every principle herein has to be strictly followed, and even less will be -universally agreed upon. These are guidelines and nothing more, but they are -ones codified over many years of collective experience by the authors of -*Clean Code*. +Non si tratta di una guida stilistica, bensì una guida per cercare di produrre software +[leggibile, riutilizzabile e rifattorizzabile](https://github.com/ryanmcdermott/3rs-of-software-architecture) in JavaScript. -Our craft of software engineering is just a bit over 50 years old, and we are -still learning a lot. When software architecture is as old as architecture -itself, maybe then we will have harder rules to follow. For now, let these -guidelines serve as a touchstone by which to assess the quality of the -JavaScript code that you and your team produce. +Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code*. -One more thing: knowing these won't immediately make you a better software -developer, and working with them for many years doesn't mean you won't make -mistakes. Every piece of code starts as a first draft, like wet clay getting -shaped into its final form. Finally, we chisel away the imperfections when -we review it with our peers. Don't beat yourself up for first drafts that need -improvement. Beat up the code instead! +Il nostro lavoro come software engeneer esiste da soli 50 anni e stiamo ancora cercando di apprendere molto. Quando l'architettura del software godrà della stessa anzianità dell'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. -## **Variables** -### Use meaningful and pronounceable variable names +Un ultima cosa: conoscere queste regole non farà di te immediatamente un developer migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. +Ogni singola parte di codice parte come bozza, inizialmente, per per poi prendere forma esattamente come una scultura di argilla. +Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice con i nostri colleghi. Ma non abbatterti tu la prima volta che il tuo codice sarà revisionato e richiederà miglioramenti: *Abbatti il codice!* -**Bad:** -```javascript -var yyyymmdstr = moment().format('YYYY/MM/DD'); -``` - -**Good**: -```javascript -var yearMonthDay = moment().format('YYYY/MM/DD'); -``` -**[⬆ back to top](#table-of-contents)** - -### Use ES6 constants when variable values do not change -In the bad example, the variable can be changed. -When you declare a constant, the variable should stay -the same throughout the program. +## **Variabili** +### Utilizza nomi di variabili comprensibili e pronunciabili - -**Bad:** +**Da evitare** ```javascript -var FIRST_US_PRESIDENT = "George Washington"; +const yyyymmdstr = moment().format('YYYY/MM/DD'); ``` -**Good**: +**Bene:** ```javascript -const FIRST_US_PRESIDENT = "George Washington"; +const currentDate = moment().format('YYYY/MM/DD'); ``` -**[⬆ back to top](#table-of-contents)** - +**[⬆ torna su](#lista-dei-contenuti)** -### Use the same vocabulary for the same type of variable +### Usa la stessa semantica per lo stesso tipo di variabili -**Bad:** +**Da evitare** ```javascript getUserInfo(); getClientData(); getCustomerRecord(); ``` -**Good**: +**Bene:** ```javascript getUser(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Use searchable names -We will read more code than we will ever write. It's important that the code we -do write is readable and searchable. By *not* naming variables that end up -being meaningful for understanding our program, we hurt our readers. -Make your names searchable. +### Utilizza nomi che possano essere cercati +Leggiamo molto più codice di quanto non ne scriveremo mai. È importante che il codice che noi scriviamo sia leggibile e ricercabile. Nominando variabili che non assumono uno specifico contesto all'interno del nostro software, possiamo irritare chi lo legge. +Fai in modo che i nomi delle tue variabili siano ricercabili. +Strumenti come [buddy.js](https://github.com/danielstjules/buddy.js) e +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare, per esempio, costanti non rinominate. -**Bad:** +**Da evitare** ```javascript -// What the heck is 525600 for? -for (var i = 0; i < 525600; i++) { - runCronJob(); -} +// Cosa caspita significa 86400000? +setTimeout(blastOff, 86400000); + ``` -**Good**: +**Bene:** ```javascript -// Declare them as capitalized `var` globals. -var MINUTES_IN_A_YEAR = 525600; -for (var i = 0; i < MINUTES_IN_A_YEAR; i++) { - runCronJob(); -} +// Dichiarala come costante in maiuscolo. +const MILLISECONDI_IN_UN_GIORNO = 86400000; + +setTimeout(blastOff, MILLISECONDI_IN_UN_GIORNO); + ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Use explanatory variables -**Bad:** +### Utilizza nomi di variabili esplicartivi +**Da evitare** ```javascript -const cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; -saveCityState(cityStateRegex.match(cityStateRegex)[1], cityStateRegex.match(cityStateRegex)[2]); +const address = 'One Infinite Loop, Cupertino 95014'; +const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; +saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]); ``` -**Good**: +**Bene:** ```javascript -const cityStateRegex = /^(.+)[,\\s]+(.+?)\s*(\d{5})?$/; -const match = cityStateRegex.match(cityStateRegex); -const city = match[1]; -const state = match[2]; -saveCityState(city, state); +const address = 'One Infinite Loop, Cupertino 95014'; +const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; +const [, city, zipCode] = address.match(cityZipCodeRegex) || []; +saveCityZipCode(city, zipCode); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid Mental Mapping -Explicit is better than implicit. +### Evita mappe mentali +Essere espliciti è meglio che non esserlo. -**Bad:** +**Da evitare** ```javascript -var locations = ['Austin', 'New York', 'San Francisco']; +const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { doStuff(); doSomeOtherStuff(); - ... - ... - ... - // Wait, what is `l` for again? + // ... + // ... + // ... + // A cosa fa riferimento esattamente `l`? dispatch(l); }); ``` -**Good**: +**Bene:** ```javascript -var locations = ['Austin', 'New York', 'San Francisco']; +const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { doStuff(); doSomeOtherStuff(); - ... - ... - ... + // ... + // ... + // ... dispatch(location); }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Non contestualizzare inutilmente -### Don't add unneeded context -If your class/object name tells you something, don't repeat that in your -variable name. +Se il nome della tua classe/oggetto ti indica a cosa fa riferimento, non ripeterlo nei nomi delle sue proprietà o funzioni. -**Bad:** +**Da evitare** ```javascript -var Car = { +const Car = { carMake: 'Honda', carModel: 'Accord', carColor: 'Blue' @@ -175,9 +149,9 @@ function paintCar(car) { } ``` -**Good**: +**Bene:** ```javascript -var Car = { +const Car = { make: 'Honda', model: 'Accord', color: 'Blue' @@ -187,83 +161,81 @@ function paintCar(car) { car.color = 'Red'; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Short-circuiting is cleaner than conditionals +### Utilizza i valori di default (predefiniti), anzichè usare condizioni o valutazioni minime -**Bad:** +I valori di default, generalmente sono più chiari dei [valutazioni minime](https://it.wikipedia.org/wiki/Valutazione_a_corto_circuito). Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. +Tutti gli altri valori "falsy" come `''`, `""`, `false`, `null`, `0`, e +`NaN`, non saranno sostituiti da un valore predefinito. + +**Da evitare** ```javascript function createMicrobrewery(name) { - var breweryName; - if (name) { - breweryName = name; - } else { - breweryName = 'Hipster Brew Co.'; - } + const breweryName = name || 'Hipster Brew Co.'; + // ... } + ``` -**Good**: +**Bene:** ```javascript -function createMicrobrewery(name) { - var breweryName = name || 'Hipster Brew Co.' +function createMicrobrewery(name = 'Hipster Brew Co.') { + // ... } + ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +## **Funzioni** +### Argomenti di una funzione (idealmente 2 o anche meno) + +Limitare il numero di argomenti di una funzione è incredibilmente importante perchè ti permette di testarla più facilmente. Avere più di 3 argomenti può portare ad un'esplosione di combinazioni da testare, che produrranno una lunga serie di casi da verificare. + +1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. Nei casi in cui questo non sia del tutto vero, un oggetto può aiutare ad ovviare a questo problema. + +Dal momento in cui JavaScript permette la creazione di oggetti al volo puoi usare un oggetto, se pensi che il tuo metodo richieda molti argomenti. -## **Functions** -### Function arguments (2 or less ideally) -Limiting the amount of function parameters is incredibly important because it -makes testing your function easier. Having more than three leads to a -combinatorial explosion where you have to test tons of different cases with -each separate argument. +Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi: -Zero arguments is the ideal case. One or two arguments is ok, and three should -be avoided. Anything more than that should be consolidated. Usually, if you have -more than two arguments then your function is trying to do too much. In cases -where it's not, most of the time a higher-level object will suffice as an -argument. +1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà saranno utilizzate -Since JavaScript allows us to make objects on the fly, without a lot of class -boilerplate, you can use an object if you are finding yourself needing a -lot of arguments. +2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti inattesi. **Nota**: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. -**Bad:** +3. Un Linter può avvisarti che non stai utilizzando alcune delle proprietà del tuo oggetto, diversamente non sarebbe possibile. + +**Da evitare** ```javascript function createMenu(title, body, buttonText, cancellable) { - ... + // ... } ``` -**Good**: +**Bene:** ```javascript -var menuConfig = { +function createMenu({ title, body, buttonText, cancellable }) { + // ... +} + +createMenu({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true -} - -function createMenu(menuConfig) { - ... -} - +}); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Functions should do one thing -This is by far the most important rule in software engineering. When functions -do more than one thing, they are harder to compose, test, and reason about. -When you can isolate a function to just one action, they can be refactored -easily and your code will read much cleaner. If you take nothing else away from -this guide other than this, you'll be ahead of many developers. +### Un metodo dovrebbe fare una sola cosa +Questa è di sicuro la regola più importante nell'ingegneria del software. Quando un metodo si occupa di più di un solo aspetto sarà più difficile da testare, comporre e ragioraci sopra. +Se è possibile far eseguire al metodo una sola azione sarà più facile il suo refactor e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. -**Bad:** +**Da evitare** ```javascript function emailClients(clients) { - clients.forEach(client => { - let clientRecord = database.lookup(client); + clients.forEach((client) => { + const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } @@ -271,73 +243,66 @@ function emailClients(clients) { } ``` -**Good**: +**Bene:** ```javascript -function emailClients(clients) { - clients.forEach(client => { - emailClientIfNeeded(client); - }); +function emailActiveClients(clients) { + clients + .filter(isActiveClient) + .forEach(email); } -function emailClientIfNeeded(client) { - if (isClientActive(client)) { - email(client); - } -} - -function isClientActive(client) { - let clientRecord = database.lookup(client); +function isActiveClient(client) { + const clientRecord = database.lookup(client); return clientRecord.isActive(); } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Function names should say what they do +### I nomi delle funzioni dovrebbero farti capire cosa fanno -**Bad:** +**Da evitare** ```javascript -function dateAdd(date, month) { +function addToDate(date, month) { // ... } -let date = new Date(); +const date = new Date(); -// It's hard to to tell from the function name what is added -dateAdd(date, 1); +// Difficile da dire esattamente cosa viene aggiunto tramite questa funzione +addToDate(date, 1); ``` -**Good**: +**Bene:** ```javascript -function dateAddMonth(date, month) { +function addMonthToDate(month, date) { // ... } -let date = new Date(); -dateAddMonth(date, 1); +const date = new Date(); +addMonthToDate(1, date); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Le funzioni dovrebbero avere un solo livello di astrazione -### Functions should only be one level of abstraction -When you have more than one level of abstraction your function is usually -doing too much. Splitting up functions leads to reusability and easier -testing. +Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarle e testare più facilmente. -**Bad:** +**Da evitare** ```javascript function parseBetterJSAlternative(code) { - let REGEXES = [ + const REGEXES = [ // ... ]; - let statements = code.split(' '); - let tokens; + const statements = code.split(' '); + const tokens = []; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { // ... }); }); - let ast; + const ast = []; tokens.forEach((token) => { // lex... }); @@ -348,66 +313,65 @@ function parseBetterJSAlternative(code) { } ``` -**Good**: +**Bene:** ```javascript +function parseBetterJSAlternative(code) { + const tokens = tokenize(code); + const ast = lexer(tokens); + ast.forEach((node) => { + // parse... + }); +} + function tokenize(code) { - let REGEXES = [ + const REGEXES = [ // ... ]; - let statements = code.split(' '); - let tokens; + const statements = code.split(' '); + const tokens = []; REGEXES.forEach((REGEX) => { statements.forEach((statement) => { -<<<<<<< HEAD - // ... - }) -======= - tokens.push( // ... ); + tokens.push( /* ... */ ); }); ->>>>>>> fadfb57... Fill missing commas }); return tokens; } function lexer(tokens) { - let ast; + const ast = []; tokens.forEach((token) => { - // lex... + ast.push( /* ... */ ); }); return ast; } - -function parseBetterJSAlternative(code) { - let tokens = tokenize(code); - let ast = lexer(tokens); - ast.forEach((node) => { - // parse... - }); -} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Rimuovi il codice duplicato +Fai del tuo meglio per evitare codice duplicato. Duplicare il codice è un male, perchè vuol dire che c'è più di un punto da modificare nel caso in cui dovessi cambiare alcune logiche. + +Immagina di avere un ristorante e di dover tener traccia del tuo magazzino: la riserva di pomodori, cipolle, aglio, spezie, etc. Se hai più di una lista in cui tieni traccia di queste quantità dovrai aggiornarle tutte ogni volta che servirai, per esempio, un piatto con dei pomodori. Al contrario, se dovessi avere una sola lista, avrai un solo posto un cui dovrai tenere traccia delle modifiche sulle quantità in magazzino. -### Remove duplicate code -Never ever, ever, under any circumstance, have duplicate code. There's no reason -for it and it's quite possibly the worst sin you can commit as a professional -developer. Duplicate code means there's more than one place to alter something -if you need to change some logic. JavaScript is untyped, so it makes having -generic functions quite easy. Take advantage of that! +Generalmente si duplica il codice perchè ci sono due o tre piccole differenze tra una parte e l'altra del software. Questo permette di condividere le parti comuni del codice, ma allo stesso tempo avrai dei duplicati di parti che fanno la stessa cosa. +Rimuovere questi duplicati, significa creare un'astrazione che permette di gestire queste differenze attraverso un unico metodo/modulo/classe. -**Bad:** +Ottenere la sufficiente astrazione può essere complicato. Per questo dovresti seguire i principi SOLID, approfonditi nella sezione *Classi*. +Un'astrazione non ottimale potrebbe anche essere peggio del codice duplicato, per cui fai attenzione! Non ripeterti, altrimenti dovrai aggiornare tutte le occorrenze della stessa logica ogni volta che vorrai cambiare qualcosa. + +**Da evitare** ```javascript function showDeveloperList(developers) { - developers.forEach(developers => { - var expectedSalary = developer.calculateExpectedSalary(); - var experience = developer.getExperience(); - var githubLink = developer.getGithubLink(); - var data = { - expectedSalary: expectedSalary, - experience: experience, - githubLink: githubLink + developers.forEach((developer) => { + const expectedSalary = developer.calculateExpectedSalary(); + const experience = developer.getExperience(); + const githubLink = developer.getGithubLink(); + const data = { + expectedSalary, + experience, + githubLink }; render(data); @@ -415,14 +379,14 @@ function showDeveloperList(developers) { } function showManagerList(managers) { - managers.forEach(manager => { - var expectedSalary = manager.calculateExpectedSalary(); - var experience = manager.getExperience(); - var portfolio = manager.getMBAProjects(); - var data = { - expectedSalary: expectedSalary, - experience: experience, - portfolio: portfolio + managers.forEach((manager) => { + const expectedSalary = manager.calculateExpectedSalary(); + const experience = manager.getExperience(); + const portfolio = manager.getMBAProjects(); + const data = { + expectedSalary, + experience, + portfolio }; render(data); @@ -430,81 +394,62 @@ function showManagerList(managers) { } ``` -**Good**: +**Bene:** ```javascript -function showList(employees) { - employees.forEach(employee => { - var expectedSalary = employee.calculateExpectedSalary(); - var experience = employee.getExperience(); - var portfolio; - - if (employee.type === 'manager') { - portfolio = employee.getMBAProjects(); - } else { - portfolio = employee.getGithubLink(); - } +function showEmployeeList(employees) { + employees.forEach((employee) => { + const expectedSalary = employee.calculateExpectedSalary(); + const experience = employee.getExperience(); - var data = { - expectedSalary: expectedSalary, - experience: experience, - portfolio: portfolio + const data = { + expectedSalary, + experience }; + switch (employee.type) { + case 'manager': + data.portfolio = employee.getMBAProjects(); + break; + case 'developer': + data.githubLink = employee.getGithubLink(); + break; + } + render(data); }); } ``` -**[⬆ back to top](#table-of-contents)** - -### Use default arguments instead of short circuiting -**Bad:** -```javascript -function writeForumComment(subject, body) { - subject = subject || 'No Subject'; - body = body || 'No text'; -} - -``` - -**Good**: -```javascript -function writeForumComment(subject = 'No subject', body = 'No text') { - ... -} - -``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Set default objects with Object.assign +### Estendi un oggetto con Object.assign -**Bad:** +**Da evitare** ```javascript -var menuConfig = { +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; - + 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); ``` -**Good**: +**Bene:** ```javascript -var menuConfig = { +const menuConfig = { title: 'Order', // User did not include 'body' key buttonText: 'Send', cancellable: true -} +}; function createMenu(config) { config = Object.assign({ @@ -514,62 +459,59 @@ function createMenu(config) { cancellable: true }, config); - // config now equals: {title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true} + // config adesso sarà: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't use flags as function parameters -Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. +### Non usare valori flag (true/false) come parametri di una funzione -**Bad:** +Un valore di tipo flag indica che la tua funzione può eseguire più di una sola operazione. Una funzione dovrebbe eseguire una sola operazione. Separa la tua funzione se deve eseguire più di una operazione in base al parametro flag che riceve in input. + +**Da evitare** ```javascript function createFile(name, temp) { if (temp) { - fs.create('./temp/' + name); + fs.create(`./temp/${name}`); } else { fs.create(name); } } ``` -**Good**: +**Bene:** ```javascript -function createTempFile(name) { - fs.create('./temp/' + name); -} - function createFile(name) { fs.create(name); } + +function createTempFile(name) { + createFile(`./temp/${name}`); +} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Evitare effetti inattesi (parte 1) -### Avoid Side Effects -A function produces a side effect if it does anything other than take a value in -and return another value or values. A side effect could be writing to a file, -modifying some global variable, or accidentally wiring all your money to a -stranger. +Una funzione può generare un effetto collaterale se fa altro oltre a ricevere un valore e restituirne uno o più. L'effetto collaterale potrebbe essere scrivere su un file, modificare una variabile globale o accidentalmente girare tutti i tuoi soldi ad uno sconosciuto. -Now, you do need to have side effects in a program on occasion. Like the previous -example, you might need to write to a file. What you want to do is to -centralize where you are doing this. Don't have several functions and classes -that write to a particular file. Have one service that does it. One and only one. +Probabilmente, e occasionalmente, avrai bisogno di generare un effetto collaterale: come nell'esempio precedente magari proprio scrivere su un file. +Quello che dovrai fare è centralizzare il punto in cui lo fai. Non avere più funzioni e classi che fanno la stessa cosa, ma averne un servizio ed uno soltanto che se ne occupa. -The main point is to avoid common pitfalls like sharing state between objects -without any structure, using mutable data types that can be written to by anything, -and not centralizing where your side effects occur. If you can do this, you will -be happier than the vast majority of other programmers. +La questione più importante è evitare le insidie che stanno dietro ad errate manipolazioni di oggetti senza alcuna struttura, utilizzando strutture che possono essere modificate da qualunque parte. +Se riuscirai ad evitare che questo accada...sarai ben più felice della maggior parte degli altri programmatori. -**Bad:** +**Da evitare** ```javascript -// 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. -var name = 'Ryan McDermott'; + +// Variable globale utilizzata dalla funzione seguente. +// Nel caso in cui dovessimo utilizzarla in un'altra funzione a questo punto si tratterebbe di un array e potrebbe generare un errore. + +let name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name = name.split(' '); @@ -580,85 +522,85 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**Good**: +**Bene:** ```javascript function splitIntoFirstAndLastName(name) { return name.split(' '); } -var name = 'Ryan McDermott' -var newName = splitIntoFirstAndLastName(name); +const name = 'Ryan McDermott'; +const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't write to global functions -Polluting globals is a bad practice in JavaScript because you could clash with another -library and the user of your API would be none-the-wiser until they get an -exception in production. Let's think about an example: what if you wanted to -extend JavaScript's native Array method to have a `diff` method that could -show the difference between two arrays? You could write your new function -to the `Array.prototype`, but it could clash with another library that tried -to do the same thing. What if that other library was just using `diff` to find -the difference between the first and last elements of an array? This is why it -would be much better to just use ES6 classes and simply extend the `Array` global. +### Evitare effetti inattesi (parte 2) +In Javascript i valori primitivi sono passati come valori, mentre oggetti ed array vengono passati come reference. Nel caso di oggetti ed array, se la tua funzione modifica l'array contenente un carrello della spesa, per esempio, aggiungendo o rimuovendo un oggetto da acquistare, tutte le altre funzioni che utilizzeranno l'array `carrello` saranno condizionati da questa modifica. Questo potrebbe essere un bene ed un male allo stesso tempo. Immaginiamo una situazione in cui questo è un male: -**Bad:** -```javascript -Array.prototype.diff = function(comparisonArray) { - var values = []; - var hash = {}; +l'utente clicca sul tasto "Acquista", che richiamerà una funzione `acquista` che effettua una richiesta ed invia l'array `carrello` al server. Per via di una pessima connessione, il metodo `acquista` riproverà ad effettuare la richiesta al server. Cosa succede se nello stesso momento accidentalmemte l'utente clicca su "Aggiungi al carrello" su di un oggetto che non ha intenzione di acquistare prima che venga eseguita nuovamente la funzione? +Verrà inviata la richiesta con il nuovo oggetto accidentalmente aggiunto al carrello utilizzando la funzione `aggiungiOggettoAlCarrello`. - for (var i of comparisonArray) { - hash[i] = true; - } +Un'ottima soluzione è quella di di clonare sempre l'array `carrello`, modificarlo e restituire il clone. +Questo ci assicurerà che che nessun'altra funzione che gestisce il carrello subirà cambiamenti non voluti. - for (var i of this) { - if (!hash[i]) { - values.push(i); - } - } +Due precisazioni vanno fatte su questo approccio: - return values; -} +1. Potrebbe essere che tu voglia realmente modificare l'oggetto in input, ma vedrai che utilizzando questo approccio ti accorgerai che questi casi sono veramente rari. La maggior parte delle volte dovrai utilizzare questo approccio per non generare effetti inattesi + +2. Clonare oggetti molto grandi potrebbe essere davvero dispendioso in termini di risorse. Fortunatamente questo non è un problema perchè esistono [ottime librerie](https://facebook.github.io/immutable-js/) che permettono di utilizzare questo approccio senza dispendio di memoria e più velocemente rispetto al dovelo fare manualmente. + +**Da evitare** +```javascript +const addItemToCart = (cart, item) => { + cart.push({ item, date: Date.now() }); +}; ``` -**Good:** +**Bene:** ```javascript -class SuperArray extends Array { - constructor(...args) { - super(...args); - } +const addItemToCart = (cart, item) => { + return [...cart, { item, date: Date.now() }]; +}; +``` - diff(comparisonArray) { - var values = []; - var hash = {}; +**[⬆ torna su](#lista-dei-contenuti)** - for (var i of comparisonArray) { - hash[i] = true; - } +### Non aggiungere funzioni globali +Contaminare o aggiungere delle variabili globali è una pratica sconsigliata, in quanto potresti entrare in conflitto con altre librerie e chi utiliza le tue API potrebbe non accorgersene fintanto che non si trova in produzione, generando un'eccezione. +Facciamo un esempio pratico: supponiamo che tu voglia estendere il costrutto Array nativo di JavaScript aggiungendo il metodo `diff` che mostra le differenze tra due array. Come puoi fare? +Potresti scrivere il metodo utilizzando `Array.prototype`, che però potrebbe entrare in conflitto con con un'altra libreria che fa la stessa cosa. Cosa succederebbe se anche l'altra libreria utilizzasse `diff` per trovare le differenze tra due array? Ecco perchè è molto meglio utilizzare le classi ES2015/ES6 e semplicemente estendere `Array`. - for (var i of this) { - if (!hash[i]) { - values.push(i); - } - } +**Da evitare** +```javascript +Array.prototype.diff = function diff(comparisonArray) { + const hash = new Set(comparisonArray); + return this.filter(elem => !hash.has(elem)); +}; +``` - return values; +**Bene:** +```javascript +class SuperArray extends Array { + diff(comparisonArray) { + const hash = new Set(comparisonArray); + return this.filter(elem => !hash.has(elem)); } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Preferisci la programmazione funzionale a quella imperativa -### Favor functional programming over imperative programming -If Haskell were an IPA then JavaScript would be an O'Douls. That is to say, -JavaScript isn't a functional language in the way that Haskell is, but it has -a functional flavor to it. Functional languages are cleaner and easier to test. -Favor this style of programming when you can. +*[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale)* - +*[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa)* -**Bad:** +Javascript non è un linguaggio funzionale alla stregua di [Haskell](https://www.haskell.org/), ma entrambi hanno qualcosa che li accomuna. +I linguaggi funzionali generalmente sono più puliti e facili da testare. +Preferisci questo stile se possibile. + +**Da evitare** ```javascript const programmerOutput = [ { @@ -676,14 +618,14 @@ const programmerOutput = [ } ]; -var totalOutput = 0; +let totalOutput = 0; -for (var i = 0; i < programmerOutput.length; i++) { +for (let i = 0; i < programmerOutput.length; i++) { totalOutput += programmerOutput[i].linesOfCode; } ``` -**Good**: +**Bene:** ```javascript const programmerOutput = [ { @@ -701,22 +643,22 @@ const programmerOutput = [ } ]; -var totalOutput = programmerOutput - .map((programmer) => programmer.linesOfCode) - .reduce((acc, linesOfCode) => acc + linesOfCode, 0); +const totalOutput = programmerOutput + .map(output => output.linesOfCode) + .reduce((totalLines, lines) => totalLines + lines); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Encapsulate conditionals +### Incapsula le condizioni -**Bad:** +**Da evitare** ```javascript if (fsm.state === 'fetching' && isEmpty(listNode)) { - /// ... + // ... } ``` -**Good**: +**Bene:** ```javascript function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); @@ -726,11 +668,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid negative conditionals +### Evita di verificare condizioni in negativo -**Bad:** +**Da evitare** ```javascript function isDOMNodeNotPresent(node) { // ... @@ -741,7 +683,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**Good**: +**Bene:** ```javascript function isDOMNodePresent(node) { // ... @@ -751,151 +693,140 @@ if (isDOMNodePresent(node)) { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid conditionals -This seems like an impossible task. Upon first hearing this, most people say, -"how am I supposed to do anything without an `if` statement?" The answer is that -you can use polymorphism to achieve the same task in many cases. The second -question is usually, "well that's great but why would I want to do that?" The -answer is a previous clean code concept we learned: a function should only do -one thing. When you have classes and functions that have `if` statements, you -are telling your user that your function does more than one thing. Remember, -just do one thing. +### Evita le condizioni +Sembrerebbe un task impossibile. Ad un rapido sguardo molti sviluppatori potrebbero pensare "come posso pensare di far funzionare qualcosa senza utilizzare un `if`?" +La risposta è che puoi utilizzare il polimorfismo per ottenere lo stesso risultato in molti casi. +La seconda domanda generalmente è "Ottimo! Ma perchè dovrei farlo?". +La risposta è data in uno dei concetti precedentemente descritti: una funzione dovrebbe eseguire una sola operazione. +Quando hai una Classe con delle funzioni che utilizzano lo stato `if` stai dicendo all'utente che la tua funzione può fare più di una operazione. -**Bad:** + +**Da evitare** ```javascript class Airplane { - //... + // ... getCruisingAltitude() { switch (this.type) { case '777': - return getMaxAltitude() - getPassengerCount(); + return this.getMaxAltitude() - this.getPassengerCount(); case 'Air Force One': - return getMaxAltitude(); + return this.getMaxAltitude(); case 'Cessna': - return getMaxAltitude() - getFuelExpenditure(); + return this.getMaxAltitude() - this.getFuelExpenditure(); } } } ``` -**Good**: +**Bene:** ```javascript class Airplane { - //... + // ... } class Boeing777 extends Airplane { - //... + // ... getCruisingAltitude() { - return getMaxAltitude() - getPassengerCount(); + return this.getMaxAltitude() - this.getPassengerCount(); } } class AirForceOne extends Airplane { - //... + // ... getCruisingAltitude() { - return getMaxAltitude(); + return this.getMaxAltitude(); } } class Cessna extends Airplane { - //... + // ... getCruisingAltitude() { - return getMaxAltitude() - getFuelExpenditure(); + return this.getMaxAltitude() - this.getFuelExpenditure(); } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid type-checking (part 1) -JavaScript is untyped, which means your functions can take any type of argument. -Sometimes you are bitten by this freedom and it becomes tempting to do -type-checking in your functions. There are many ways to avoid having to do this. -The first thing to consider is consistent APIs. +### Evita la validazione dei tipi (parte 1) +JavaScript è un linguaggio non tipizzato, il che significa che le tue funzioni possono accettare qualunque tipo di argomento. +Qualche volta potresti essere tentato da tutta questa libertà e potresti essere altrettanto tentato di veririfcare il tipo di dato ricevuto nella tua funzione. +Ci sono molti modi per evitare di dover fare questo tipo di controllo. +Come prima cosa cerca scrivere API consistenti. -**Bad:** +**Da evitare** ```javascript function travelToTexas(vehicle) { if (vehicle instanceof Bicycle) { - vehicle.peddle(this.currentLocation, new Location('texas')); + vehicle.pedal(this.currentLocation, new Location('texas')); } else if (vehicle instanceof Car) { vehicle.drive(this.currentLocation, new Location('texas')); } } ``` -**Good**: +**Bene:** ```javascript function travelToTexas(vehicle) { vehicle.move(this.currentLocation, new Location('texas')); } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid type-checking (part 2) -If you are working with basic primitive values like strings, integers, and arrays, -and you can't use polymorphism but you still feel the need to type-check, -you should consider using TypeScript. It is an excellent alternative to normal -JavaScript, as it provides you with static typing on top of standard JavaScript -syntax. The problem with manually type-checking normal JavaScript is that -doing it well requires so much extra verbiage that the faux "type-safety" you get -doesn't make up for the lost readability. Keep your JavaScript clean, write -good tests, and have good code reviews. Otherwise, do all of that but with -TypeScript (which, like I said, is a great alternative!). +### Evita la validazione dei tipi (part 2) +Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di [TypeScript](https://www.typescriptlang.org/). +È una vlidissima alternativa al normale JavaScript che fornicsce una validazione di tipi statica utilizzando la sintassi JavaScript. +Il problema con la validazione manuale dei tipi utilizzando lo standard JavaScript è che richiede tanto codice extra che non compensa la scarsa leggibilità del codice ottenuto. +Mantieni pulito il tuo codice, scrivi dei test validi e cerca di fare delle buone revisioni del coidice. Altrimenti utilizza TypeScript (che come detto in precedenza è una validissima alternativa!) -**Bad:** +**Da evitare** ```javascript function combine(val1, val2) { - if (typeof val1 == "number" && typeof val2 == "number" || - typeof val1 == "string" && typeof val2 == "string") { + if (typeof val1 === 'number' && typeof val2 === 'number' || + typeof val1 === 'string' && typeof val2 === 'string') { return val1 + val2; - } else { - throw new Error('Must be of type String or Number'); } + + throw new Error('Must be of type String or Number'); } ``` -**Good**: +**Bene:** ```javascript function combine(val1, val2) { return val1 + val2; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't over-optimize -Modern browsers do a lot of optimization under-the-hood at runtime. A lot of -times, if you are optimizing then you are just wasting your time. [There are good -resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) -for seeing where optimization is lacking. Target those in the meantime, until -they are fixed if they can be. +### Non ottimizzare eccessivamente +I browser moderni eseguono un sacco di ottimizzazione "sottobanco". Molte volte, quando cerchi di ottimizzare il tuo codice, stai perdendo tempo prezioso. [Ci sono tante guide](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) che aiutano a capire quali tipi di ottimizzazione sono superflui e quali no. Utilizza queste guide nel frattempo, fintanto che queste lacune non verranno colmate. -**Bad:** +**Da evitare** ```javascript -// On old browsers, each iteration with uncached `list.length` would be costly -// because of `list.length` recomputation. In modern browsers, this is optimized. -for (var i = 0, len = list.length; i < len; i++) { +// Nei vecchi browser ogni volta che in un ciclo verifichi `list.length` potrebbe +// essere dispendioso per via del suo ricalcolo. Nei browser moderni +// questa operazione è stata ottimizzata +for (let i = 0, len = list.length; i < len; i++) { // ... } ``` -**Good**: +**Bene:** ```javascript -for (var i = 0; i < list.length; i++) { +for (let i = 0; i < list.length; i++) { // ... } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Remove dead code -Dead code is just as bad as duplicate code. There's no reason to keep it in -your codebase. If it's not being called, get rid of it! It will still be safe -in your version history if you still need it. +### Rimuovi il codice inutilizzato +Il codice inutilizzato è dannoso quanto il codice duplicato. Non c'è motivo per cui tenerlo nel tuo codebase. Se realmente non viene utilizzato rimuovilo! +Sarà comunque presente nella storia del tuo file se utilizzi un sistema di versioning nel caso in cui dovesse servirti. -**Bad:** +**Da evitare** ```javascript function oldRequestModule(url) { // ... @@ -905,310 +836,485 @@ function newRequestModule(url) { // ... } -var req = newRequestModule; +const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**Good**: +**Bene:** ```javascript function newRequestModule(url) { // ... } -var req = newRequestModule; +const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Objects and Data Structures** -### Use getters and setters -JavaScript doesn't have interfaces or types so it is very hard to enforce this -pattern, because we don't have keywords like `public` and `private`. As it is, -using getters and setters to access data on objects is far better than simply -looking for a property on an object. "Why?" you might ask. Well, here's an -unorganized list of reasons why: +## **Ogetti e strutture dati** +### Utilizza getter e setter +Utilizzare getter e setter per acceedere ai dati di un oggetto può essere meglio che accedere direttamente alle sue proprietà. Ti starai sicuramente chiedendo il motivo. +Eccoti qualche motivo per cui utilizzare getter e setter: +* Quando hai bisogno di eseguire un'operazione sul dato recuperato, non devi andare a modificarlo ogni volta che accedi al dato nel tuo codice. +* Valida il dato nel momento in cui lo setti con `set` +* Incapsula la rappresentazione interna +* È più facile loggare e gestire gli errori quando imposti o recuperi un dato +* Puoi caricare i dati del tuo oggetto in modalità *lazy*, per esempio caricandoli dal server. -1. When you want to do more beyond getting an object property, you don't have -to look up and change every accessor in your codebase. -2. Makes adding validation simple when doing a `set`. -3. Encapsulates the internal representation. -4. Easy to add logging and error handling when getting and setting. -5. Inheriting this class, you can override default functionality. -6. You can lazy load your object's properties, let's say getting it from a -server. - -**Bad:** +**Da evitare** ```javascript -class BankAccount { - constructor() { - this.balance = 1000; - } -} +function makeBankAccount() { + // ... -let bankAccount = new BankAccount(); + return { + balance: 0, + // ... + }; +} -// Buy shoes... -bankAccount.balance = bankAccount.balance - 100; +const account = makeBankAccount(); +account.balance = 100; ``` -**Good**: +**Bene:** ```javascript -class BankAccount { - constructor() { - this.balance = 1000; +function makeBankAccount() { + // questa proprietà è privata + let balance = 0; + + // un "getter", la rende pubblica restituendola in questo modo + function getBalance() { + return balance; } - // It doesn't have to be prefixed with `get` or `set` to be a getter/setter - withdraw(amount) { - if (verifyAmountCanBeDeducted(amount)) { - this.balance -= amount; - } + // un "setter", la rende pubblica in questo modo + function setBalance(amount) { + // ... e la valida prima di impostarla + balance = amount; } -} -let bankAccount = new BankAccount(); + return { + // ... + getBalance, + setBalance, + }; +} -// Buy shoes... -bankAccount.withdraw(100); +const account = makeBankAccount(); +account.setBalance(100); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Make objects have private members -This can be accomplished through closures (for ES5 and below). +### Imposta proprietà private in un oggetto +Può essere fatto attraverso le *closure* (per ES5 e versioni precedenti). -**Bad:** +**Da evitare** ```javascript -var Employee = function(name) { +const Employee = function(name) { this.name = name; -} +}; -Employee.prototype.getName = function() { +Employee.prototype.getName = function getName() { return this.name; -} +}; -var employee = new Employee('John Doe'); -console.log('Employee name: ' + employee.getName()); // Employee name: John Doe +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 +console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**Good**: +**Bene:** ```javascript -var Employee = (function() { - function Employee(name) { - this.getName = function() { +function makeEmployee(name) { + return { + getName() { return name; - }; - } - - return Employee; -}()); + }, + }; +} -var employee = new Employee('John Doe'); -console.log('Employee name: ' + employee.getName()); // Employee name: John Doe +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 +console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Classes** -### Single Responsibility Principle (SRP) -As stated in Clean Code, "There should never be more than one reason for a class -to change". It's tempting to jam-pack a class with a lot of functionality, like -when you can only take one suitcase on your flight. The issue with this is -that your class won't be conceptually cohesive and it will give it many reasons -to change. Minimizing the amount of times you need to change a class is important. -It's important because if too much functionality is in one class and you modify a piece of it, -it can be difficult to understand how that will affect other dependent modules in -your codebase. +## **Classi** +### Utilizza le classi ES2015/ES6 piuttosto che le funzioni di ES5 +È molto difficile ottenere leggibilità sull'ereditarietà, costrutti e metodi in definizioni di oggetti in ES5. Se hai bisogno di ereditarietà (e bada bene, non è detto che tu ne abbia bisogno), utilizza il costrutto Class di ES2015/ES6. Altrimenti utilizza piccole funzioni fintanto che non avrai bisogno di gestire oggetti più complessi. -**Bad:** +**Da evitare** ```javascript -class UserSettings { - constructor(user) { - this.user = user; +const Animal = function(age) { + if (!(this instanceof Animal)) { + throw new Error('Instantiate Animal with `new`'); } - changeSettings(settings) { - if (this.verifyCredentials(user)) { - // ... - } - } + this.age = age; +}; - verifyCredentials(user) { - // ... - } -} -``` +Animal.prototype.move = function move() {}; -**Good**: -```javascript -class UserAuth { - constructor(user) { - this.user = user; +const Mammal = function(age, furColor) { + if (!(this instanceof Mammal)) { + throw new Error('Instantiate Mammal with `new`'); } - verifyCredentials() { - // ... - } -} + Animal.call(this, age); + this.furColor = furColor; +}; +Mammal.prototype = Object.create(Animal.prototype); +Mammal.prototype.constructor = Mammal; +Mammal.prototype.liveBirth = function liveBirth() {}; -class UserSettings { - constructor(user) { - this.user = user; - this.auth = new UserAuth(user); +const Human = function(age, furColor, languageSpoken) { + if (!(this instanceof Human)) { + throw new Error('Instantiate Human with `new`'); } - changeSettings(settings) { - if (this.auth.verifyCredentials()) { - // ... - } - } -} -``` -**[⬆ back to top](#table-of-contents)** + Mammal.call(this, age, furColor); + this.languageSpoken = languageSpoken; +}; -### Open/Closed Principle (OCP) -As stated by Bertrand Meyer, "software entities (classes, modules, functions, -etc.) should be open for extension, but closed for modification." What does that -mean though? This principle basically states that you should allow users to -extend the functionality of your module without having to open up the `.js` -source code file and manually manipulate it. +Human.prototype = Object.create(Mammal.prototype); +Human.prototype.constructor = Human; +Human.prototype.speak = function speak() {}; +``` -**Bad:** +**Bene:** ```javascript -class AjaxRequester { - constructor() { - // What if we wanted another HTTP Method, like DELETE? We would have to - // open this file up and modify this and put it in manually. - this.HTTP_METHODS = ['POST', 'PUT', 'GET']; - } - - get(url) { - // ... +class Animal { + constructor(age) { + this.age = age; } + move() { /* ... */ } } -``` -**Good**: -```javascript -class AjaxRequester { - constructor() { - this.HTTP_METHODS = ['POST', 'PUT', 'GET']; +class Mammal extends Animal { + constructor(age, furColor) { + super(age); + this.furColor = furColor; } - get(url) { - // ... - } + liveBirth() { /* ... */ } +} - addHTTPMethod(method) { - this.HTTP_METHODS.push(method); +class Human extends Mammal { + constructor(age, furColor, languageSpoken) { + super(age, furColor); + this.languageSpoken = languageSpoken; } + + speak() { /* ... */ } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Liskov Substitution Principle (LSP) -This is a scary term for a very simple concept. It's formally defined as "If S -is a subtype of T, then objects of type T may be replaced with objects of type S -(i.e., objects of type S may substitute objects of type T) without altering any -of the desirable properties of that program (correctness, task performed, -etc.)." That's an even scarier definition. +### Concatena i metodi +Questo pattern è molto utilizzato in JavaScript e puoi trovarne applicazione in molte liberie come [jQuery](https://jquery.com/) e [Lodash](https://lodash.com/). Permette al tuo codice di essere maggiormente espressivo e meno verboso. +Proprio per questo motivo, insisto, utilizza la concatenazione dei metodi e guarda come può essere più pulito il tuo codice. Nei metodi della tua classe, semplicemente restituisci il riferimento `this` alla fine di ogni metodo, in modo da poter concatenare altri metodi. -The best explanation for this is if you have a parent class and a child class, -then the base class and child class can be used interchangeably without getting -incorrect results. This might still be confusing, so let's take a look at the -classic Square-Rectangle example. Mathematically, a square is a rectangle, but -if you model it using the "is-a" relationship via inheritance, you quickly -get into trouble. -**Bad:** +**Da evitare** ```javascript -class Rectangle { - constructor() { - this.width = 0; - this.height = 0; +class Car { + constructor(make, model, color) { + this.make = make; + this.model = model; + this.color = color; } - setColor(color) { - // ... + setMake(make) { + this.make = make; } - render(area) { - // ... + setModel(model) { + this.model = model; } - setWidth(width) { - this.width = width; + setColor(color) { + this.color = color; } - setHeight(height) { - this.height = height; + save() { + console.log(this.make, this.model, this.color); } +} - getArea() { - return this.width * this.height; +const car = new Car('Ford','F-150','red'); +car.setColor('pink'); +car.save(); +``` + +**Bene:** +```javascript +class Car { + constructor(make, model, color) { + this.make = make; + this.model = model; + this.color = color; } -} -class Square extends Rectangle { - constructor() { - super(); + setMake(make) { + this.make = make; + // NOTA: restituisci this per poter concatenare altri metodi. + return this; } - setWidth(width) { - this.width = width; - this.height = width; + setModel(model) { + this.model = model; + // NOTA: restituisci this per poter concatenare altri metodi. + return this; } - setHeight(height) { - this.width = height; - this.height = height; + setColor(color) { + this.color = color; + // NOTA: restituisci this per poter concatenare altri metodi. + return this; } -} -function renderLargeRectangles(rectangles) { - rectangles.forEach((rectangle) => { - rectangle.setWidth(4); - rectangle.setHeight(5); - let area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20. - rectangle.render(area); - }); + save() { + console.log(this.make, this.model, this.color); + // NOTA: restituisci this per poter concatenare altri metodi. + return this; + } } -let rectangles = [new Rectangle(), new Rectangle(), new Square()]; -renderLargeRectangles(rectangles); +const car = new Car('Ford','F-150','red') + .setColor('pink') + .save(); ``` +**[⬆ torna su](#lista-dei-contenuti)** + +### Preferisci una struttura compositiva all'ereditarietà +Come dichiarato dalla Gang of four in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) dovresti preferire la strutturazione all'ereditarietà quando puoi. Ci sono validi motivi per utilizzare l'ereditarietà e altrettanto validi motivi per utilizzare la strutturazione. +Il punto principale di questo assunto è che mentalmente sei portato a preferire l'ereditarietà. Prova a pensare alla strutturazione per risolvere il tuo problema: tante volte è davvero la soluzione migliore. +Ti potresti chiedere: "Quando dovrei utilizzare l'ereditarietà?". Dipende dal problema che devi affrontare, ma c'è una discreta lista di suggerimenti che ti potrebbero aiutare a capire quando l'ereditarietà è meglio della strutturazione: -**Good**: +1. L'estensione che stai mettendo in atto rappresenta una relazione di tipo "è-un" e non "ha-un" (Umano->Animale vs. Utente->DettagliUtente). +2. Puoi riutilizzare il codice dalla classe padre +3. Vuoi fare cambiamenti globali a tutte le classi estese tramite la classe di partenza. + +**Da evitare** ```javascript -class Shape { - constructor() {} +class Employee { + constructor(name, email) { + this.name = name; + this.email = email; + } - setColor(color) { + // ... +} + +// Male 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; + } + + // ... +} +``` + +**Bene:** +```javascript +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); + } + // ... +} +``` +**[⬆ torna su](#lista-dei-contenuti)** + +## **SOLID** +### Single Responsibility Principle (SRP) (Principio di singola responsabilità) +Come indicato in *Clean code*, "Non dovrebbe mai esserci più di un solo motivo per modificare una classe". La tentazione è sempre quella di fare un'unica classe con molte funzionalità, come quando vuoi portarti un unico bagaglio a bordo. +Il problema con questo approccio è che le tue classi non saranno concettualmente coese e ti potrebbero dare più di una ragione per modificarle in seguito. +Minimizzare il numero di volte in cui modificare una classe è importante. +È importante perchè quando ci sono troppe funzionalità in una classe è difficile capire che effetto avrà sulle classe che la estendono, nel caso in cui farai un cambiamento. + +**Da evitare** +```javascript +class UserSettings { + constructor(user) { + this.user = user; + } + + changeSettings(settings) { + if (this.verifyCredentials()) { + // ... + } + } + + verifyCredentials() { // ... } +} +``` - render(area) { +**Bene:** +```javascript +class UserAuth { + constructor(user) { + this.user = user; + } + + verifyCredentials() { // ... } } -class Rectangle extends Shape { + +class UserSettings { + constructor(user) { + this.user = user; + this.auth = new UserAuth(user); + } + + changeSettings(settings) { + if (this.auth.verifyCredentials()) { + // ... + } + } +} +``` +**[⬆ torna su](#lista-dei-contenuti)** + +### Open/Closed Principle (OCP) (Principio aperto/chiuso) +Come dichiarato da Bertrand Meyer, "Le entità di un software (Classi, moduli, funzioni, etc.) dovrebbero essere aperte all'estensione, ma chiuse alla modifica. Cosa significa esattamente? +Quello che intende è che dovresti dare ai tuoi utilizzatori la possibilità di aggiungere nuove funzionalità, non modificando quelle esistenti. + +**Da evitare** +```javascript +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 +} +``` + +**Bene:** +```javascript +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 + }); + } +} +``` +**[⬆ torna su](#lista-dei-contenuti)** + +### Liskov Substitution Principle (LSP) (Principio di sostituzione di Liskov) +Questo nome sembra molto più spaventoso di quello che in realtà significa. +Formalmente la sua defnizione è "Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S (per esempio un oggetto di tipo S può sostituire un oggetto di tipo T) senza modificare alcuna della proprietà del software (correttezza, compito da svolgere, etc.)". Questa definizione suona comunque complessa. + +Forse una spiegazione più esaustiva potrebbe essere: "Se hai una classe *figlio* che estende una classe *genitore* allora le due classi possono essere intercambiate all'interno del codice senza generare errori o risultati inattesi". +Potrebbe esssere ancora poco chiaro, ma vediamo con un esempio (Quadrato/Rettangolo): matematicamente il Quadrato è un Rettangolo, ma se il tuo modello eredita una relazione di tipo "è-un", potresti avere presto qualche problema. + +**Da evitare** +```javascript +class Rectangle { + constructor() { this.width = 0; this.height = 0; } + setColor(color) { + // ... + } + + render(area) { + // ... + } + setWidth(width) { this.width = width; } @@ -1222,13 +1328,59 @@ class Rectangle extends Shape { } } -class Square extends Shape { - constructor() { +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(); // Sbagliato: Restituisce 25 anche + // per il quadrato. Dovrebbe essere 20. + rectangle.render(area); + }); +} + +const rectangles = [new Rectangle(), new Rectangle(), new Square()]; +renderLargeRectangles(rectangles); +``` + +**Bene:** +```javascript +class Shape { + setColor(color) { + // ... + } + + render(area) { + // ... + } +} + +class Rectangle extends Shape { + constructor(width, height) { super(); - this.length = 0; + this.width = width; + this.height = height; + } + + getArea() { + return this.width * this.height; } +} - setLength(length) { +class Square extends Shape { + constructor(length) { + super(); this.length = length; } @@ -1239,39 +1391,27 @@ class Square extends Shape { function renderLargeShapes(shapes) { shapes.forEach((shape) => { - switch (shape.constructor.name) { - case 'Square': - shape.setLength(5); - case 'Rectangle': - shape.setWidth(4); - shape.setHeight(5); - } - - let area = shape.getArea(); + const area = shape.getArea(); shape.render(area); }); } -let shapes = [new Rectangle(), new Rectangle(), new Square()]; +const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +### Interface Segregation Principle (ISP) (Principio di segregazione delle interfacce) -### Interface Segregation Principle (ISP) -JavaScript doesn't have interfaces so this principle doesn't apply as strictly -as others. However, it's important and relevant even with JavaScript's lack of -type system. +JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante nonostante la sua mancanza di tipizzazione. -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. +ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [*duck-typing*](https://it.wikipedia.org/wiki/Duck_typing) +Un buon esempio in JavaScript potrebbe essere fatto per le classi che richiedono la deinizione di un set di proprietà molto grande. +Non utilizzare classi che richiedono la definizione di molte proprietà per essere istanziate è sicuramente un beneficio perchè spesso non tutte queste proprietà richiedono di essere impostate per utilizzare la classe. +Rendere questi parametri opzionali evita di avere un'interfaccia pesante. -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". -**Bad:** +**Da evitare** ```javascript class DOMTraverser { constructor(settings) { @@ -1289,15 +1429,15 @@ class DOMTraverser { } } -let $ = new DOMTraverser({ +const $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), - animationModule: function() {} // Most of the time, we won't need to animate when traversing. + animationModule() {} //Il più delle volte potremmo non dover animare questo oggetto // ... }); ``` -**Good**: +**Bene:** ```javascript class DOMTraverser { constructor(settings) { @@ -1322,44 +1462,45 @@ class DOMTraverser { } } -let $ = new DOMTraverser({ +const $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), options: { - animationModule: function() {} + animationModule() {} } }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Dependency Inversion Principle (DIP) -This principle states two essential things: -1. High-level modules should not depend on low-level modules. Both should -depend on abstractions. -2. Abstractions should not depend upon details. Details should depend on -abstractions. +### Dependency Inversion Principle (DIP) (Principio di inversione delle dipendenze) +Questo principio sostanzialmente indica due cose: +1. Moduli ad alto livello non dovrebbero dipendere da molidi a basso livello. Entrambi dovrebbero dipendere da moduli astratti. +2. L'astrazione non dovrebbe dipendere da classi concrete. Le classi concrete dovrebbero dipendere da astrazioni. -This can be hard to understand at first, but if you've worked with Angular.js, -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. +A primo impatto questo concetto potrebbe essere difficile da capire, ma nel caso in cui tu abbia lavorato con AngularJS avrai sicuramente visto l'applicazione di questo principio nel concetto di *Dependency injection (DI)*. Nonostante non sia esattamente identico come concetto, DIP evita che i moduli di alto livello conoscano i dettagli dei moduli di basso livello pur utilizzandoli. +Uno dei benefici di questo utilizzo è che riduce la dipendenza tra due moduli. +La dipendenza tra due moduli è un concetto negativo, perchè ne rende difficile il refactor. +Come detto in precedenza, non essendoci il concetto di interfaccia in JavaScript, tutte le dipendenze sono contratte implicitamente. +Nell'esempio successivo, la dipendenza implicita è che tutte le istanze di `InventoryTracker` avranno un metodo `requestItems`. -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. - -**Bad:** +**Da evitare** ```javascript +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` + //Da evitare: abbiamo creato una dipendenza specifica per ogni istanza. + //Dovremmo fare in modo che requestItems dipenda dal metodo `request` + this.requester = new InventoryRequester(); } @@ -1370,21 +1511,11 @@ class InventoryTracker { } } -class InventoryRequester { - constructor() { - this.REQ_METHODS = ['HTTP']; - } - - requestItem(item) { - // ... - } -} - -let inventoryTracker = new InventoryTracker(['apples', 'bananas']); +const inventoryTracker = new InventoryTracker(['apples', 'bananas']); inventoryTracker.requestItems(); ``` -**Good**: +**Bene:** ```javascript class InventoryTracker { constructor(items, requester) { @@ -1419,268 +1550,36 @@ class InventoryRequesterV2 { } } -// By constructing our dependencies externally and injecting them, we can easily -// substitute our request module for a fancy new one that uses WebSockets. -let inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); -inventoryTracker.requestItems(); -``` -**[⬆ back to top](#table-of-contents)** - -### Prefer ES6 classes over ES5 plain functions -It's very difficult to get readable class inheritance, construction, and method -definitions for classical ES5 classes. If you need inheritance (and be aware -that you might not), then prefer classes. However, prefer small functions over -classes until you find yourself needing larger and more complex objects. - -**Bad:** -```javascript -var Animal = function(age) { - if (!(this instanceof Animal)) { - throw new Error("Instantiate Animal with `new`"); - } - - this.age = age; -}; - -Animal.prototype.move = function() {}; - -var 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() {}; - -var 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() {}; -``` - -**Good:** -```javascript -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() {} -} -``` -**[⬆ back to top](#table-of-contents)** - - -### Use method chaining -Against the advice of Clean Code, this is one place where we will have to differ. -It has been argued that method chaining is unclean and violates the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter). -Maybe it's true, but this pattern is very useful in JavaScript and you see it in -many libraries such as jQuery and Lodash. It allows your code to be expressive, -and less verbose. For that reason, I say, use method chaining and take a look at -how clean your code will be. In your class functions, simply return `this` at -the end of every function, and you can chain further class methods onto it. - -**Bad:** -```javascript -class Car { - constructor() { - this.make = 'Honda'; - this.model = 'Accord'; - this.color = 'white'; - } - - setMake(make) { - this.name = name; - } - - setModel(model) { - this.model = model; - } - - setColor(color) { - this.color = color; - } - - save() { - console.log(this.make, this.model, this.color); - } -} - -let car = new Car(); -car.setColor('pink'); -car.setMake('Ford'); -car.setModel('F-150'); -car.save(); -``` - -**Good**: -```javascript -class Car { - constructor() { - this.make = 'Honda'; - this.model = 'Accord'; - this.color = 'white'; - } - - setMake(make) { - this.name = name; - // NOTE: Returning this for chaining - return this; - } +//Avendo dichiarato la nostra dipendenza esternamente ed aggiunta dall'esterno, la possiamo +//sostituire facilemente con un'altra che utilizza WebSockets - 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); - } -} - -let car = new Car() - .setColor('pink') - .setMake('Ford') - .setModel('F-150') - .save(); -``` -**[⬆ back to top](#table-of-contents)** - -### Prefer composition over inheritance -As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, -you should prefer composition over inheritance where you can. There are lots of -good reasons to use inheritance and lots of good reasons to use composition. -The main point for this maxim is that if your mind instinctively goes for -inheritance, try to think if composition could model your problem better. In some -cases it can. - -You might be wondering then, "when should I use inheritance?" It -depends on your problem at hand, but this is a decent list of when inheritance -makes more sense than composition: - -1. Your inheritance represents an "is-a" relationship and not a "has-a" -relationship (Animal->Human vs. User->UserDetails). -2. You can reuse code from the base classes (Humans can move like all animals). -3. You want to make global changes to derived classes by changing a base class. -(Change the caloric expenditure of all animals when they move). - -**Bad:** -```javascript -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; - } - - // ... -} -``` - -**Good**: -```javascript -class Employee { - constructor(name, email) { - this.name = name; - this.email = email; - - } - - setTaxData(ssn, salary) { - this.taxData = new EmployeeTaxData(ssn, salary); - } - // ... -} - -class EmployeeTaxData { - constructor(ssn, salary) { - this.ssn = ssn; - this.salary = salary; - } - - // ... -} +const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2()); +inventoryTracker.requestItems(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Testing** -Testing is more important than shipping. If you have 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](http://gotwarlost.github.io/istanbul/). +## **Test** +Testare è più importante che rilasciare. Se non hai test o non ne hai un numero adeguato, non saprai se ad ogni rilascio puoi rompere qualcosa. +Decidere quale sia il numero sufficiente di test dipende dal tuo team, ma cercare di coprire il 100% dei casi (per tutti gli stati ed i branch) vuol dire avere massima tranquillità durante i rilasci. +Questo significa che oltre ad utilizzare una suite di test valida, dovresti utilizzare anche un [buon strumento di copertura](http://gotwarlost.github.io/istanbul/). -There's no excuse to not write tests. There's [plenty of good JS test frameworks] -(http://jstherightway.org/#testing-tools), 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. +Non ci sono scuse per non scrivere test. C'è un'abbondanza di ottimi [framewerk per i test in JavaScript](http://jstherightway.org/#Test-tools) quindi cerca quello più adatto alle tue esigenze. +Quando tu ed il tuo team avrete individuato quello più giusto per voi, dovrete iniziare sempre a scrivere i test per ogni modulo/feature che introdurrete nel vostro software. +Se il vostro approccio preferito è quello del TestDrivenDevelopment (TDD) ottimo, ma assicurati di individuare i tuoi obiettivi prima di rilasciare ogni singola feature o eseguire il refactor di una esistente. -### Single concept per test +### Un singolo comportamento per test -**Bad:** +**Da evitare** ```javascript -const assert = require('assert'); +import assert from 'assert'; -describe('MakeMomentJSGreatAgain', function() { - it('handles date boundaries', function() { +describe('MakeMomentJSGreatAgain', () => { + it('handles date boundaries', () => { let date; date = new MakeMomentJSGreatAgain('1/1/2015'); date.addDays(30); - date.shouldEqual('1/31/2015'); + assert.equal('1/31/2015', date); date = new MakeMomentJSGreatAgain('2/1/2016'); date.addDays(28); @@ -1693,47 +1592,48 @@ describe('MakeMomentJSGreatAgain', function() { }); ``` -**Good**: +**Bene:** ```javascript -const assert = require('assert'); +import assert from 'assert'; -describe('MakeMomentJSGreatAgain', function() { - it('handles 30-day months', function() { - let date = new MakeMomentJSGreatAgain('1/1/2015'); +describe('MakeMomentJSGreatAgain', () => { + it('handles 30-day months', () => { + const date = new MakeMomentJSGreatAgain('1/1/2015'); date.addDays(30); - date.shouldEqual('1/31/2015'); + assert.equal('1/31/2015', date); }); - it('handles leap year', function() { - let date = new MakeMomentJSGreatAgain('2/1/2016'); + 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', function() { - let date = new MakeMomentJSGreatAgain('2/1/2015'); + it('handles non-leap year', () => { + const date = new MakeMomentJSGreatAgain('2/1/2015'); date.addDays(28); assert.equal('03/01/2015', date); }); }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Concurrency** -### Use Promises, not callbacks -Callbacks aren't clean, and they cause excessive amounts of nesting. With ES6, -Promises are a built-in global type. Use them! +## **Consequenzialità** +### utilizza le Promise, non funzioni di callback +Le funzioni di callback non sono sempre chiare e possono generare un eccessivo numero di nidificazioni. Con ES2015/ES6 sono nativamente e globalmente accessibili. Utilizzale! -**Bad:** +**Da evitare** ```javascript -require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', function(err, response) { - if (err) { - console.error(err); - } - else { - require('fs').writeFile('article.html', response.body, function(err) { - if (err) { - console.error(err); +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'); } @@ -1743,83 +1643,74 @@ require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', func ``` -**Good**: +**Bene:** ```javascript -require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') - .then(function(response) { - return require('fs-promise').writeFile('article.html', response); +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(function() { + .then(() => { console.log('File written'); }) - .catch(function(err) { + .catch((err) => { console.error(err); }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Async/Await are even cleaner than Promises -Promises are a very clean alternative to callbacks, but ES7 brings async and await -which offer an even cleaner solution. All you need is a function that is prefixed -in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of functions. Use this if you can take advantage of ES7 features -today! +### Async/Await sono addirittura più chiari delle Promise +Le Promise sono una valida e chiara alternativa alle funzioni di callback, ma ES2017/ES8 offrono anche async and await che possono essere addirittura una soluzione più migliore. +Tutto quello che devi fare non è niente altro che scrivere una funzione che abbia prefisso `async` e puoi scrivere la tua logica senza dover concatenare con la kyword `then`. +Utilizza questo approccio se hai la possibilità di utilizzare le feature ES2017/ES8! -**Bad:** +**Da evitare** ```javascript -require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') - .then(function(response) { - return require('fs-promise').writeFile('article.html', response); +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(function() { + .then(() => { console.log('File written'); }) - .catch(function(err) { + .catch((err) => { console.error(err); }); ``` -**Good**: +**Bene:** ```javascript +import { get } from 'request-promise'; +import { writeFile } from 'fs-promise'; + async function getCleanCodeArticle() { try { -<<<<<<< HEAD - var request = await require('request-promise') - var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); - var fileHandle = await require('fs-promise'); -======= - const request = await require('request-promise'); - const response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); - const fileHandle = await require('fs-promise'); ->>>>>>> fadfb57... Fill missing commas - - await fileHandle.writeFile('article.html', response); + const response = await get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'); + await writeFile('article.html', response); console.log('File written'); } catch(err) { - console.log(err); - } + console.error(err); } +} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Error Handling** -Thrown errors are a good thing! They mean the runtime has successfully -identified when something in your program has gone wrong and it's letting -you know by stopping function execution on the current stack, killing the -process (in Node), and notifying you in the console with a stack trace. +## **Gestione degli errori** +Generare errori è una buona cosa. Vuol dire che l'esecuzione del tuo codice ha identificato precisamente quando nel tuo software qualcosa è andato storto e ti permette di interromperne l'esecuzione nello stack corrente terminando il processo (in Node), e notificandolo attraverso la console. -### Don't ignore caught errors -Doing nothing with a caught error doesn't give you the ability to ever fix -or react to said error. Logging the error to the console (`console.log`) -isn't much better as often times it can get lost in a sea of things printed -to the console. If you wrap any bit of code in a `try/catch` it means you -think an error may occur there and therefore you should have a plan, -or create a code path, for when it occurs. +### Non ingnorare gli errori intercettati +Non fare niente con gli errori intercettati non rende possibile correggerli o reagire all'errore. Loggare gli errori nella console (`console.log`) non ti assicura di non perderti nel mare di log stampati in console. +Se invece inserisci il tuo codice all'interno del costrutto `try/catch` vuol dire riconoscere la possibilità che esista un errore nel caso mettere in piedi un modo per gestirlo. -**Bad:** +**Da evitare** ```javascript try { functionThatMightThrow(); @@ -1828,78 +1719,72 @@ try { } ``` -**Good:** +**Bene:** ```javascript try { functionThatMightThrow(); } catch (error) { - // One option (more noisy than console.log): + // Un'ozione (più visibile del console.log): console.error(error); - // Another option: + // Un'altra opzione: notifyUserOfError(error); - // Another option: + // Un'altra opzione: reportErrorToService(error); - // OR do all three! + // Oppure usale tutte e tre! } ``` -### Don't ignore rejected promises -For the same reason you shouldn't ignore caught errors -from `try/catch`. +### Non ignorare le Promise quando vengono rigettate +Per la stessa ragione per cui non dovresti ignorare gli errori con `try/catch`. -**Bad:** +**Da evitare** ```javascript getdata() -.then(data => { - functionThatMightThrow(data); -}) -.catch(error => { - console.log(error); -}); + .then((data) => { + functionThatMightThrow(data); + }) + .catch((error) => { + console.log(error); + }); ``` -**Good:** +**Bene:** ```javascript 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! -}); + .then((data) => { + functionThatMightThrow(data); + }) + .catch((error) => { + // Un'ozione (più visibile del console.log): + console.error(error); + // Un'altra opzione: + notifyUserOfError(error); + // Un'altra opzione: + reportErrorToService(error); + // Oppure usale tutte e tre! + }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + + +## **Formattazione** +La formattazione è soggettiva. Come molte di quelle sopracitate, non esiste una regola assoluta e veloce che devi seguire. Il punto principale, però, è NON DISCUTERE della formattazione. +Ci sono un [sacco di strumenti](http://standardjs.com/rules.html) che automatizzano questo processo. Usane uno. È uno spreco di tempo e denaro per gli sviluppatori discutere della formattazione. -## **Formatting** -Formatting is subjective. Like many rules herein, there is no hard and fast -rule that you must follow. The main point is DO NOT ARGUE over formatting. -There are [tons of tools](http://standardjs.com/rules.html) to automate this. -Use one! It's a waste of time and money for engineers to argue over formatting. +### Utilizza le maiuscole in modo consistente -For things that don't fall under the purview of automatic formatting -(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here -for some guidance. +JavaScript non è tipizzato, per questo l'uso delle maiuscole può darti indicazioni sulle tue variabili, funzioni, etc. Queste regole sono soggettive, per questo tu ed il tuo team potrete scegliere quella che volete. Il punto è: non importa quale regola sceglierete, l'importante è essere consistenti. -### Use consistent capitalization -JavaScript is untyped, so capitalization tells you a lot about your variables, -functions, etc. These rules are subjective, so your team can choose whatever -they want. The point is, no matter what you all choose, just be consistent. -**Bad:** +**Da evitare** ```javascript -var DAYS_IN_WEEK = 7; -var daysInMonth = 30; +const DAYS_IN_WEEK = 7; +const daysInMonth = 30; -var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; +const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restore_database() {} @@ -1908,13 +1793,13 @@ class animal {} class Alpaca {} ``` -**Good**: +**Bene:** ```javascript -var DAYS_IN_WEEK = 7; -var DAYS_IN_MONTH = 30; +const DAYS_IN_WEEK = 7; +const DAYS_IN_MONTH = 30; -var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; -var artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; +const SONGS = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; +const ARTISTS = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restoreDatabase() {} @@ -1922,15 +1807,14 @@ function restoreDatabase() {} class Animal {} class Alpaca {} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Function callers and callees should be close -If a function calls another, keep those functions vertically close in the source -file. Ideally, keep the caller right above the callee. We tend to read code from -top-to-bottom, like a newspaper. Because of this, make your code read that way. +### Richiami e dichiarazioni di funzioni dovrebbero essere vicini +Se una funzione ne richiama un'altra, mantieni queste funzioni verticalmente vicine nel sorgente. Idealmente, mantieni il richiamo subito sopra la dichiarazione. +Generalmente tendiamo a leggere il codice dall'alto verso il basso, come un giornale. Proprio per questo manteniamolo leggibile seguendo questa modalità. -**Bad:** +**Da evitare** ```javascript class PerformanceReview { constructor(employee) { @@ -1941,23 +1825,23 @@ class PerformanceReview { return db.lookup(this.employee, 'peers'); } - lookupMananger() { + lookupManager() { return db.lookup(this.employee, 'manager'); } getPeerReviews() { - let peers = this.lookupPeers(); + const peers = this.lookupPeers(); // ... } perfReview() { - getPeerReviews(); - getManagerReview(); - getSelfReview(); + this.getPeerReviews(); + this.getManagerReview(); + this.getSelfReview(); } getManagerReview() { - let manager = this.lookupManager(); + const manager = this.lookupManager(); } getSelfReview() { @@ -1965,11 +1849,11 @@ class PerformanceReview { } } -let review = new PerformanceReview(user); +const review = new PerformanceReview(employee); review.perfReview(); ``` -**Good**: +**Bene:** ```javascript class PerformanceReview { constructor(employee) { @@ -1977,13 +1861,13 @@ class PerformanceReview { } perfReview() { - getPeerReviews(); - getManagerReview(); - getSelfReview(); + this.getPeerReviews(); + this.getManagerReview(); + this.getSelfReview(); } getPeerReviews() { - let peers = this.lookupPeers(); + const peers = this.lookupPeers(); // ... } @@ -1992,10 +1876,10 @@ class PerformanceReview { } getManagerReview() { - let manager = this.lookupManager(); + const manager = this.lookupManager(); } - lookupMananger() { + lookupManager() { return db.lookup(this.employee, 'manager'); } @@ -2004,60 +1888,59 @@ class PerformanceReview { } } -let review = new PerformanceReview(employee); +const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -## **Comments** -### Only comment things that have business logic complexity. -Comments are an apology, not a requirement. Good code *mostly* documents itself. - -**Bad:** +## **Commento** +### Commenta solo il codice che ha un alto livello di complessità +Commentare è una scusa, non un requisito. Un buon codice *spesso* si spiega da solo. +**Da evitare** ```javascript function hashIt(data) { // The hash - var hash = 0; + let hash = 0; // Length of string - var length = data.length; + const length = data.length; // Loop through every character in data - for (var i = 0; i < length; i++) { + for (let i = 0; i < length; i++) { // Get character code. - var char = data.charCodeAt(i); + const char = data.charCodeAt(i); // Make the hash hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer - hash = hash & hash; + hash &= hash; } } ``` -**Good**: +**Bene:** ```javascript function hashIt(data) { - var hash = 0; - var length = data.length; + let hash = 0; + const length = data.length; - for (var i = 0; i < length; i++) { - var char = data.charCodeAt(i); + for (let i = 0; i < length; i++) { + const char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; // Convert to 32-bit integer - hash = hash & hash; + hash &= hash; } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't leave commented out code in your codebase -Version control exists for a reason. Leave old code in your history. +### Non lasciare parti del tuo codice commentate all'interno dei sorgenti +I sistemi di versioning esistono per un motivo. Lascia il tuo codice vecchio alla storia. -**Bad:** +**Da evitare** ```javascript doStuff(); // doOtherStuff(); @@ -2065,17 +1948,17 @@ doStuff(); // doSoMuchStuff(); ``` -**Good**: +**Bene:** ```javascript doStuff(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Don't have journal comments -Remember, use version control! There's no need for dead code, commented code, -and especially journal comments. Use `git log` to get history! +### Non utilizzare i commenti come un diario +Ricordati di usare sistemi di version control. Non c'è motivo per cui codice non utilizzato, codice commentato e specialmente commenti con riferimenti a date esistano nel tuo file. +Usa `git log` per avere lo storico! -**Bad:** +**Da evitare** ```javascript /** * 2016-12-20: Removed monads, didn't understand them (RM) @@ -2088,24 +1971,23 @@ function combine(a, b) { } ``` -**Good**: +**Bene:** ```javascript function combine(a, b) { return a + b; } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** -### Avoid positional markers -They usually just add noise. Let the functions and variable names along with the -proper indentation and formatting give the visual structure to your code. +### Evita di specificare di cosa si tratta +Generalmente è solo fastidioso. Lascia che le tue funzioni e le tue variabili, insieme ad una corretta indentazioni ti diano una struttura visiva del tuo codice. -**Bad:** +**Da evitare** ```javascript //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation //////////////////////////////////////////////////////////////////////////////// -let $scope.model = { +$scope.model = { menu: 'foo', nav: 'bar' }; @@ -2113,62 +1995,44 @@ let $scope.model = { //////////////////////////////////////////////////////////////////////////////// // Action setup //////////////////////////////////////////////////////////////////////////////// -let actions = function() { +const actions = function() { // ... -} +}; ``` -**Good**: +**Bene:** ```javascript -let $scope.model = { +$scope.model = { menu: 'foo', nav: 'bar' }; -let actions = function() { +const actions = function() { // ... -} -``` -**[⬆ back to top](#table-of-contents)** - -### Avoid legal comments in source files -That's what your `LICENSE` file at the top of your source tree is for. - -**Bad:** -```javascript -/* -The MIT License (MIT) - -Copyright (c) 2016 Ryan McDermott - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE -*/ - -function calculateBill() { - // ... -} -``` - -**Good**: -```javascript -function calculateBill() { - // ... -} +}; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ torna su](#lista-dei-contenuti)** + +## Traduzioni + +Questa guida è disponibile in altre lingue: + + - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: + - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) + - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) + - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) + - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) + - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: + - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) + - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) + - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) + - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) + - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: + [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italiano**: + [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) + +**[⬆ torna su](#lista-dei-contenuti)** \ No newline at end of file From bf099802610963b6935c177d9a385dbc0fd6ef9d Mon Sep 17 00:00:00 2001 From: Francesco Colonna Date: Sat, 13 Oct 2018 14:06:57 +0200 Subject: [PATCH 133/170] Added link to italian translation --- README.md | 952 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 547 insertions(+), 405 deletions(-) diff --git a/README.md b/README.md index 7b141c6e..2c1f9058 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,122 @@ # clean-code-javascript -## Lista dei contenuti - 1. [Introduzione](#introduzione) - 2. [Variabili](#variabili) - 3. [Funzioni](#funzioni) - 4. [Ogetti e strutture dati](#objects-and-data-structures) - 5. [Classi](#Classi) +## Table of Contents + 1. [Introduction](#introduction) + 2. [Variables](#variables) + 3. [Functions](#functions) + 4. [Objects and Data Structures](#objects-and-data-structures) + 5. [Classes](#classes) 6. [SOLID](#solid) - 7. [Test](#Test) + 7. [Testing](#testing) 8. [Concurrency](#concurrency) 9. [Error Handling](#error-handling) 10. [Formatting](#formatting) 11. [Comments](#comments) 12. [Translation](#translation) -## Introduzione -![Immagine umoristica che rappresenta quanto sia possibile stimare la qualità di un software attraverso il numero di parolacce espresse durante la lettura del codice](http://www.osnews.com/images/comics/wtfm.jpg) +## Introduction +![Humorous image of software quality estimation as a count of how many expletives +you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) -Principi di Ingegneria del Software, dal libro di Robert C. Martin +Software engineering principles, from Robert C. Martin's book [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adattati a JavaScript. +adapted for JavaScript. This is not a style guide. It's a guide to producing +[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. -Non si tratta di una guida stilistica, bensì una guida per cercare di produrre software -[leggibile, riutilizzabile e rifattorizzabile](https://github.com/ryanmcdermott/3rs-of-software-architecture) in JavaScript. +Not every principle herein has to be strictly followed, and even fewer will be +universally agreed upon. These are guidelines and nothing more, but they are +ones codified over many years of collective experience by the authors of +*Clean Code*. -Non tutti i principi di questa guida devono essere seguiti alla lettera, e solo alcuni sono universalmente condivisi. Sono linee guida e niente più, ma sono state tutte apprese in anni di esperienza collettiva dall'autore di *Clean code*. +Our craft of software engineering is just a bit over 50 years old, and we are +still learning a lot. When software architecture is as old as architecture +itself, maybe then we will have harder rules to follow. For now, let these +guidelines serve as a touchstone by which to assess the quality of the +JavaScript code that you and your team produce. -Il nostro lavoro come software engeneer esiste da soli 50 anni e stiamo ancora cercando di apprendere molto. Quando l'architettura del software godrà della stessa anzianità dell'architettura in sè, probabilmente avremo regole più rigide da seguire. Per ora facciamo si che queste linee guida servano come termine di paragone per valutare la qualità del software che tu ed il tuo team producete. +One more thing: knowing these won't immediately make you a better software +developer, and working with them for many years doesn't mean you won't make +mistakes. Every piece of code starts as a first draft, like wet clay getting +shaped into its final form. Finally, we chisel away the imperfections when +we review it with our peers. Don't beat yourself up for first drafts that need +improvement. Beat up the code instead! -Un ultima cosa: conoscere queste regole non farà di te immediatamente un developer migliore, e lavorare per tanti anni come tale non ti eviterà di commettere errori. -Ogni singola parte di codice parte come bozza, inizialmente, per per poi prendere forma esattamente come una scultura di argilla. -Solo alla fine perfezioneremo il nostro software, quando revisioneremo il codice con i nostri colleghi. Ma non abbatterti tu la prima volta che il tuo codice sarà revisionato e richiederà miglioramenti: *Abbatti il codice!* +## **Variables** +### Use meaningful and pronounceable variable names -## **Variabili** -### Utilizza nomi di variabili comprensibili e pronunciabili - -**Da evitare** +**Bad:** ```javascript const yyyymmdstr = moment().format('YYYY/MM/DD'); ``` -**Bene:** +**Good:** ```javascript const currentDate = moment().format('YYYY/MM/DD'); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Usa la stessa semantica per lo stesso tipo di variabili +### Use the same vocabulary for the same type of variable -**Da evitare** +**Bad:** ```javascript getUserInfo(); getClientData(); getCustomerRecord(); ``` -**Bene:** +**Good:** ```javascript getUser(); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Utilizza nomi che possano essere cercati -Leggiamo molto più codice di quanto non ne scriveremo mai. È importante che il codice che noi scriviamo sia leggibile e ricercabile. Nominando variabili che non assumono uno specifico contesto all'interno del nostro software, possiamo irritare chi lo legge. -Fai in modo che i nomi delle tue variabili siano ricercabili. -Strumenti come [buddy.js](https://github.com/danielstjules/buddy.js) e -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) possono aiutarti ad identificare, per esempio, costanti non rinominate. +### Use searchable names +We will read more code than we will ever write. It's important that the code we +do write is readable and searchable. By *not* naming variables that end up +being meaningful for understanding our program, we hurt our readers. +Make your names searchable. Tools like +[buddy.js](https://github.com/danielstjules/buddy.js) and +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) +can help identify unnamed constants. -**Da evitare** +**Bad:** ```javascript -// Cosa caspita significa 86400000? +// What the heck is 86400000 for? setTimeout(blastOff, 86400000); ``` -**Bene:** +**Good:** ```javascript -// Dichiarala come costante in maiuscolo. -const MILLISECONDI_IN_UN_GIORNO = 86400000; +// Declare them as capitalized named constants. +const MILLISECONDS_IN_A_DAY = 86400000; -setTimeout(blastOff, MILLISECONDI_IN_UN_GIORNO); +setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Utilizza nomi di variabili esplicartivi -**Da evitare** +### Use explanatory variables +**Bad:** ```javascript const address = 'One Infinite Loop, Cupertino 95014'; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]); ``` -**Bene:** +**Good:** ```javascript const address = 'One Infinite Loop, Cupertino 95014'; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; const [, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evita mappe mentali -Essere espliciti è meglio che non esserlo. +### Avoid Mental Mapping +Explicit is better than implicit. -**Da evitare** +**Bad:** ```javascript const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((l) => { @@ -113,12 +125,12 @@ locations.forEach((l) => { // ... // ... // ... - // A cosa fa riferimento esattamente `l`? + // Wait, what is `l` for again? dispatch(l); }); ``` -**Bene:** +**Good:** ```javascript const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { @@ -130,13 +142,13 @@ locations.forEach((location) => { dispatch(location); }); ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Non contestualizzare inutilmente +**[⬆ back to top](#table-of-contents)** -Se il nome della tua classe/oggetto ti indica a cosa fa riferimento, non ripeterlo nei nomi delle sue proprietà o funzioni. +### Don't add unneeded context +If your class/object name tells you something, don't repeat that in your +variable name. -**Da evitare** +**Bad:** ```javascript const Car = { carMake: 'Honda', @@ -149,7 +161,7 @@ function paintCar(car) { } ``` -**Bene:** +**Good:** ```javascript const Car = { make: 'Honda', @@ -161,15 +173,15 @@ function paintCar(car) { car.color = 'Red'; } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Utilizza i valori di default (predefiniti), anzichè usare condizioni o valutazioni minime +### Use default arguments instead of short circuiting or conditionals +Default arguments are often cleaner than short circuiting. Be aware that if you +use them, your function will only provide default values for `undefined` +arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and +`NaN`, will not be replaced by a default value. -I valori di default, generalmente sono più chiari dei [valutazioni minime](https://it.wikipedia.org/wiki/Valutazione_a_corto_circuito). Tieni presente che se non utilizzerai questo approccio, la tua funzione restituirà solo `undefined` come valore di default. -Tutti gli altri valori "falsy" come `''`, `""`, `false`, `null`, `0`, e -`NaN`, non saranno sostituiti da un valore predefinito. - -**Da evitare** +**Bad:** ```javascript function createMicrobrewery(name) { const breweryName = name || 'Hipster Brew Co.'; @@ -178,40 +190,52 @@ function createMicrobrewery(name) { ``` -**Bene:** +**Good:** ```javascript function createMicrobrewery(name = 'Hipster Brew Co.') { // ... } ``` -**[⬆ torna su](#lista-dei-contenuti)** - -## **Funzioni** -### Argomenti di una funzione (idealmente 2 o anche meno) +**[⬆ back to top](#table-of-contents)** -Limitare il numero di argomenti di una funzione è incredibilmente importante perchè ti permette di testarla più facilmente. Avere più di 3 argomenti può portare ad un'esplosione di combinazioni da testare, che produrranno una lunga serie di casi da verificare. +## **Functions** +### Function arguments (2 or fewer ideally) +Limiting the amount of function parameters is incredibly important because it +makes testing your function easier. Having more than three leads to a +combinatorial explosion where you have to test tons of different cases with +each separate argument. -1 o 2 argomenti sono l'ideale e dovremmo evitarne un terzo se possibile. Generalmente se la tua funzione ha più di 2 argomenti, forse, sta facendo troppe operazioni. Nei casi in cui questo non sia del tutto vero, un oggetto può aiutare ad ovviare a questo problema. +One or two arguments is the ideal case, and three should be avoided if possible. +Anything more than that should be consolidated. Usually, if you have +more than two arguments then your function is trying to do too much. In cases +where it's not, most of the time a higher-level object will suffice as an +argument. -Dal momento in cui JavaScript permette la creazione di oggetti al volo puoi usare un oggetto, se pensi che il tuo metodo richieda molti argomenti. +Since JavaScript allows you to make objects on the fly, without a lot of class +boilerplate, you can use an object if you are finding yourself needing a +lot of arguments. -Per rendere evidente cosa la funzione si aspetta di ricevere, puoi utilizzare la sintassi destrutturata (destructuring syntax) di ES2015/ES6 che ha diversi vantaggi: +To make it obvious what properties the function expects, you can use the ES2015/ES6 +destructuring syntax. This has a few advantages: -1. Quando qualcuno osserva la firma della tua funzione, è immediatamente chiaro che proprietà saranno utilizzate +1. When someone looks at the function signature, it's immediately clear what +properties are being used. +2. Destructuring also clones the specified primitive values of the argument +object passed into the function. This can help prevent side effects. Note: +objects and arrays that are destructured from the argument object are NOT +cloned. +3. Linters can warn you about unused properties, which would be impossible +without destructuring. -2. Destrutturare, oltretutto, clona i valori primitivi passati alla funzione. Questo può prevenire effetti inattesi. **Nota**: oggetti ed array destrutturati nell'oggetto usato come argomento NON saranno clonati. - -3. Un Linter può avvisarti che non stai utilizzando alcune delle proprietà del tuo oggetto, diversamente non sarebbe possibile. - -**Da evitare** +**Bad:** ```javascript function createMenu(title, body, buttonText, cancellable) { // ... } ``` -**Bene:** +**Good:** ```javascript function createMenu({ title, body, buttonText, cancellable }) { // ... @@ -224,14 +248,17 @@ createMenu({ cancellable: true }); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Un metodo dovrebbe fare una sola cosa -Questa è di sicuro la regola più importante nell'ingegneria del software. Quando un metodo si occupa di più di un solo aspetto sarà più difficile da testare, comporre e ragioraci sopra. -Se è possibile far eseguire al metodo una sola azione sarà più facile il suo refactor e la leggibilità del tuo codice sarà maggiore e più chiara. Anche se non dovesse rimanerti in mente altro di questa guida, sarai comunque più avanti di molti sviluppatori. +### Functions should do one thing +This is by far the most important rule in software engineering. When functions +do more than one thing, they are harder to compose, test, and reason about. +When you can isolate a function to just one action, they can be refactored +easily and your code will read much cleaner. If you take nothing else away from +this guide other than this, you'll be ahead of many developers. -**Da evitare** +**Bad:** ```javascript function emailClients(clients) { clients.forEach((client) => { @@ -243,7 +270,7 @@ function emailClients(clients) { } ``` -**Bene:** +**Good:** ```javascript function emailActiveClients(clients) { clients @@ -256,11 +283,11 @@ function isActiveClient(client) { return clientRecord.isActive(); } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### I nomi delle funzioni dovrebbero farti capire cosa fanno +### Function names should say what they do -**Da evitare** +**Bad:** ```javascript function addToDate(date, month) { // ... @@ -268,11 +295,11 @@ function addToDate(date, month) { const date = new Date(); -// Difficile da dire esattamente cosa viene aggiunto tramite questa funzione +// It's hard to tell from the function name what is added addToDate(date, 1); ``` -**Bene:** +**Good:** ```javascript function addMonthToDate(month, date) { // ... @@ -281,13 +308,14 @@ function addMonthToDate(month, date) { const date = new Date(); addMonthToDate(1, date); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Le funzioni dovrebbero avere un solo livello di astrazione +### Functions should only be one level of abstraction +When you have more than one level of abstraction your function is usually +doing too much. Splitting up functions leads to reusability and easier +testing. -Quando hai più di un livello di astrazione, la tua funzione generalmente sta facendo troppe cose. Dividere in più funzioni aiuta a riutilizzarle e testare più facilmente. - -**Da evitare** +**Bad:** ```javascript function parseBetterJSAlternative(code) { const REGEXES = [ @@ -313,7 +341,7 @@ function parseBetterJSAlternative(code) { } ``` -**Bene:** +**Good:** ```javascript function parseBetterJSAlternative(code) { const tokens = tokenize(code); @@ -348,20 +376,31 @@ function lexer(tokens) { return ast; } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Rimuovi il codice duplicato -Fai del tuo meglio per evitare codice duplicato. Duplicare il codice è un male, perchè vuol dire che c'è più di un punto da modificare nel caso in cui dovessi cambiare alcune logiche. +### Remove duplicate code +Do your absolute best to avoid duplicate code. Duplicate code is bad because it +means that there's more than one place to alter something if you need to change +some logic. -Immagina di avere un ristorante e di dover tener traccia del tuo magazzino: la riserva di pomodori, cipolle, aglio, spezie, etc. Se hai più di una lista in cui tieni traccia di queste quantità dovrai aggiornarle tutte ogni volta che servirai, per esempio, un piatto con dei pomodori. Al contrario, se dovessi avere una sola lista, avrai un solo posto un cui dovrai tenere traccia delle modifiche sulle quantità in magazzino. +Imagine if you run a restaurant and you keep track of your inventory: all your +tomatoes, onions, garlic, spices, etc. If you have multiple lists that +you keep this on, then all have to be updated when you serve a dish with +tomatoes in them. If you only have one list, there's only one place to update! -Generalmente si duplica il codice perchè ci sono due o tre piccole differenze tra una parte e l'altra del software. Questo permette di condividere le parti comuni del codice, ma allo stesso tempo avrai dei duplicati di parti che fanno la stessa cosa. -Rimuovere questi duplicati, significa creare un'astrazione che permette di gestire queste differenze attraverso un unico metodo/modulo/classe. +Oftentimes you have duplicate code because you have two or more slightly +different things, that share a lot in common, but their differences force you +to have two or more separate functions that do much of the same things. Removing +duplicate code means creating an abstraction that can handle this set of +different things with just one function/module/class. -Ottenere la sufficiente astrazione può essere complicato. Per questo dovresti seguire i principi SOLID, approfonditi nella sezione *Classi*. -Un'astrazione non ottimale potrebbe anche essere peggio del codice duplicato, per cui fai attenzione! Non ripeterti, altrimenti dovrai aggiornare tutte le occorrenze della stessa logica ogni volta che vorrai cambiare qualcosa. +Getting the abstraction right is critical, that's why you should follow the +SOLID principles laid out in the *Classes* section. Bad abstractions can be +worse than duplicate code, so be careful! Having said this, if you can make +a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself +updating multiple places anytime you want to change one thing. -**Da evitare** +**Bad:** ```javascript function showDeveloperList(developers) { developers.forEach((developer) => { @@ -394,7 +433,7 @@ function showManagerList(managers) { } ``` -**Bene:** +**Good:** ```javascript function showEmployeeList(employees) { employees.forEach((employee) => { @@ -419,11 +458,11 @@ function showEmployeeList(employees) { }); } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Estendi un oggetto con Object.assign +### Set default objects with Object.assign -**Da evitare** +**Bad:** ```javascript const menuConfig = { title: null, @@ -442,7 +481,7 @@ function createMenu(config) { createMenu(menuConfig); ``` -**Bene:** +**Good:** ```javascript const menuConfig = { title: 'Order', @@ -459,20 +498,19 @@ function createMenu(config) { cancellable: true }, config); - // config adesso sarà: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig); ``` -**[⬆ torna su](#lista-dei-contenuti)** - +**[⬆ back to top](#table-of-contents)** -### Non usare valori flag (true/false) come parametri di una funzione -Un valore di tipo flag indica che la tua funzione può eseguire più di una sola operazione. Una funzione dovrebbe eseguire una sola operazione. Separa la tua funzione se deve eseguire più di una operazione in base al parametro flag che riceve in input. +### Don't use flags as function parameters +Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. -**Da evitare** +**Bad:** ```javascript function createFile(name, temp) { if (temp) { @@ -483,7 +521,7 @@ function createFile(name, temp) { } ``` -**Bene:** +**Good:** ```javascript function createFile(name) { fs.create(name); @@ -493,24 +531,28 @@ function createTempFile(name) { createFile(`./temp/${name}`); } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evitare effetti inattesi (parte 1) +### Avoid Side Effects (part 1) +A function produces a side effect if it does anything other than take a value in +and return another value or values. A side effect could be writing to a file, +modifying some global variable, or accidentally wiring all your money to a +stranger. -Una funzione può generare un effetto collaterale se fa altro oltre a ricevere un valore e restituirne uno o più. L'effetto collaterale potrebbe essere scrivere su un file, modificare una variabile globale o accidentalmente girare tutti i tuoi soldi ad uno sconosciuto. +Now, you do need to have side effects in a program on occasion. Like the previous +example, you might need to write to a file. What you want to do is to +centralize where you are doing this. Don't have several functions and classes +that write to a particular file. Have one service that does it. One and only one. -Probabilmente, e occasionalmente, avrai bisogno di generare un effetto collaterale: come nell'esempio precedente magari proprio scrivere su un file. -Quello che dovrai fare è centralizzare il punto in cui lo fai. Non avere più funzioni e classi che fanno la stessa cosa, ma averne un servizio ed uno soltanto che se ne occupa. +The main point is to avoid common pitfalls like sharing state between objects +without any structure, using mutable data types that can be written to by anything, +and not centralizing where your side effects occur. If you can do this, you will +be happier than the vast majority of other programmers. -La questione più importante è evitare le insidie che stanno dietro ad errate manipolazioni di oggetti senza alcuna struttura, utilizzando strutture che possono essere modificate da qualunque parte. -Se riuscirai ad evitare che questo accada...sarai ben più felice della maggior parte degli altri programmatori. - -**Da evitare** +**Bad:** ```javascript - -// Variable globale utilizzata dalla funzione seguente. -// Nel caso in cui dovessimo utilizzarla in un'altra funzione a questo punto si tratterebbe di un array e potrebbe generare un errore. - +// 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 name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { @@ -522,7 +564,7 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**Bene:** +**Good:** ```javascript function splitIntoFirstAndLastName(name) { return name.split(' '); @@ -534,45 +576,69 @@ const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Evitare effetti inattesi (parte 2) -In Javascript i valori primitivi sono passati come valori, mentre oggetti ed array vengono passati come reference. Nel caso di oggetti ed array, se la tua funzione modifica l'array contenente un carrello della spesa, per esempio, aggiungendo o rimuovendo un oggetto da acquistare, tutte le altre funzioni che utilizzeranno l'array `carrello` saranno condizionati da questa modifica. Questo potrebbe essere un bene ed un male allo stesso tempo. Immaginiamo una situazione in cui questo è un male: - -l'utente clicca sul tasto "Acquista", che richiamerà una funzione `acquista` che effettua una richiesta ed invia l'array `carrello` al server. Per via di una pessima connessione, il metodo `acquista` riproverà ad effettuare la richiesta al server. Cosa succede se nello stesso momento accidentalmemte l'utente clicca su "Aggiungi al carrello" su di un oggetto che non ha intenzione di acquistare prima che venga eseguita nuovamente la funzione? -Verrà inviata la richiesta con il nuovo oggetto accidentalmente aggiunto al carrello utilizzando la funzione `aggiungiOggettoAlCarrello`. - -Un'ottima soluzione è quella di di clonare sempre l'array `carrello`, modificarlo e restituire il clone. -Questo ci assicurerà che che nessun'altra funzione che gestisce il carrello subirà cambiamenti non voluti. - -Due precisazioni vanno fatte su questo approccio: - -1. Potrebbe essere che tu voglia realmente modificare l'oggetto in input, ma vedrai che utilizzando questo approccio ti accorgerai che questi casi sono veramente rari. La maggior parte delle volte dovrai utilizzare questo approccio per non generare effetti inattesi - -2. Clonare oggetti molto grandi potrebbe essere davvero dispendioso in termini di risorse. Fortunatamente questo non è un problema perchè esistono [ottime librerie](https://facebook.github.io/immutable-js/) che permettono di utilizzare questo approccio senza dispendio di memoria e più velocemente rispetto al dovelo fare manualmente. - -**Da evitare** +**[⬆ back to top](#table-of-contents)** + +### Avoid Side Effects (part 2) +In JavaScript, primitives are passed by value and objects/arrays are passed by +reference. In the case of objects and arrays, if your function makes a change +in a shopping cart array, for example, by adding an item to purchase, +then any other function that uses that `cart` array will be affected by this +addition. That may be great, however it can be bad too. Let's imagine a bad +situation: + +The user clicks the "Purchase", button which calls a `purchase` function that +spawns a network request and sends the `cart` array to the server. Because +of a bad network connection, the `purchase` function has to keep retrying the +request. Now, what if in the meantime the user accidentally clicks "Add to Cart" +button on an item they don't actually want before the network request begins? +If that happens and the network request begins, then that purchase function +will send the accidentally added item because it has a reference to a shopping +cart array that the `addItemToCart` function modified by adding an unwanted +item. + +A great solution would be for the `addItemToCart` to always clone the `cart`, +edit it, and return the clone. This ensures that no other functions that are +holding onto a reference of the shopping cart will be affected by any changes. + +Two caveats to mention to this approach: + 1. 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! + + 2. 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](https://facebook.github.io/immutable-js/) 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. + +**Bad:** ```javascript const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); }; ``` -**Bene:** +**Good:** ```javascript const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; }; ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Non aggiungere funzioni globali -Contaminare o aggiungere delle variabili globali è una pratica sconsigliata, in quanto potresti entrare in conflitto con altre librerie e chi utiliza le tue API potrebbe non accorgersene fintanto che non si trova in produzione, generando un'eccezione. -Facciamo un esempio pratico: supponiamo che tu voglia estendere il costrutto Array nativo di JavaScript aggiungendo il metodo `diff` che mostra le differenze tra due array. Come puoi fare? -Potresti scrivere il metodo utilizzando `Array.prototype`, che però potrebbe entrare in conflitto con con un'altra libreria che fa la stessa cosa. Cosa succederebbe se anche l'altra libreria utilizzasse `diff` per trovare le differenze tra due array? Ecco perchè è molto meglio utilizzare le classi ES2015/ES6 e semplicemente estendere `Array`. +### Don't write to global functions +Polluting globals is a bad practice in JavaScript because you could clash with another +library and the user of your API would be none-the-wiser until they get an +exception in production. Let's think about an example: what if you wanted to +extend JavaScript's native Array method to have a `diff` method that could +show the difference between two arrays? You could write your new function +to the `Array.prototype`, but it could clash with another library that tried +to do the same thing. What if that other library was just using `diff` to find +the difference between the first and last elements of an array? This is why it +would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. -**Da evitare** +**Bad:** ```javascript Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); @@ -580,7 +646,7 @@ Array.prototype.diff = function diff(comparisonArray) { }; ``` -**Bene:** +**Good:** ```javascript class SuperArray extends Array { diff(comparisonArray) { @@ -589,18 +655,14 @@ class SuperArray extends Array { } } ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Preferisci la programmazione funzionale a quella imperativa - -*[Programmazione funzionale](https://it.wikipedia.org/wiki/Programmazione_funzionale)* - -*[Programmazione imperativa](https://it.wikipedia.org/wiki/Programmazione_imperativa)* +**[⬆ back to top](#table-of-contents)** -Javascript non è un linguaggio funzionale alla stregua di [Haskell](https://www.haskell.org/), ma entrambi hanno qualcosa che li accomuna. -I linguaggi funzionali generalmente sono più puliti e facili da testare. -Preferisci questo stile se possibile. +### Favor functional programming over imperative programming +JavaScript isn't a functional language in the way that Haskell is, but it has +a functional flavor to it. Functional languages can be cleaner and easier to test. +Favor this style of programming when you can. -**Da evitare** +**Bad:** ```javascript const programmerOutput = [ { @@ -625,7 +687,7 @@ for (let i = 0; i < programmerOutput.length; i++) { } ``` -**Bene:** +**Good:** ```javascript const programmerOutput = [ { @@ -647,18 +709,18 @@ const totalOutput = programmerOutput .map(output => output.linesOfCode) .reduce((totalLines, lines) => totalLines + lines); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Incapsula le condizioni +### Encapsulate conditionals -**Da evitare** +**Bad:** ```javascript if (fsm.state === 'fetching' && isEmpty(listNode)) { // ... } ``` -**Bene:** +**Good:** ```javascript function shouldShowSpinner(fsm, listNode) { return fsm.state === 'fetching' && isEmpty(listNode); @@ -668,11 +730,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evita di verificare condizioni in negativo +### Avoid negative conditionals -**Da evitare** +**Bad:** ```javascript function isDOMNodeNotPresent(node) { // ... @@ -683,7 +745,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**Bene:** +**Good:** ```javascript function isDOMNodePresent(node) { // ... @@ -693,17 +755,19 @@ if (isDOMNodePresent(node)) { // ... } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evita le condizioni -Sembrerebbe un task impossibile. Ad un rapido sguardo molti sviluppatori potrebbero pensare "come posso pensare di far funzionare qualcosa senza utilizzare un `if`?" -La risposta è che puoi utilizzare il polimorfismo per ottenere lo stesso risultato in molti casi. -La seconda domanda generalmente è "Ottimo! Ma perchè dovrei farlo?". -La risposta è data in uno dei concetti precedentemente descritti: una funzione dovrebbe eseguire una sola operazione. -Quando hai una Classe con delle funzioni che utilizzano lo stato `if` stai dicendo all'utente che la tua funzione può fare più di una operazione. +### Avoid conditionals +This seems like an impossible task. Upon first hearing this, most people say, +"how am I supposed to do anything without an `if` statement?" The answer is that +you can use polymorphism to achieve the same task in many cases. The second +question is usually, "well that's great but why would I want to do that?" The +answer is a previous clean code concept we learned: a function should only do +one thing. When you have classes and functions that have `if` statements, you +are telling your user that your function does more than one thing. Remember, +just do one thing. - -**Da evitare** +**Bad:** ```javascript class Airplane { // ... @@ -720,7 +784,7 @@ class Airplane { } ``` -**Bene:** +**Good:** ```javascript class Airplane { // ... @@ -747,15 +811,15 @@ class Cessna extends Airplane { } } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evita la validazione dei tipi (parte 1) -JavaScript è un linguaggio non tipizzato, il che significa che le tue funzioni possono accettare qualunque tipo di argomento. -Qualche volta potresti essere tentato da tutta questa libertà e potresti essere altrettanto tentato di veririfcare il tipo di dato ricevuto nella tua funzione. -Ci sono molti modi per evitare di dover fare questo tipo di controllo. -Come prima cosa cerca scrivere API consistenti. +### Avoid type-checking (part 1) +JavaScript is untyped, which means your functions can take any type of argument. +Sometimes you are bitten by this freedom and it becomes tempting to do +type-checking in your functions. There are many ways to avoid having to do this. +The first thing to consider is consistent APIs. -**Da evitare** +**Bad:** ```javascript function travelToTexas(vehicle) { if (vehicle instanceof Bicycle) { @@ -766,21 +830,26 @@ function travelToTexas(vehicle) { } ``` -**Bene:** +**Good:** ```javascript function travelToTexas(vehicle) { vehicle.move(this.currentLocation, new Location('texas')); } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evita la validazione dei tipi (part 2) -Se stai lavorando con tipi di dati primitivi come stringhe o interi e non puoi utilizzare il paradigma del polimorfismo, ma senti ancora l'esigenza di validare il tipo di dato considera l'utilizzo di [TypeScript](https://www.typescriptlang.org/). -È una vlidissima alternativa al normale JavaScript che fornicsce una validazione di tipi statica utilizzando la sintassi JavaScript. -Il problema con la validazione manuale dei tipi utilizzando lo standard JavaScript è che richiede tanto codice extra che non compensa la scarsa leggibilità del codice ottenuto. -Mantieni pulito il tuo codice, scrivi dei test validi e cerca di fare delle buone revisioni del coidice. Altrimenti utilizza TypeScript (che come detto in precedenza è una validissima alternativa!) +### Avoid type-checking (part 2) +If you are working with basic primitive values like strings and integers, +and you can't use polymorphism but you still feel the need to type-check, +you should consider using TypeScript. It is an excellent alternative to normal +JavaScript, as it provides you with static typing on top of standard JavaScript +syntax. The problem with manually type-checking normal JavaScript is that +doing it well requires so much extra verbiage that the faux "type-safety" you get +doesn't make up for the lost readability. Keep your JavaScript clean, write +good tests, and have good code reviews. Otherwise, do all of that but with +TypeScript (which, like I said, is a great alternative!). -**Da evitare** +**Bad:** ```javascript function combine(val1, val2) { if (typeof val1 === 'number' && typeof val2 === 'number' || @@ -792,41 +861,45 @@ function combine(val1, val2) { } ``` -**Bene:** +**Good:** ```javascript function combine(val1, val2) { return val1 + val2; } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Non ottimizzare eccessivamente -I browser moderni eseguono un sacco di ottimizzazione "sottobanco". Molte volte, quando cerchi di ottimizzare il tuo codice, stai perdendo tempo prezioso. [Ci sono tante guide](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) che aiutano a capire quali tipi di ottimizzazione sono superflui e quali no. Utilizza queste guide nel frattempo, fintanto che queste lacune non verranno colmate. +### Don't over-optimize +Modern browsers do a lot of optimization under-the-hood at runtime. A lot of +times, if you are optimizing then you are just wasting your time. [There are good +resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) +for seeing where optimization is lacking. Target those in the meantime, until +they are fixed if they can be. -**Da evitare** +**Bad:** ```javascript -// Nei vecchi browser ogni volta che in un ciclo verifichi `list.length` potrebbe -// essere dispendioso per via del suo ricalcolo. Nei browser moderni -// questa operazione è stata ottimizzata +// On old browsers, each iteration with uncached `list.length` would be costly +// because of `list.length` recomputation. In modern browsers, this is optimized. for (let i = 0, len = list.length; i < len; i++) { // ... } ``` -**Bene:** +**Good:** ```javascript for (let i = 0; i < list.length; i++) { // ... } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Rimuovi il codice inutilizzato -Il codice inutilizzato è dannoso quanto il codice duplicato. Non c'è motivo per cui tenerlo nel tuo codebase. Se realmente non viene utilizzato rimuovilo! -Sarà comunque presente nella storia del tuo file se utilizzi un sistema di versioning nel caso in cui dovesse servirti. +### Remove dead code +Dead code is just as bad as duplicate code. There's no reason to keep it in +your codebase. If it's not being called, get rid of it! It will still be safe +in your version history if you still need it. -**Da evitare** +**Bad:** ```javascript function oldRequestModule(url) { // ... @@ -841,7 +914,7 @@ inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**Bene:** +**Good:** ```javascript function newRequestModule(url) { // ... @@ -850,20 +923,24 @@ function newRequestModule(url) { const req = newRequestModule; inventoryTracker('apples', req, 'www.inventory-awesome.io'); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** + +## **Objects and Data Structures** +### Use getters and setters +Using getters and setters to access data on objects could be better than simply +looking for a property on an object. "Why?" you might ask. Well, here's an +unorganized list of reasons why: -## **Ogetti e strutture dati** -### Utilizza getter e setter -Utilizzare getter e setter per acceedere ai dati di un oggetto può essere meglio che accedere direttamente alle sue proprietà. Ti starai sicuramente chiedendo il motivo. -Eccoti qualche motivo per cui utilizzare getter e setter: -* Quando hai bisogno di eseguire un'operazione sul dato recuperato, non devi andare a modificarlo ogni volta che accedi al dato nel tuo codice. -* Valida il dato nel momento in cui lo setti con `set` -* Incapsula la rappresentazione interna -* È più facile loggare e gestire gli errori quando imposti o recuperi un dato -* Puoi caricare i dati del tuo oggetto in modalità *lazy*, per esempio caricandoli dal server. +* When you want to do more beyond getting an object property, you don't have +to look up and change every accessor in your codebase. +* Makes adding validation simple when doing a `set`. +* Encapsulates the internal representation. +* Easy to add logging and error handling when getting and setting. +* You can lazy load your object's properties, let's say getting it from a +server. -**Da evitare** +**Bad:** ```javascript function makeBankAccount() { // ... @@ -878,20 +955,20 @@ const account = makeBankAccount(); account.balance = 100; ``` -**Bene:** +**Good:** ```javascript function makeBankAccount() { - // questa proprietà è privata + // this one is private let balance = 0; - // un "getter", la rende pubblica restituendola in questo modo + // a "getter", made public via the returned object below function getBalance() { return balance; } - // un "setter", la rende pubblica in questo modo + // a "setter", made public via the returned object below function setBalance(amount) { - // ... e la valida prima di impostarla + // ... validate before updating the balance balance = amount; } @@ -905,13 +982,13 @@ function makeBankAccount() { const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Imposta proprietà private in un oggetto -Può essere fatto attraverso le *closure* (per ES5 e versioni precedenti). +### Make objects have private members +This can be accomplished through closures (for ES5 and below). -**Da evitare** +**Bad:** ```javascript const Employee = function(name) { @@ -928,7 +1005,7 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**Bene:** +**Good:** ```javascript function makeEmployee(name) { return { @@ -943,14 +1020,17 @@ console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -## **Classi** -### Utilizza le classi ES2015/ES6 piuttosto che le funzioni di ES5 -È molto difficile ottenere leggibilità sull'ereditarietà, costrutti e metodi in definizioni di oggetti in ES5. Se hai bisogno di ereditarietà (e bada bene, non è detto che tu ne abbia bisogno), utilizza il costrutto Class di ES2015/ES6. Altrimenti utilizza piccole funzioni fintanto che non avrai bisogno di gestire oggetti più complessi. +## **Classes** +### Prefer ES2015/ES6 classes over ES5 plain functions +It's very difficult to get readable class inheritance, construction, and method +definitions for classical ES5 classes. If you need inheritance (and be aware +that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over +classes until you find yourself needing larger and more complex objects. -**Da evitare** +**Bad:** ```javascript const Animal = function(age) { if (!(this instanceof Animal)) { @@ -989,7 +1069,7 @@ Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ``` -**Bene:** +**Good:** ```javascript class Animal { constructor(age) { @@ -1017,15 +1097,17 @@ class Human extends Mammal { speak() { /* ... */ } } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Concatena i metodi -Questo pattern è molto utilizzato in JavaScript e puoi trovarne applicazione in molte liberie come [jQuery](https://jquery.com/) e [Lodash](https://lodash.com/). Permette al tuo codice di essere maggiormente espressivo e meno verboso. -Proprio per questo motivo, insisto, utilizza la concatenazione dei metodi e guarda come può essere più pulito il tuo codice. Nei metodi della tua classe, semplicemente restituisci il riferimento `this` alla fine di ogni metodo, in modo da poter concatenare altri metodi. +### Use method chaining +This pattern is very useful in JavaScript and you see it in many libraries such +as jQuery and Lodash. It allows your code to be expressive, and less verbose. +For that reason, I say, use method chaining and take a look at how clean your code +will be. In your class functions, simply return `this` at the end of every function, +and you can chain further class methods onto it. - -**Da evitare** +**Bad:** ```javascript class Car { constructor(make, model, color) { @@ -1056,7 +1138,7 @@ car.setColor('pink'); car.save(); ``` -**Bene:** +**Good:** ```javascript class Car { constructor(make, model, color) { @@ -1067,25 +1149,25 @@ class Car { setMake(make) { this.make = make; - // NOTA: restituisci this per poter concatenare altri metodi. + // NOTE: Returning this for chaining return this; } setModel(model) { this.model = model; - // NOTA: restituisci this per poter concatenare altri metodi. + // NOTE: Returning this for chaining return this; } setColor(color) { this.color = color; - // NOTA: restituisci this per poter concatenare altri metodi. + // NOTE: Returning this for chaining return this; } save() { console.log(this.make, this.model, this.color); - // NOTA: restituisci this per poter concatenare altri metodi. + // NOTE: Returning this for chaining return this; } } @@ -1094,18 +1176,27 @@ const car = new Car('Ford','F-150','red') .setColor('pink') .save(); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** + +### Prefer composition over inheritance +As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, +you should prefer composition over inheritance where you can. There are lots of +good reasons to use inheritance and lots of good reasons to use composition. +The main point for this maxim is that if your mind instinctively goes for +inheritance, try to think if composition could model your problem better. In some +cases it can. -### Preferisci una struttura compositiva all'ereditarietà -Come dichiarato dalla Gang of four in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) dovresti preferire la strutturazione all'ereditarietà quando puoi. Ci sono validi motivi per utilizzare l'ereditarietà e altrettanto validi motivi per utilizzare la strutturazione. -Il punto principale di questo assunto è che mentalmente sei portato a preferire l'ereditarietà. Prova a pensare alla strutturazione per risolvere il tuo problema: tante volte è davvero la soluzione migliore. -Ti potresti chiedere: "Quando dovrei utilizzare l'ereditarietà?". Dipende dal problema che devi affrontare, ma c'è una discreta lista di suggerimenti che ti potrebbero aiutare a capire quando l'ereditarietà è meglio della strutturazione: +You might be wondering then, "when should I use inheritance?" It +depends on your problem at hand, but this is a decent list of when inheritance +makes more sense than composition: -1. L'estensione che stai mettendo in atto rappresenta una relazione di tipo "è-un" e non "ha-un" (Umano->Animale vs. Utente->DettagliUtente). -2. Puoi riutilizzare il codice dalla classe padre -3. Vuoi fare cambiamenti globali a tutte le classi estese tramite la classe di partenza. +1. Your inheritance represents an "is-a" relationship and not a "has-a" +relationship (Human->Animal vs. User->UserDetails). +2. You can reuse code from the base classes (Humans can move like all animals). +3. You want to make global changes to derived classes by changing a base class. +(Change the caloric expenditure of all animals when they move). -**Da evitare** +**Bad:** ```javascript class Employee { constructor(name, email) { @@ -1116,7 +1207,7 @@ class Employee { // ... } -// Male because Employees "have" tax data. EmployeeTaxData is not a type of Employee +// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee class EmployeeTaxData extends Employee { constructor(ssn, salary) { super(); @@ -1128,7 +1219,7 @@ class EmployeeTaxData extends Employee { } ``` -**Bene:** +**Good:** ```javascript class EmployeeTaxData { constructor(ssn, salary) { @@ -1151,16 +1242,20 @@ class Employee { // ... } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** ## **SOLID** -### Single Responsibility Principle (SRP) (Principio di singola responsabilità) -Come indicato in *Clean code*, "Non dovrebbe mai esserci più di un solo motivo per modificare una classe". La tentazione è sempre quella di fare un'unica classe con molte funzionalità, come quando vuoi portarti un unico bagaglio a bordo. -Il problema con questo approccio è che le tue classi non saranno concettualmente coese e ti potrebbero dare più di una ragione per modificarle in seguito. -Minimizzare il numero di volte in cui modificare una classe è importante. -È importante perchè quando ci sono troppe funzionalità in una classe è difficile capire che effetto avrà sulle classe che la estendono, nel caso in cui farai un cambiamento. - -**Da evitare** +### Single Responsibility Principle (SRP) +As stated in Clean Code, "There should never be more than one reason for a class +to change". It's tempting to jam-pack a class with a lot of functionality, like +when you can only take one suitcase on your flight. The issue with this is +that your class won't be conceptually cohesive and it will give it many reasons +to change. Minimizing the amount of times you need to change a class is important. +It's important because if too much functionality is in one class and you modify +a piece of it, it can be difficult to understand how that will affect other +dependent modules in your codebase. + +**Bad:** ```javascript class UserSettings { constructor(user) { @@ -1179,7 +1274,7 @@ class UserSettings { } ``` -**Bene:** +**Good:** ```javascript class UserAuth { constructor(user) { @@ -1205,13 +1300,15 @@ class UserSettings { } } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Open/Closed Principle (OCP) (Principio aperto/chiuso) -Come dichiarato da Bertrand Meyer, "Le entità di un software (Classi, moduli, funzioni, etc.) dovrebbero essere aperte all'estensione, ma chiuse alla modifica. Cosa significa esattamente? -Quello che intende è che dovresti dare ai tuoi utilizzatori la possibilità di aggiungere nuove funzionalità, non modificando quelle esistenti. +### Open/Closed Principle (OCP) +As stated by Bertrand Meyer, "software entities (classes, modules, functions, +etc.) should be open for extension, but closed for modification." What does that +mean though? This principle basically states that you should allow users to +add new functionalities without changing existing code. -**Da evitare** +**Bad:** ```javascript class AjaxAdapter extends Adapter { constructor() { @@ -1254,7 +1351,7 @@ function makeHttpCall(url) { } ``` -**Bene:** +**Good:** ```javascript class AjaxAdapter extends Adapter { constructor() { @@ -1290,16 +1387,23 @@ class HttpRequester { } } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Liskov Substitution Principle (LSP) (Principio di sostituzione di Liskov) -Questo nome sembra molto più spaventoso di quello che in realtà significa. -Formalmente la sua defnizione è "Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S (per esempio un oggetto di tipo S può sostituire un oggetto di tipo T) senza modificare alcuna della proprietà del software (correttezza, compito da svolgere, etc.)". Questa definizione suona comunque complessa. +### Liskov Substitution Principle (LSP) +This is a scary term for a very simple concept. It's formally defined as "If S +is a subtype of T, then objects of type T may be replaced with objects of type S +(i.e., objects of type S may substitute objects of type T) without altering any +of the desirable properties of that program (correctness, task performed, +etc.)." That's an even scarier definition. -Forse una spiegazione più esaustiva potrebbe essere: "Se hai una classe *figlio* che estende una classe *genitore* allora le due classi possono essere intercambiate all'interno del codice senza generare errori o risultati inattesi". -Potrebbe esssere ancora poco chiaro, ma vediamo con un esempio (Quadrato/Rettangolo): matematicamente il Quadrato è un Rettangolo, ma se il tuo modello eredita una relazione di tipo "è-un", potresti avere presto qualche problema. +The best explanation for this is if you have a parent class and a child class, +then the base class and child class can be used interchangeably without getting +incorrect results. This might still be confusing, so let's take a look at the +classic Square-Rectangle example. Mathematically, a square is a rectangle, but +if you model it using the "is-a" relationship via inheritance, you quickly +get into trouble. -**Da evitare** +**Bad:** ```javascript class Rectangle { constructor() { @@ -1344,8 +1448,7 @@ function renderLargeRectangles(rectangles) { rectangles.forEach((rectangle) => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // Sbagliato: Restituisce 25 anche - // per il quadrato. Dovrebbe essere 20. + const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. rectangle.render(area); }); } @@ -1354,7 +1457,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**Bene:** +**Good:** ```javascript class Shape { setColor(color) { @@ -1399,19 +1502,24 @@ function renderLargeShapes(shapes) { const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ torna su](#lista-dei-contenuti)** - -### Interface Segregation Principle (ISP) (Principio di segregazione delle interfacce) +**[⬆ back to top](#table-of-contents)** -JavaScript non utilizza Interfacce, quindi non è possibile applicare questo principio alla lettera. Tuttavia è importante nonostante la sua mancanza di tipizzazione. +### Interface Segregation Principle (ISP) +JavaScript doesn't have interfaces so this principle doesn't apply as strictly +as others. However, it's important and relevant even with JavaScript's lack of +type system. -ISP indica che "Gli utenti non dovrebbero mai esssere forzati a dipendere da interfacce che non utilizza.". Le interfacce sono contratti impliciti in JavaScript per via del [*duck-typing*](https://it.wikipedia.org/wiki/Duck_typing) -Un buon esempio in JavaScript potrebbe essere fatto per le classi che richiedono la deinizione di un set di proprietà molto grande. -Non utilizzare classi che richiedono la definizione di molte proprietà per essere istanziate è sicuramente un beneficio perchè spesso non tutte queste proprietà richiedono di essere impostate per utilizzare la classe. -Rendere questi parametri opzionali evita di avere un'interfaccia pesante. +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". -**Da evitare** +**Bad:** ```javascript class DOMTraverser { constructor(settings) { @@ -1431,13 +1539,13 @@ class DOMTraverser { const $ = new DOMTraverser({ rootNode: document.getElementsByTagName('body'), - animationModule() {} //Il più delle volte potremmo non dover animare questo oggetto + animationModule() {} // Most of the time, we won't need to animate when traversing. // ... }); ``` -**Bene:** +**Good:** ```javascript class DOMTraverser { constructor(settings) { @@ -1469,20 +1577,30 @@ const $ = new DOMTraverser({ } }); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Dependency Inversion Principle (DIP) (Principio di inversione delle dipendenze) -Questo principio sostanzialmente indica due cose: -1. Moduli ad alto livello non dovrebbero dipendere da molidi a basso livello. Entrambi dovrebbero dipendere da moduli astratti. -2. L'astrazione non dovrebbe dipendere da classi concrete. Le classi concrete dovrebbero dipendere da astrazioni. +### Dependency Inversion Principle (DIP) +This principle states two essential things: +1. High-level modules should not depend on low-level modules. Both should +depend on abstractions. +2. Abstractions should not depend upon details. Details should depend on +abstractions. -A primo impatto questo concetto potrebbe essere difficile da capire, ma nel caso in cui tu abbia lavorato con AngularJS avrai sicuramente visto l'applicazione di questo principio nel concetto di *Dependency injection (DI)*. Nonostante non sia esattamente identico come concetto, DIP evita che i moduli di alto livello conoscano i dettagli dei moduli di basso livello pur utilizzandoli. -Uno dei benefici di questo utilizzo è che riduce la dipendenza tra due moduli. -La dipendenza tra due moduli è un concetto negativo, perchè ne rende difficile il refactor. -Come detto in precedenza, non essendoci il concetto di interfaccia in JavaScript, tutte le dipendenze sono contratte implicitamente. -Nell'esempio successivo, la dipendenza implicita è che tutte le istanze di `InventoryTracker` avranno un metodo `requestItems`. +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. -**Da evitare** +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. + +**Bad:** ```javascript class InventoryRequester { constructor() { @@ -1498,9 +1616,8 @@ class InventoryTracker { constructor(items) { this.items = items; - //Da evitare: abbiamo creato una dipendenza specifica per ogni istanza. - //Dovremmo fare in modo che requestItems dipenda dal metodo `request` - + // 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(); } @@ -1515,7 +1632,7 @@ const inventoryTracker = new InventoryTracker(['apples', 'bananas']); inventoryTracker.requestItems(); ``` -**Bene:** +**Good:** ```javascript class InventoryTracker { constructor(items, requester) { @@ -1550,26 +1667,32 @@ class InventoryRequesterV2 { } } -//Avendo dichiarato la nostra dipendenza esternamente ed aggiunta dall'esterno, la possiamo -//sostituire facilemente con un'altra che utilizza WebSockets - +// 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(); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -## **Test** -Testare è più importante che rilasciare. Se non hai test o non ne hai un numero adeguato, non saprai se ad ogni rilascio puoi rompere qualcosa. -Decidere quale sia il numero sufficiente di test dipende dal tuo team, ma cercare di coprire il 100% dei casi (per tutti gli stati ed i branch) vuol dire avere massima tranquillità durante i rilasci. -Questo significa che oltre ad utilizzare una suite di test valida, dovresti utilizzare anche un [buon strumento di copertura](http://gotwarlost.github.io/istanbul/). +## **Testing** +Testing is more important than shipping. If you have no tests or an +inadequate amount, then every time you ship code you won't be sure that you +didn't break anything. Deciding on what constitutes an adequate amount is up +to your team, but having 100% coverage (all statements and branches) is how +you achieve very high confidence and developer peace of mind. This means that +in addition to having a great testing framework, you also need to use a +[good coverage tool](http://gotwarlost.github.io/istanbul/). -Non ci sono scuse per non scrivere test. C'è un'abbondanza di ottimi [framewerk per i test in JavaScript](http://jstherightway.org/#Test-tools) quindi cerca quello più adatto alle tue esigenze. -Quando tu ed il tuo team avrete individuato quello più giusto per voi, dovrete iniziare sempre a scrivere i test per ogni modulo/feature che introdurrete nel vostro software. -Se il vostro approccio preferito è quello del TestDrivenDevelopment (TDD) ottimo, ma assicurati di individuare i tuoi obiettivi prima di rilasciare ogni singola feature o eseguire il refactor di una esistente. +There's no excuse to not write tests. There are [plenty of good JS test frameworks](http://jstherightway.org/#testing-tools), 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. -### Un singolo comportamento per test +### Single concept per test -**Da evitare** +**Bad:** ```javascript import assert from 'assert'; @@ -1592,7 +1715,7 @@ describe('MakeMomentJSGreatAgain', () => { }); ``` -**Bene:** +**Good:** ```javascript import assert from 'assert'; @@ -1616,13 +1739,14 @@ describe('MakeMomentJSGreatAgain', () => { }); }); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -## **Consequenzialità** -### utilizza le Promise, non funzioni di callback -Le funzioni di callback non sono sempre chiare e possono generare un eccessivo numero di nidificazioni. Con ES2015/ES6 sono nativamente e globalmente accessibili. Utilizzale! +## **Concurrency** +### Use Promises, not callbacks +Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, +Promises are a built-in global type. Use them! -**Da evitare** +**Bad:** ```javascript import { get } from 'request'; import { writeFile } from 'fs'; @@ -1643,7 +1767,7 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) ``` -**Bene:** +**Good:** ```javascript import { get } from 'request'; import { writeFile } from 'fs'; @@ -1660,14 +1784,16 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') }); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Async/Await sono addirittura più chiari delle Promise -Le Promise sono una valida e chiara alternativa alle funzioni di callback, ma ES2017/ES8 offrono anche async and await che possono essere addirittura una soluzione più migliore. -Tutto quello che devi fare non è niente altro che scrivere una funzione che abbia prefisso `async` e puoi scrivere la tua logica senza dover concatenare con la kyword `then`. -Utilizza questo approccio se hai la possibilità di utilizzare le feature ES2017/ES8! +### Async/Await are even cleaner than Promises +Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await +which offer an even cleaner solution. All you need is a function that is prefixed +in an `async` keyword, and then you can write your logic imperatively without +a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features +today! -**Da evitare** +**Bad:** ```javascript import { get } from 'request-promise'; import { writeFile } from 'fs-promise'; @@ -1685,7 +1811,7 @@ get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') ``` -**Bene:** +**Good:** ```javascript import { get } from 'request-promise'; import { writeFile } from 'fs-promise'; @@ -1700,17 +1826,24 @@ async function getCleanCodeArticle() { } } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -## **Gestione degli errori** -Generare errori è una buona cosa. Vuol dire che l'esecuzione del tuo codice ha identificato precisamente quando nel tuo software qualcosa è andato storto e ti permette di interromperne l'esecuzione nello stack corrente terminando il processo (in Node), e notificandolo attraverso la console. +## **Error Handling** +Thrown errors are a good thing! They mean the runtime has successfully +identified when something in your program has gone wrong and it's letting +you know by stopping function execution on the current stack, killing the +process (in Node), and notifying you in the console with a stack trace. -### Non ingnorare gli errori intercettati -Non fare niente con gli errori intercettati non rende possibile correggerli o reagire all'errore. Loggare gli errori nella console (`console.log`) non ti assicura di non perderti nel mare di log stampati in console. -Se invece inserisci il tuo codice all'interno del costrutto `try/catch` vuol dire riconoscere la possibilità che esista un errore nel caso mettere in piedi un modo per gestirlo. +### Don't ignore caught errors +Doing nothing with a caught error doesn't give you the ability to ever fix +or react to said error. Logging the error to the console (`console.log`) +isn't much better as often times it can get lost in a sea of things printed +to the console. If you wrap any bit of code in a `try/catch` it means you +think an error may occur there and therefore you should have a plan, +or create a code path, for when it occurs. -**Da evitare** +**Bad:** ```javascript try { functionThatMightThrow(); @@ -1719,25 +1852,26 @@ try { } ``` -**Bene:** +**Good:** ```javascript try { functionThatMightThrow(); } catch (error) { - // Un'ozione (più visibile del console.log): + // One option (more noisy than console.log): console.error(error); - // Un'altra opzione: + // Another option: notifyUserOfError(error); - // Un'altra opzione: + // Another option: reportErrorToService(error); - // Oppure usale tutte e tre! + // OR do all three! } ``` -### Non ignorare le Promise quando vengono rigettate -Per la stessa ragione per cui non dovresti ignorare gli errori con `try/catch`. +### Don't ignore rejected promises +For the same reason you shouldn't ignore caught errors +from `try/catch`. -**Da evitare** +**Bad:** ```javascript getdata() .then((data) => { @@ -1748,37 +1882,42 @@ getdata() }); ``` -**Bene:** +**Good:** ```javascript getdata() .then((data) => { functionThatMightThrow(data); }) .catch((error) => { - // Un'ozione (più visibile del console.log): + // One option (more noisy than console.log): console.error(error); - // Un'altra opzione: + // Another option: notifyUserOfError(error); - // Un'altra opzione: + // Another option: reportErrorToService(error); - // Oppure usale tutte e tre! + // OR do all three! }); ``` -**[⬆ torna su](#lista-dei-contenuti)** - +**[⬆ back to top](#table-of-contents)** -## **Formattazione** -La formattazione è soggettiva. Come molte di quelle sopracitate, non esiste una regola assoluta e veloce che devi seguire. Il punto principale, però, è NON DISCUTERE della formattazione. -Ci sono un [sacco di strumenti](http://standardjs.com/rules.html) che automatizzano questo processo. Usane uno. È uno spreco di tempo e denaro per gli sviluppatori discutere della formattazione. +## **Formatting** +Formatting is subjective. Like many rules herein, there is no hard and fast +rule that you must follow. The main point is DO NOT ARGUE over formatting. +There are [tons of tools](http://standardjs.com/rules.html) to automate this. +Use one! It's a waste of time and money for engineers to argue over formatting. -### Utilizza le maiuscole in modo consistente +For things that don't fall under the purview of automatic formatting +(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here +for some guidance. -JavaScript non è tipizzato, per questo l'uso delle maiuscole può darti indicazioni sulle tue variabili, funzioni, etc. Queste regole sono soggettive, per questo tu ed il tuo team potrete scegliere quella che volete. Il punto è: non importa quale regola sceglierete, l'importante è essere consistenti. +### Use consistent capitalization +JavaScript is untyped, so capitalization tells you a lot about your variables, +functions, etc. These rules are subjective, so your team can choose whatever +they want. The point is, no matter what you all choose, just be consistent. - -**Da evitare** +**Bad:** ```javascript const DAYS_IN_WEEK = 7; const daysInMonth = 30; @@ -1793,7 +1932,7 @@ class animal {} class Alpaca {} ``` -**Bene:** +**Good:** ```javascript const DAYS_IN_WEEK = 7; const DAYS_IN_MONTH = 30; @@ -1807,14 +1946,15 @@ function restoreDatabase() {} class Animal {} class Alpaca {} ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Richiami e dichiarazioni di funzioni dovrebbero essere vicini -Se una funzione ne richiama un'altra, mantieni queste funzioni verticalmente vicine nel sorgente. Idealmente, mantieni il richiamo subito sopra la dichiarazione. -Generalmente tendiamo a leggere il codice dall'alto verso il basso, come un giornale. Proprio per questo manteniamolo leggibile seguendo questa modalità. +### Function callers and callees should be close +If a function calls another, keep those functions vertically close in the source +file. Ideally, keep the caller right above the callee. We tend to read code from +top-to-bottom, like a newspaper. Because of this, make your code read that way. -**Da evitare** +**Bad:** ```javascript class PerformanceReview { constructor(employee) { @@ -1853,7 +1993,7 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**Bene:** +**Good:** ```javascript class PerformanceReview { constructor(employee) { @@ -1892,12 +2032,13 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** + +## **Comments** +### Only comment things that have business logic complexity. +Comments are an apology, not a requirement. Good code *mostly* documents itself. -## **Commento** -### Commenta solo il codice che ha un alto livello di complessità -Commentare è una scusa, non un requisito. Un buon codice *spesso* si spiega da solo. -**Da evitare** +**Bad:** ```javascript function hashIt(data) { // The hash @@ -1918,7 +2059,7 @@ function hashIt(data) { } ``` -**Bene:** +**Good:** ```javascript function hashIt(data) { @@ -1935,12 +2076,12 @@ function hashIt(data) { } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Non lasciare parti del tuo codice commentate all'interno dei sorgenti -I sistemi di versioning esistono per un motivo. Lascia il tuo codice vecchio alla storia. +### Don't leave commented out code in your codebase +Version control exists for a reason. Leave old code in your history. -**Da evitare** +**Bad:** ```javascript doStuff(); // doOtherStuff(); @@ -1948,17 +2089,17 @@ doStuff(); // doSoMuchStuff(); ``` -**Bene:** +**Good:** ```javascript doStuff(); ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Non utilizzare i commenti come un diario -Ricordati di usare sistemi di version control. Non c'è motivo per cui codice non utilizzato, codice commentato e specialmente commenti con riferimenti a date esistano nel tuo file. -Usa `git log` per avere lo storico! +### Don't have journal comments +Remember, use version control! There's no need for dead code, commented code, +and especially journal comments. Use `git log` to get history! -**Da evitare** +**Bad:** ```javascript /** * 2016-12-20: Removed monads, didn't understand them (RM) @@ -1971,18 +2112,19 @@ function combine(a, b) { } ``` -**Bene:** +**Good:** ```javascript function combine(a, b) { return a + b; } ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -### Evita di specificare di cosa si tratta -Generalmente è solo fastidioso. Lascia che le tue funzioni e le tue variabili, insieme ad una corretta indentazioni ti diano una struttura visiva del tuo codice. +### Avoid positional markers +They usually just add noise. Let the functions and variable names along with the +proper indentation and formatting give the visual structure to your code. -**Da evitare** +**Bad:** ```javascript //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation @@ -2000,7 +2142,7 @@ const actions = function() { }; ``` -**Bene:** +**Good:** ```javascript $scope.model = { menu: 'foo', @@ -2011,11 +2153,11 @@ const actions = function() { // ... }; ``` -**[⬆ torna su](#lista-dei-contenuti)** +**[⬆ back to top](#table-of-contents)** -## Traduzioni +## Translation -Questa guida è disponibile in altre lingue: +This is also available in other languages: - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) @@ -2032,7 +2174,7 @@ Questa guida è disponibile in altre lingue: - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italiano**: + - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) -**[⬆ torna su](#lista-dei-contenuti)** \ No newline at end of file +**[⬆ back to top](#table-of-contents)** From cb0f7d551bb524b8d7fa2b495bfaf26beeaf9533 Mon Sep 17 00:00:00 2001 From: Alex Schmitt <39417513+alex-schmitt@users.noreply.github.com> Date: Mon, 21 Jan 2019 11:25:38 -0600 Subject: [PATCH 134/170] remove map method in favor functional programming --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c1f9058..f92ce5ec 100644 --- a/README.md +++ b/README.md @@ -706,8 +706,7 @@ const programmerOutput = [ ]; const totalOutput = programmerOutput - .map(output => output.linesOfCode) - .reduce((totalLines, lines) => totalLines + lines); + .reduce((totalLines, output) => totalLines + output.linesOfCode, 0) ``` **[⬆ back to top](#table-of-contents)** From ce1d3833ba3da6b09006c786f5f55f2bb2564006 Mon Sep 17 00:00:00 2001 From: Victor Ribero Date: Sat, 2 Feb 2019 11:04:13 +0100 Subject: [PATCH 135/170] Added link to the new spanish translation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 16211cee..8d0b20b8 100644 --- a/README.md +++ b/README.md @@ -2160,6 +2160,7 @@ This is also available in other languages: - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) + - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) @@ -2176,4 +2177,4 @@ This is also available in other languages: - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** \ No newline at end of file From b1c3a6ec1a71cce9c40fe8f6bc7c58d66d1589f4 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 10 Feb 2019 17:11:05 -0800 Subject: [PATCH 136/170] Format using Prettier's Markdown formatter --- README.md | 751 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 471 insertions(+), 280 deletions(-) diff --git a/README.md b/README.md index 8d0b20b8..4f4628ec 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,34 @@ # clean-code-javascript ## Table of Contents - 1. [Introduction](#introduction) - 2. [Variables](#variables) - 3. [Functions](#functions) - 4. [Objects and Data Structures](#objects-and-data-structures) - 5. [Classes](#classes) - 6. [SOLID](#solid) - 7. [Testing](#testing) - 8. [Concurrency](#concurrency) - 9. [Error Handling](#error-handling) - 10. [Formatting](#formatting) - 11. [Comments](#comments) - 12. [Translation](#translation) + +1. [Introduction](#introduction) +2. [Variables](#variables) +3. [Functions](#functions) +4. [Objects and Data Structures](#objects-and-data-structures) +5. [Classes](#classes) +6. [SOLID](#solid) +7. [Testing](#testing) +8. [Concurrency](#concurrency) +9. [Error Handling](#error-handling) +10. [Formatting](#formatting) +11. [Comments](#comments) +12. [Translation](#translation) ## Introduction + ![Humorous image of software quality estimation as a count of how many expletives you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) Software engineering principles, from Robert C. Martin's book -[*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), +[_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), adapted for JavaScript. This is not a style guide. It's a guide to producing [readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of -*Clean Code*. +_Clean Code_. Our craft of software engineering is just a bit over 50 years old, and we are still learning a lot. When software architecture is as old as architecture @@ -42,22 +44,27 @@ we review it with our peers. Don't beat yourself up for first drafts that need improvement. Beat up the code instead! ## **Variables** + ### Use meaningful and pronounceable variable names **Bad:** + ```javascript -const yyyymmdstr = moment().format('YYYY/MM/DD'); +const yyyymmdstr = moment().format("YYYY/MM/DD"); ``` **Good:** + ```javascript -const currentDate = moment().format('YYYY/MM/DD'); +const currentDate = moment().format("YYYY/MM/DD"); ``` + **[⬆ back to top](#table-of-contents)** ### Use the same vocabulary for the same type of variable **Bad:** + ```javascript getUserInfo(); getClientData(); @@ -65,14 +72,17 @@ getCustomerRecord(); ``` **Good:** + ```javascript getUser(); ``` + **[⬆ back to top](#table-of-contents)** ### Use searchable names + We will read more code than we will ever write. It's important that the code we -do write is readable and searchable. By *not* naming variables that end up +do write is readable and searchable. By _not_ naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. Tools like [buddy.js](https://github.com/danielstjules/buddy.js) and @@ -80,46 +90,56 @@ Make your names searchable. Tools like can help identify unnamed constants. **Bad:** + ```javascript // What the heck is 86400000 for? setTimeout(blastOff, 86400000); - ``` **Good:** + ```javascript // Declare them as capitalized named constants. const MILLISECONDS_IN_A_DAY = 86400000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); - ``` + **[⬆ back to top](#table-of-contents)** ### Use explanatory variables + **Bad:** + ```javascript -const address = 'One Infinite Loop, Cupertino 95014'; +const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; -saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]); +saveCityZipCode( + address.match(cityZipCodeRegex)[1], + address.match(cityZipCodeRegex)[2] +); ``` **Good:** + ```javascript -const address = 'One Infinite Loop, Cupertino 95014'; +const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; const [, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` + **[⬆ back to top](#table-of-contents)** ### Avoid Mental Mapping + Explicit is better than implicit. **Bad:** + ```javascript -const locations = ['Austin', 'New York', 'San Francisco']; -locations.forEach((l) => { +const locations = ["Austin", "New York", "San Francisco"]; +locations.forEach(l => { doStuff(); doSomeOtherStuff(); // ... @@ -131,9 +151,10 @@ locations.forEach((l) => { ``` **Good:** + ```javascript -const locations = ['Austin', 'New York', 'San Francisco']; -locations.forEach((location) => { +const locations = ["Austin", "New York", "San Francisco"]; +locations.forEach(location => { doStuff(); doSomeOtherStuff(); // ... @@ -142,65 +163,74 @@ locations.forEach((location) => { dispatch(location); }); ``` + **[⬆ back to top](#table-of-contents)** ### Don't add unneeded context + If your class/object name tells you something, don't repeat that in your variable name. **Bad:** + ```javascript const Car = { - carMake: 'Honda', - carModel: 'Accord', - carColor: 'Blue' + carMake: "Honda", + carModel: "Accord", + carColor: "Blue" }; function paintCar(car) { - car.carColor = 'Red'; + car.carColor = "Red"; } ``` **Good:** + ```javascript const Car = { - make: 'Honda', - model: 'Accord', - color: 'Blue' + make: "Honda", + model: "Accord", + color: "Blue" }; function paintCar(car) { - car.color = 'Red'; + car.color = "Red"; } ``` + **[⬆ back to top](#table-of-contents)** ### Use default arguments instead of short circuiting or conditionals + Default arguments are often cleaner than short circuiting. Be aware that if you use them, your function will only provide default values for `undefined` arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and `NaN`, will not be replaced by a default value. **Bad:** + ```javascript function createMicrobrewery(name) { - const breweryName = name || 'Hipster Brew Co.'; + const breweryName = name || "Hipster Brew Co."; // ... } - ``` **Good:** + ```javascript -function createMicrobrewery(name = 'Hipster Brew Co.') { +function createMicrobrewery(name = "Hipster Brew Co.") { // ... } - ``` + **[⬆ back to top](#table-of-contents)** ## **Functions** + ### Function arguments (2 or fewer ideally) + Limiting the amount of function parameters is incredibly important because it makes testing your function easier. Having more than three leads to a combinatorial explosion where you have to test tons of different cases with @@ -220,15 +250,16 @@ To make it obvious what properties the function expects, you can use the ES2015/ destructuring syntax. This has a few advantages: 1. When someone looks at the function signature, it's immediately clear what -properties are being used. + properties are being used. 2. Destructuring also clones the specified primitive values of the argument -object passed into the function. This can help prevent side effects. Note: -objects and arrays that are destructured from the argument object are NOT -cloned. + object passed into the function. This can help prevent side effects. Note: + objects and arrays that are destructured from the argument object are NOT + cloned. 3. Linters can warn you about unused properties, which would be impossible -without destructuring. + without destructuring. **Bad:** + ```javascript function createMenu(title, body, buttonText, cancellable) { // ... @@ -236,22 +267,24 @@ function createMenu(title, body, buttonText, cancellable) { ``` **Good:** + ```javascript function createMenu({ title, body, buttonText, cancellable }) { // ... } createMenu({ - title: 'Foo', - body: 'Bar', - buttonText: 'Baz', + title: "Foo", + body: "Bar", + buttonText: "Baz", cancellable: true }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Functions should do one thing + This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. When you can isolate a function to just one action, they can be refactored @@ -259,9 +292,10 @@ easily and your code will read much cleaner. If you take nothing else away from this guide other than this, you'll be ahead of many developers. **Bad:** + ```javascript function emailClients(clients) { - clients.forEach((client) => { + clients.forEach(client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); @@ -271,11 +305,10 @@ function emailClients(clients) { ``` **Good:** + ```javascript function emailActiveClients(clients) { - clients - .filter(isActiveClient) - .forEach(email); + clients.filter(isActiveClient).forEach(email); } function isActiveClient(client) { @@ -283,11 +316,13 @@ function isActiveClient(client) { return clientRecord.isActive(); } ``` + **[⬆ back to top](#table-of-contents)** ### Function names should say what they do **Bad:** + ```javascript function addToDate(date, month) { // ... @@ -300,6 +335,7 @@ addToDate(date, 1); ``` **Good:** + ```javascript function addMonthToDate(month, date) { // ... @@ -308,45 +344,49 @@ function addMonthToDate(month, date) { const date = new Date(); addMonthToDate(1, date); ``` + **[⬆ back to top](#table-of-contents)** ### Functions should only be one level of abstraction + When you have more than one level of abstraction your function is usually doing too much. Splitting up functions leads to reusability and easier testing. **Bad:** + ```javascript function parseBetterJSAlternative(code) { const REGEXES = [ // ... ]; - const statements = code.split(' '); + const statements = code.split(" "); const tokens = []; - REGEXES.forEach((REGEX) => { - statements.forEach((statement) => { + REGEXES.forEach(REGEX => { + statements.forEach(statement => { // ... }); }); const ast = []; - tokens.forEach((token) => { + tokens.forEach(token => { // lex... }); - ast.forEach((node) => { + ast.forEach(node => { // parse... }); } ``` **Good:** + ```javascript function parseBetterJSAlternative(code) { const tokens = tokenize(code); const syntaxTree = parse(tokens); - syntaxTree.forEach((node) => { + syntaxTree.forEach(node => { // parse... }); } @@ -356,11 +396,11 @@ function tokenize(code) { // ... ]; - const statements = code.split(' '); + const statements = code.split(" "); const tokens = []; - REGEXES.forEach((REGEX) => { - statements.forEach((statement) => { - tokens.push( /* ... */ ); + REGEXES.forEach(REGEX => { + statements.forEach(statement => { + tokens.push(/* ... */); }); }); @@ -369,16 +409,18 @@ function tokenize(code) { function parse(tokens) { const syntaxTree = []; - tokens.forEach((token) => { - syntaxTree.push( /* ... */ ); + tokens.forEach(token => { + syntaxTree.push(/* ... */); }); return syntaxTree; } ``` + **[⬆ back to top](#table-of-contents)** ### Remove duplicate code + Do your absolute best to avoid duplicate code. Duplicate code is bad because it means that there's more than one place to alter something if you need to change some logic. @@ -395,15 +437,16 @@ duplicate code means creating an abstraction that can handle this set of different things with just one function/module/class. Getting the abstraction right is critical, that's why you should follow the -SOLID principles laid out in the *Classes* section. Bad abstractions can be +SOLID principles laid out in the _Classes_ section. Bad abstractions can be worse than duplicate code, so be careful! Having said this, if you can make a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself updating multiple places anytime you want to change one thing. **Bad:** + ```javascript function showDeveloperList(developers) { - developers.forEach((developer) => { + developers.forEach(developer => { const expectedSalary = developer.calculateExpectedSalary(); const experience = developer.getExperience(); const githubLink = developer.getGithubLink(); @@ -418,7 +461,7 @@ function showDeveloperList(developers) { } function showManagerList(managers) { - managers.forEach((manager) => { + managers.forEach(manager => { const expectedSalary = manager.calculateExpectedSalary(); const experience = manager.getExperience(); const portfolio = manager.getMBAProjects(); @@ -434,9 +477,10 @@ function showManagerList(managers) { ``` **Good:** + ```javascript function showEmployeeList(employees) { - employees.forEach((employee) => { + employees.forEach(employee => { const expectedSalary = employee.calculateExpectedSalary(); const experience = employee.getExperience(); @@ -446,10 +490,10 @@ function showEmployeeList(employees) { }; switch (employee.type) { - case 'manager': + case "manager": data.portfolio = employee.getMBAProjects(); break; - case 'developer': + case "developer": data.githubLink = employee.getGithubLink(); break; } @@ -458,45 +502,52 @@ function showEmployeeList(employees) { }); } ``` + **[⬆ back to top](#table-of-contents)** ### Set default objects with Object.assign **Bad:** + ```javascript const menuConfig = { title: null, - body: 'Bar', + 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; + 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); ``` **Good:** + ```javascript const menuConfig = { - title: 'Order', + title: "Order", // User did not include 'body' key - buttonText: 'Send', + buttonText: "Send", cancellable: true }; function createMenu(config) { - config = Object.assign({ - title: 'Foo', - body: 'Bar', - buttonText: 'Baz', - cancellable: true - }, config); + config = Object.assign( + { + title: "Foo", + body: "Bar", + buttonText: "Baz", + cancellable: true + }, + config + ); // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... @@ -504,13 +555,15 @@ function createMenu(config) { createMenu(menuConfig); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Don't use flags as function parameters + Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. **Bad:** + ```javascript function createFile(name, temp) { if (temp) { @@ -522,6 +575,7 @@ function createFile(name, temp) { ``` **Good:** + ```javascript function createFile(name) { fs.create(name); @@ -531,9 +585,11 @@ function createTempFile(name) { createFile(`./temp/${name}`); } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid Side Effects (part 1) + A function produces a side effect if it does anything other than take a value in and return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a @@ -550,13 +606,14 @@ and not centralizing where your side effects occur. If you can do this, you will be happier than the vast majority of other programmers. **Bad:** + ```javascript // 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 name = 'Ryan McDermott'; +let name = "Ryan McDermott"; function splitIntoFirstAndLastName() { - name = name.split(' '); + name = name.split(" "); } splitIntoFirstAndLastName(); @@ -565,20 +622,23 @@ console.log(name); // ['Ryan', 'McDermott']; ``` **Good:** + ```javascript function splitIntoFirstAndLastName(name) { - return name.split(' '); + return name.split(" "); } -const name = 'Ryan McDermott'; +const name = "Ryan McDermott"; const newName = splitIntoFirstAndLastName(name); console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` + **[⬆ back to top](#table-of-contents)** ### Avoid Side Effects (part 2) + In JavaScript, primitives are passed by value and objects/arrays are passed by reference. In the case of objects and arrays, if your function makes a change in a shopping cart array, for example, by adding an item to purchase, @@ -601,17 +661,19 @@ 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: - 1. 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! - 2. 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](https://facebook.github.io/immutable-js/) 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. +1. 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! + +2. 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](https://facebook.github.io/immutable-js/) 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. **Bad:** + ```javascript const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); @@ -619,6 +681,7 @@ const addItemToCart = (cart, item) => { ``` **Good:** + ```javascript const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; @@ -628,6 +691,7 @@ const addItemToCart = (cart, item) => { **[⬆ back to top](#table-of-contents)** ### Don't write to global functions + Polluting globals is a bad practice in JavaScript because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in production. Let's think about an example: what if you wanted to @@ -639,6 +703,7 @@ the difference between the first and last elements of an array? This is why it would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. **Bad:** + ```javascript Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); @@ -647,6 +712,7 @@ Array.prototype.diff = function diff(comparisonArray) { ``` **Good:** + ```javascript class SuperArray extends Array { diff(comparisonArray) { @@ -655,27 +721,33 @@ class SuperArray extends Array { } } ``` + **[⬆ back to top](#table-of-contents)** ### Favor functional programming over imperative programming + JavaScript isn't a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can. **Bad:** + ```javascript const programmerOutput = [ { - name: 'Uncle Bobby', + name: "Uncle Bobby", linesOfCode: 500 - }, { - name: 'Suzie Q', + }, + { + name: "Suzie Q", linesOfCode: 1500 - }, { - name: 'Jimmy Gosling', + }, + { + name: "Jimmy Gosling", linesOfCode: 150 - }, { - name: 'Gracie Hopper', + }, + { + name: "Gracie Hopper", linesOfCode: 1000 } ]; @@ -688,52 +760,63 @@ for (let i = 0; i < programmerOutput.length; i++) { ``` **Good:** + ```javascript const programmerOutput = [ { - name: 'Uncle Bobby', + name: "Uncle Bobby", linesOfCode: 500 - }, { - name: 'Suzie Q', + }, + { + name: "Suzie Q", linesOfCode: 1500 - }, { - name: 'Jimmy Gosling', + }, + { + name: "Jimmy Gosling", linesOfCode: 150 - }, { - name: 'Gracie Hopper', + }, + { + name: "Gracie Hopper", linesOfCode: 1000 } ]; -const totalOutput = programmerOutput - .reduce((totalLines, output) => totalLines + output.linesOfCode, 0) +const totalOutput = programmerOutput.reduce( + (totalLines, output) => totalLines + output.linesOfCode, + 0 +); ``` + **[⬆ back to top](#table-of-contents)** ### Encapsulate conditionals **Bad:** + ```javascript -if (fsm.state === 'fetching' && isEmpty(listNode)) { +if (fsm.state === "fetching" && isEmpty(listNode)) { // ... } ``` **Good:** + ```javascript function shouldShowSpinner(fsm, listNode) { - return fsm.state === 'fetching' && isEmpty(listNode); + return fsm.state === "fetching" && isEmpty(listNode); } if (shouldShowSpinner(fsmInstance, listNodeInstance)) { // ... } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid negative conditionals **Bad:** + ```javascript function isDOMNodeNotPresent(node) { // ... @@ -745,6 +828,7 @@ if (!isDOMNodeNotPresent(node)) { ``` **Good:** + ```javascript function isDOMNodePresent(node) { // ... @@ -754,9 +838,11 @@ if (isDOMNodePresent(node)) { // ... } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid conditionals + This seems like an impossible task. Upon first hearing this, most people say, "how am I supposed to do anything without an `if` statement?" The answer is that you can use polymorphism to achieve the same task in many cases. The second @@ -767,16 +853,17 @@ are telling your user that your function does more than one thing. Remember, just do one thing. **Bad:** + ```javascript class Airplane { // ... getCruisingAltitude() { switch (this.type) { - case '777': + case "777": return this.getMaxAltitude() - this.getPassengerCount(); - case 'Air Force One': + case "Air Force One": return this.getMaxAltitude(); - case 'Cessna': + case "Cessna": return this.getMaxAltitude() - this.getFuelExpenditure(); } } @@ -784,6 +871,7 @@ class Airplane { ``` **Good:** + ```javascript class Airplane { // ... @@ -810,34 +898,40 @@ class Cessna extends Airplane { } } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 1) + JavaScript is untyped, which means your functions can take any type of argument. Sometimes you are bitten by this freedom and it becomes tempting to do type-checking in your functions. There are many ways to avoid having to do this. The first thing to consider is consistent APIs. **Bad:** + ```javascript function travelToTexas(vehicle) { if (vehicle instanceof Bicycle) { - vehicle.pedal(this.currentLocation, new Location('texas')); + vehicle.pedal(this.currentLocation, new Location("texas")); } else if (vehicle instanceof Car) { - vehicle.drive(this.currentLocation, new Location('texas')); + vehicle.drive(this.currentLocation, new Location("texas")); } } ``` **Good:** + ```javascript function travelToTexas(vehicle) { - vehicle.move(this.currentLocation, new Location('texas')); + vehicle.move(this.currentLocation, new Location("texas")); } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid type-checking (part 2) + If you are working with basic primitive values like strings and integers, and you can't use polymorphism but you still feel the need to type-check, you should consider using TypeScript. It is an excellent alternative to normal @@ -849,26 +943,32 @@ good tests, and have good code reviews. Otherwise, do all of that but with TypeScript (which, like I said, is a great alternative!). **Bad:** + ```javascript function combine(val1, val2) { - if (typeof val1 === 'number' && typeof val2 === 'number' || - typeof val1 === 'string' && typeof val2 === 'string') { + 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'); + throw new Error("Must be of type String or Number"); } ``` **Good:** + ```javascript function combine(val1, val2) { return val1 + val2; } ``` + **[⬆ back to top](#table-of-contents)** ### Don't over-optimize + Modern browsers do a lot of optimization under-the-hood at runtime. A lot of times, if you are optimizing then you are just wasting your time. [There are good resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) @@ -876,8 +976,8 @@ for seeing where optimization is lacking. Target those in the meantime, until they are fixed if they can be. **Bad:** -```javascript +```javascript // On old browsers, each iteration with uncached `list.length` would be costly // because of `list.length` recomputation. In modern browsers, this is optimized. for (let i = 0, len = list.length; i < len; i++) { @@ -886,19 +986,23 @@ for (let i = 0, len = list.length; i < len; i++) { ``` **Good:** + ```javascript for (let i = 0; i < list.length; i++) { // ... } ``` + **[⬆ back to top](#table-of-contents)** ### Remove dead code + Dead code is just as bad as duplicate code. There's no reason to keep it in your codebase. If it's not being called, get rid of it! It will still be safe in your version history if you still need it. **Bad:** + ```javascript function oldRequestModule(url) { // ... @@ -909,43 +1013,46 @@ function newRequestModule(url) { } const req = newRequestModule; -inventoryTracker('apples', req, 'www.inventory-awesome.io'); - +inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` **Good:** + ```javascript function newRequestModule(url) { // ... } const req = newRequestModule; -inventoryTracker('apples', req, 'www.inventory-awesome.io'); +inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` + **[⬆ back to top](#table-of-contents)** ## **Objects and Data Structures** + ### Use getters and setters + Using getters and setters to access data on objects could be better than simply looking for a property on an object. "Why?" you might ask. Well, here's an unorganized list of reasons why: -* When you want to do more beyond getting an object property, you don't have -to look up and change every accessor in your codebase. -* Makes adding validation simple when doing a `set`. -* Encapsulates the internal representation. -* Easy to add logging and error handling when getting and setting. -* You can lazy load your object's properties, let's say getting it from a -server. - +- When you want to do more beyond getting an object property, you don't have + to look up and change every accessor in your codebase. +- Makes adding validation simple when doing a `set`. +- Encapsulates the internal representation. +- Easy to add logging and error handling when getting and setting. +- You can lazy load your object's properties, let's say getting it from a + server. **Bad:** + ```javascript function makeBankAccount() { // ... return { - balance: 0, + balance: 0 // ... }; } @@ -955,6 +1062,7 @@ account.balance = 100; ``` **Good:** + ```javascript function makeBankAccount() { // this one is private @@ -974,22 +1082,23 @@ function makeBankAccount() { return { // ... getBalance, - setBalance, + setBalance }; } const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Make objects have private members + This can be accomplished through closures (for ES5 and below). **Bad:** -```javascript +```javascript const Employee = function(name) { this.name = name; }; @@ -998,42 +1107,46 @@ Employee.prototype.getName = function getName() { return this.name; }; -const employee = new Employee('John Doe'); +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 ``` **Good:** + ```javascript function makeEmployee(name) { return { getName() { return name; - }, + } }; } -const employee = makeEmployee('John Doe'); +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 ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ## **Classes** + ### Prefer ES2015/ES6 classes over ES5 plain functions + It's very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over classes until you find yourself needing larger and more complex objects. **Bad:** + ```javascript const Animal = function(age) { if (!(this instanceof Animal)) { - throw new Error('Instantiate Animal with `new`'); + throw new Error("Instantiate Animal with `new`"); } this.age = age; @@ -1043,7 +1156,7 @@ Animal.prototype.move = function move() {}; const Mammal = function(age, furColor) { if (!(this instanceof Mammal)) { - throw new Error('Instantiate Mammal with `new`'); + throw new Error("Instantiate Mammal with `new`"); } Animal.call(this, age); @@ -1056,7 +1169,7 @@ Mammal.prototype.liveBirth = function liveBirth() {}; const Human = function(age, furColor, languageSpoken) { if (!(this instanceof Human)) { - throw new Error('Instantiate Human with `new`'); + throw new Error("Instantiate Human with `new`"); } Mammal.call(this, age, furColor); @@ -1069,13 +1182,16 @@ Human.prototype.speak = function speak() {}; ``` **Good:** + ```javascript class Animal { constructor(age) { this.age = age; } - move() { /* ... */ } + move() { + /* ... */ + } } class Mammal extends Animal { @@ -1084,7 +1200,9 @@ class Mammal extends Animal { this.furColor = furColor; } - liveBirth() { /* ... */ } + liveBirth() { + /* ... */ + } } class Human extends Mammal { @@ -1093,13 +1211,16 @@ class Human extends Mammal { this.languageSpoken = languageSpoken; } - speak() { /* ... */ } + speak() { + /* ... */ + } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Use method chaining + This pattern is very useful in JavaScript and you see it in many libraries such as jQuery and Lodash. It allows your code to be expressive, and less verbose. For that reason, I say, use method chaining and take a look at how clean your code @@ -1107,6 +1228,7 @@ will be. In your class functions, simply return `this` at the end of every funct and you can chain further class methods onto it. **Bad:** + ```javascript class Car { constructor(make, model, color) { @@ -1132,12 +1254,13 @@ class Car { } } -const car = new Car('Ford','F-150','red'); -car.setColor('pink'); +const car = new Car("Ford", "F-150", "red"); +car.setColor("pink"); car.save(); ``` **Good:** + ```javascript class Car { constructor(make, model, color) { @@ -1171,14 +1294,14 @@ class Car { } } -const car = new Car('Ford','F-150','red') - .setColor('pink') - .save(); +const car = new Car("Ford", "F-150", "red").setColor("pink").save(); ``` + **[⬆ back to top](#table-of-contents)** ### Prefer composition over inheritance -As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, + +As stated famously in [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, you should prefer composition over inheritance where you can. There are lots of good reasons to use inheritance and lots of good reasons to use composition. The main point for this maxim is that if your mind instinctively goes for @@ -1190,12 +1313,13 @@ depends on your problem at hand, but this is a decent list of when inheritance makes more sense than composition: 1. Your inheritance represents an "is-a" relationship and not a "has-a" -relationship (Human->Animal vs. User->UserDetails). + relationship (Human->Animal vs. User->UserDetails). 2. You can reuse code from the base classes (Humans can move like all animals). 3. You want to make global changes to derived classes by changing a base class. -(Change the caloric expenditure of all animals when they move). + (Change the caloric expenditure of all animals when they move). **Bad:** + ```javascript class Employee { constructor(name, email) { @@ -1219,6 +1343,7 @@ class EmployeeTaxData extends Employee { ``` **Good:** + ```javascript class EmployeeTaxData { constructor(ssn, salary) { @@ -1241,10 +1366,13 @@ class Employee { // ... } ``` + **[⬆ back to top](#table-of-contents)** ## **SOLID** + ### Single Responsibility Principle (SRP) + As stated in Clean Code, "There should never be more than one reason for a class to change". It's tempting to jam-pack a class with a lot of functionality, like when you can only take one suitcase on your flight. The issue with this is @@ -1255,6 +1383,7 @@ a piece of it, it can be difficult to understand how that will affect other dependent modules in your codebase. **Bad:** + ```javascript class UserSettings { constructor(user) { @@ -1274,6 +1403,7 @@ class UserSettings { ``` **Good:** + ```javascript class UserAuth { constructor(user) { @@ -1285,7 +1415,6 @@ class UserAuth { } } - class UserSettings { constructor(user) { this.user = user; @@ -1299,27 +1428,30 @@ class UserSettings { } } ``` + **[⬆ back to top](#table-of-contents)** ### Open/Closed Principle (OCP) + As stated by Bertrand Meyer, "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification." What does that mean though? This principle basically states that you should allow users to add new functionalities without changing existing code. **Bad:** + ```javascript class AjaxAdapter extends Adapter { constructor() { super(); - this.name = 'ajaxAdapter'; + this.name = "ajaxAdapter"; } } class NodeAdapter extends Adapter { constructor() { super(); - this.name = 'nodeAdapter'; + this.name = "nodeAdapter"; } } @@ -1329,12 +1461,12 @@ class HttpRequester { } fetch(url) { - if (this.adapter.name === 'ajaxAdapter') { - return makeAjaxCall(url).then((response) => { + 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) => { + } else if (this.adapter.name === "nodeAdapter") { + return makeHttpCall(url).then(response => { // transform response and return }); } @@ -1351,11 +1483,12 @@ function makeHttpCall(url) { ``` **Good:** + ```javascript class AjaxAdapter extends Adapter { constructor() { super(); - this.name = 'ajaxAdapter'; + this.name = "ajaxAdapter"; } request(url) { @@ -1366,7 +1499,7 @@ class AjaxAdapter extends Adapter { class NodeAdapter extends Adapter { constructor() { super(); - this.name = 'nodeAdapter'; + this.name = "nodeAdapter"; } request(url) { @@ -1380,15 +1513,17 @@ class HttpRequester { } fetch(url) { - return this.adapter.request(url).then((response) => { + return this.adapter.request(url).then(response => { // transform response and return }); } } ``` + **[⬆ back to top](#table-of-contents)** ### Liskov Substitution Principle (LSP) + This is a scary term for a very simple concept. It's formally defined as "If S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any @@ -1403,6 +1538,7 @@ if you model it using the "is-a" relationship via inheritance, you quickly get into trouble. **Bad:** + ```javascript class Rectangle { constructor() { @@ -1444,7 +1580,7 @@ class Square extends Rectangle { } function renderLargeRectangles(rectangles) { - rectangles.forEach((rectangle) => { + rectangles.forEach(rectangle => { rectangle.setWidth(4); rectangle.setHeight(5); const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. @@ -1457,6 +1593,7 @@ renderLargeRectangles(rectangles); ``` **Good:** + ```javascript class Shape { setColor(color) { @@ -1492,7 +1629,7 @@ class Square extends Shape { } function renderLargeShapes(shapes) { - shapes.forEach((shape) => { + shapes.forEach(shape => { const area = shape.getArea(); shape.render(area); }); @@ -1501,9 +1638,11 @@ function renderLargeShapes(shapes) { const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` + **[⬆ back to top](#table-of-contents)** ### Interface Segregation Principle (ISP) + JavaScript doesn't have interfaces so this principle doesn't apply as strictly as others. However, it's important and relevant even with JavaScript's lack of type system. @@ -1519,6 +1658,7 @@ all of the settings. Making them optional helps prevent having a "fat interface". **Bad:** + ```javascript class DOMTraverser { constructor(settings) { @@ -1537,14 +1677,14 @@ class DOMTraverser { } const $ = new DOMTraverser({ - rootNode: document.getElementsByTagName('body'), + rootNode: document.getElementsByTagName("body"), animationModule() {} // Most of the time, we won't need to animate when traversing. // ... }); - ``` **Good:** + ```javascript class DOMTraverser { constructor(settings) { @@ -1570,20 +1710,23 @@ class DOMTraverser { } const $ = new DOMTraverser({ - rootNode: document.getElementsByTagName('body'), + rootNode: document.getElementsByTagName("body"), options: { animationModule() {} } }); ``` + **[⬆ back to top](#table-of-contents)** ### Dependency Inversion Principle (DIP) + This principle states two essential things: + 1. High-level modules should not depend on low-level modules. Both should -depend on abstractions. + depend on abstractions. 2. Abstractions should not depend upon details. Details should depend on -abstractions. + 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 @@ -1600,10 +1743,11 @@ example below, the implicit contract is that any Request module for an `InventoryTracker` will have a `requestItems` method. **Bad:** + ```javascript class InventoryRequester { constructor() { - this.REQ_METHODS = ['HTTP']; + this.REQ_METHODS = ["HTTP"]; } requestItem(item) { @@ -1621,17 +1765,18 @@ class InventoryTracker { } requestItems() { - this.items.forEach((item) => { + this.items.forEach(item => { this.requester.requestItem(item); }); } } -const inventoryTracker = new InventoryTracker(['apples', 'bananas']); +const inventoryTracker = new InventoryTracker(["apples", "bananas"]); inventoryTracker.requestItems(); ``` **Good:** + ```javascript class InventoryTracker { constructor(items, requester) { @@ -1640,7 +1785,7 @@ class InventoryTracker { } requestItems() { - this.items.forEach((item) => { + this.items.forEach(item => { this.requester.requestItem(item); }); } @@ -1648,7 +1793,7 @@ class InventoryTracker { class InventoryRequesterV1 { constructor() { - this.REQ_METHODS = ['HTTP']; + this.REQ_METHODS = ["HTTP"]; } requestItem(item) { @@ -1658,7 +1803,7 @@ class InventoryRequesterV1 { class InventoryRequesterV2 { constructor() { - this.REQ_METHODS = ['WS']; + this.REQ_METHODS = ["WS"]; } requestItem(item) { @@ -1668,12 +1813,17 @@ class InventoryRequesterV2 { // 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()); +const inventoryTracker = new InventoryTracker( + ["apples", "bananas"], + new InventoryRequesterV2() +); inventoryTracker.requestItems(); ``` + **[⬆ back to top](#table-of-contents)** ## **Testing** + Testing is more important than shipping. If you have no tests or an inadequate amount, then every time you ship code you won't be sure that you didn't break anything. Deciding on what constitutes an adequate amount is up @@ -1692,100 +1842,110 @@ or refactoring an existing one. ### Single concept per test **Bad:** + ```javascript -import assert from 'assert'; +import assert from "assert"; -describe('MakeMomentJSGreatAgain', () => { - it('handles date boundaries', () => { +describe("MakeMomentJSGreatAgain", () => { + it("handles date boundaries", () => { let date; - date = new MakeMomentJSGreatAgain('1/1/2015'); + date = new MakeMomentJSGreatAgain("1/1/2015"); date.addDays(30); - assert.equal('1/31/2015', date); + assert.equal("1/31/2015", date); - date = new MakeMomentJSGreatAgain('2/1/2016'); + date = new MakeMomentJSGreatAgain("2/1/2016"); date.addDays(28); - assert.equal('02/29/2016', date); + assert.equal("02/29/2016", date); - date = new MakeMomentJSGreatAgain('2/1/2015'); + date = new MakeMomentJSGreatAgain("2/1/2015"); date.addDays(28); - assert.equal('03/01/2015', date); + assert.equal("03/01/2015", date); }); }); ``` **Good:** + ```javascript -import assert from 'assert'; +import assert from "assert"; -describe('MakeMomentJSGreatAgain', () => { - it('handles 30-day months', () => { - const date = new MakeMomentJSGreatAgain('1/1/2015'); +describe("MakeMomentJSGreatAgain", () => { + it("handles 30-day months", () => { + const date = new MakeMomentJSGreatAgain("1/1/2015"); date.addDays(30); - assert.equal('1/31/2015', date); + assert.equal("1/31/2015", date); }); - it('handles leap year', () => { - const date = new MakeMomentJSGreatAgain('2/1/2016'); + it("handles leap year", () => { + const date = new MakeMomentJSGreatAgain("2/1/2016"); date.addDays(28); - assert.equal('02/29/2016', date); + assert.equal("02/29/2016", date); }); - it('handles non-leap year', () => { - const date = new MakeMomentJSGreatAgain('2/1/2015'); + it("handles non-leap year", () => { + const date = new MakeMomentJSGreatAgain("2/1/2015"); date.addDays(28); - assert.equal('03/01/2015', date); + assert.equal("03/01/2015", date); }); }); ``` + **[⬆ back to top](#table-of-contents)** ## **Concurrency** + ### Use Promises, not callbacks + Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, Promises are a built-in global type. Use them! **Bad:** -```javascript -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'); - } - }); +```javascript +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"); + } + }); + } } -}); - +); ``` **Good:** + ```javascript -import { get } from 'request'; -import { writeFile } from 'fs'; +import { get } from "request"; +import { writeFile } from "fs"; -get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin') - .then((response) => { - return writeFile('article.html', response); +get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") + .then(response => { + return writeFile("article.html", response); }) .then(() => { - console.log('File written'); + console.log("File written"); }) - .catch((err) => { + .catch(err => { console.error(err); }); - ``` + **[⬆ back to top](#table-of-contents)** ### Async/Await are even cleaner than Promises + Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await which offer an even cleaner solution. All you need is a function that is prefixed in an `async` keyword, and then you can write your logic imperatively without @@ -1793,48 +1953,53 @@ a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 fe today! **Bad:** + ```javascript -import { get } from 'request-promise'; -import { writeFile } from 'fs-promise'; +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); +get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") + .then(response => { + return writeFile("article.html", response); }) .then(() => { - console.log('File written'); + console.log("File written"); }) - .catch((err) => { + .catch(err => { console.error(err); }); - ``` **Good:** + ```javascript -import { get } from 'request-promise'; -import { writeFile } from 'fs-promise'; +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) { + 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); } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ## **Error Handling** + Thrown errors are a good thing! They mean the runtime has successfully identified when something in your program has gone wrong and it's letting you know by stopping function execution on the current stack, killing the process (in Node), and notifying you in the console with a stack trace. ### Don't ignore caught errors + Doing nothing with a caught error doesn't give you the ability to ever fix or react to said error. Logging the error to the console (`console.log`) isn't much better as often times it can get lost in a sea of things printed @@ -1843,6 +2008,7 @@ think an error may occur there and therefore you should have a plan, or create a code path, for when it occurs. **Bad:** + ```javascript try { functionThatMightThrow(); @@ -1852,6 +2018,7 @@ try { ``` **Good:** + ```javascript try { functionThatMightThrow(); @@ -1867,27 +2034,30 @@ try { ``` ### Don't ignore rejected promises + For the same reason you shouldn't ignore caught errors from `try/catch`. **Bad:** + ```javascript getdata() - .then((data) => { + .then(data => { functionThatMightThrow(data); }) - .catch((error) => { + .catch(error => { console.log(error); }); ``` **Good:** + ```javascript getdata() - .then((data) => { + .then(data => { functionThatMightThrow(data); }) - .catch((error) => { + .catch(error => { // One option (more noisy than console.log): console.error(error); // Another option: @@ -1900,8 +2070,8 @@ getdata() **[⬆ back to top](#table-of-contents)** - ## **Formatting** + Formatting is subjective. Like many rules herein, there is no hard and fast rule that you must follow. The main point is DO NOT ARGUE over formatting. There are [tons of tools](http://standardjs.com/rules.html) to automate this. @@ -1912,17 +2082,19 @@ For things that don't fall under the purview of automatic formatting for some guidance. ### Use consistent capitalization + JavaScript is untyped, so capitalization tells you a lot about your variables, functions, etc. These rules are subjective, so your team can choose whatever they want. The point is, no matter what you all choose, just be consistent. **Bad:** + ```javascript 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']; +const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"]; +const Artists = ["ACDC", "Led Zeppelin", "The Beatles"]; function eraseDatabase() {} function restore_database() {} @@ -1932,12 +2104,13 @@ class Alpaca {} ``` **Good:** + ```javascript 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']; +const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"]; +const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"]; function eraseDatabase() {} function restoreDatabase() {} @@ -1945,15 +2118,17 @@ function restoreDatabase() {} class Animal {} class Alpaca {} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ back to top](#table-of-contents)** ### Function callers and callees should be close + If a function calls another, keep those functions vertically close in the source file. Ideally, keep the caller right above the callee. We tend to read code from top-to-bottom, like a newspaper. Because of this, make your code read that way. **Bad:** + ```javascript class PerformanceReview { constructor(employee) { @@ -1961,11 +2136,11 @@ class PerformanceReview { } lookupPeers() { - return db.lookup(this.employee, 'peers'); + return db.lookup(this.employee, "peers"); } lookupManager() { - return db.lookup(this.employee, 'manager'); + return db.lookup(this.employee, "manager"); } getPeerReviews() { @@ -1993,6 +2168,7 @@ review.perfReview(); ``` **Good:** + ```javascript class PerformanceReview { constructor(employee) { @@ -2011,7 +2187,7 @@ class PerformanceReview { } lookupPeers() { - return db.lookup(this.employee, 'peers'); + return db.lookup(this.employee, "peers"); } getManagerReview() { @@ -2019,7 +2195,7 @@ class PerformanceReview { } lookupManager() { - return db.lookup(this.employee, 'manager'); + return db.lookup(this.employee, "manager"); } getSelfReview() { @@ -2034,10 +2210,13 @@ review.perfReview(); **[⬆ back to top](#table-of-contents)** ## **Comments** + ### Only comment things that have business logic complexity. -Comments are an apology, not a requirement. Good code *mostly* documents itself. + +Comments are an apology, not a requirement. Good code _mostly_ documents itself. **Bad:** + ```javascript function hashIt(data) { // The hash @@ -2051,7 +2230,7 @@ function hashIt(data) { // Get character code. const char = data.charCodeAt(i); // Make the hash - hash = ((hash << 5) - hash) + char; + hash = (hash << 5) - hash + char; // Convert to 32-bit integer hash &= hash; } @@ -2059,28 +2238,30 @@ function hashIt(data) { ``` **Good:** -```javascript +```javascript 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; + hash = (hash << 5) - hash + char; // Convert to 32-bit integer hash &= hash; } } - ``` + **[⬆ back to top](#table-of-contents)** ### Don't leave commented out code in your codebase + Version control exists for a reason. Leave old code in your history. **Bad:** + ```javascript doStuff(); // doOtherStuff(); @@ -2089,16 +2270,20 @@ doStuff(); ``` **Good:** + ```javascript doStuff(); ``` + **[⬆ back to top](#table-of-contents)** ### Don't have journal comments + Remember, use version control! There's no need for dead code, commented code, and especially journal comments. Use `git log` to get history! **Bad:** + ```javascript /** * 2016-12-20: Removed monads, didn't understand them (RM) @@ -2112,25 +2297,29 @@ function combine(a, b) { ``` **Good:** + ```javascript function combine(a, b) { return a + b; } ``` + **[⬆ back to top](#table-of-contents)** ### Avoid positional markers + They usually just add noise. Let the functions and variable names along with the proper indentation and formatting give the visual structure to your code. **Bad:** + ```javascript //////////////////////////////////////////////////////////////////////////////// // Scope Model Instantiation //////////////////////////////////////////////////////////////////////////////// $scope.model = { - menu: 'foo', - nav: 'bar' + menu: "foo", + nav: "bar" }; //////////////////////////////////////////////////////////////////////////////// @@ -2142,39 +2331,41 @@ const actions = function() { ``` **Good:** + ```javascript $scope.model = { - menu: 'foo', - nav: 'bar' + menu: "foo", + nav: "bar" }; const actions = function() { // ... }; ``` + **[⬆ back to top](#table-of-contents)** ## Translation This is also available in other languages: - - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: - - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) - - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) - - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) - - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) - - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) - - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: - - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) - - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) - - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) - - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: +- ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) +- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) +- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) +- ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: + - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) + - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) +- ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) +- ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) +- ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) +- ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: + - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) + - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) +- ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) +- ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) +- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: +- ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) -**[⬆ back to top](#table-of-contents)** \ No newline at end of file +**[⬆ back to top](#table-of-contents)** From bcab32deb72d6aa0e0741a3d2a791652c86ea91d Mon Sep 17 00:00:00 2001 From: Gavish Barosee <30755017+GavBaros@users.noreply.github.com> Date: Sun, 24 Mar 2019 13:58:47 +0000 Subject: [PATCH 137/170] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4f4628ec..99a9cd16 100644 --- a/README.md +++ b/README.md @@ -2349,6 +2349,8 @@ const actions = function() { This is also available in other languages: +- ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: + [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr) - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) From 84205ae700acf2b89157cba74c5ab7468415a024 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Tue, 23 Jul 2019 13:21:01 +0200 Subject: [PATCH 138/170] remove distracting reference in "testing single concept per test" This distracted me from the code at hand, so I suggest changing the text to be more neutral. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 99a9cd16..78ba9240 100644 --- a/README.md +++ b/README.md @@ -1846,19 +1846,19 @@ or refactoring an existing one. ```javascript import assert from "assert"; -describe("MakeMomentJSGreatAgain", () => { +describe("MomentJS", () => { it("handles date boundaries", () => { let date; - date = new MakeMomentJSGreatAgain("1/1/2015"); + date = new MomentJS("1/1/2015"); date.addDays(30); assert.equal("1/31/2015", date); - date = new MakeMomentJSGreatAgain("2/1/2016"); + date = new MomentJS("2/1/2016"); date.addDays(28); assert.equal("02/29/2016", date); - date = new MakeMomentJSGreatAgain("2/1/2015"); + date = new MomentJS("2/1/2015"); date.addDays(28); assert.equal("03/01/2015", date); }); @@ -1870,21 +1870,21 @@ describe("MakeMomentJSGreatAgain", () => { ```javascript import assert from "assert"; -describe("MakeMomentJSGreatAgain", () => { +describe("MomentJS", () => { it("handles 30-day months", () => { - const date = new MakeMomentJSGreatAgain("1/1/2015"); + const date = new MomentJS("1/1/2015"); date.addDays(30); assert.equal("1/31/2015", date); }); it("handles leap year", () => { - const date = new MakeMomentJSGreatAgain("2/1/2016"); + const date = new MomentJS("2/1/2016"); date.addDays(28); assert.equal("02/29/2016", date); }); it("handles non-leap year", () => { - const date = new MakeMomentJSGreatAgain("2/1/2015"); + const date = new MomentJS("2/1/2015"); date.addDays(28); assert.equal("03/01/2015", date); }); From bcb1526ce137f3b5707a1af114576ffbd6fca164 Mon Sep 17 00:00:00 2001 From: tylim <5227509+tylim88@users.noreply.github.com> Date: Mon, 5 Aug 2019 23:57:12 +0800 Subject: [PATCH 139/170] fix wrong syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78ba9240..8d366c16 100644 --- a/README.md +++ b/README.md @@ -980,7 +980,7 @@ they are fixed if they can be. ```javascript // On old browsers, each iteration with uncached `list.length` would be costly // because of `list.length` recomputation. In modern browsers, this is optimized. -for (let i = 0, len = list.length; i < len; i++) { +for (let i = 0; len = list.length; i < len; i++) { // ... } ``` From c7cd5c4cb6570e19a4ed037676a61e8454d0a878 Mon Sep 17 00:00:00 2001 From: Chengings Date: Thu, 8 Aug 2019 23:14:46 +0100 Subject: [PATCH 140/170] Update plain http link to https --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8d366c16..83edcfb5 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## Introduction ![Humorous image of software quality estimation as a count of how many expletives -you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) +you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg) Software engineering principles, from Robert C. Martin's book [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), @@ -1830,9 +1830,9 @@ 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](http://gotwarlost.github.io/istanbul/). +[good coverage tool](https://gotwarlost.github.io/istanbul/). -There's no excuse to not write tests. There are [plenty of good JS test frameworks](http://jstherightway.org/#testing-tools), so find one that your team prefers. +There's no excuse to not write tests. There are [plenty of good JS test frameworks](https://jstherightway.org/#testing-tools), 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 @@ -2074,7 +2074,7 @@ getdata() Formatting is subjective. Like many rules herein, there is no hard and fast rule that you must follow. The main point is DO NOT ARGUE over formatting. -There are [tons of tools](http://standardjs.com/rules.html) to automate this. +There are [tons of tools](https://standardjs.com/rules.html) to automate this. Use one! It's a waste of time and money for engineers to argue over formatting. For things that don't fall under the purview of automatic formatting From 7b3107acce118181f7a797e905b8d3d8eced1f2c Mon Sep 17 00:00:00 2001 From: Piotr Date: Wed, 21 Aug 2019 15:11:36 +0200 Subject: [PATCH 141/170] fix typo - Indonesian --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83edcfb5..3dbd1f9c 100644 --- a/README.md +++ b/README.md @@ -2365,7 +2365,7 @@ This is also available in other languages: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) -- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: +- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesian**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) From 0f5326f19d4cdb705a8e8f6c386fcef39cf8af34 Mon Sep 17 00:00:00 2001 From: Userbit <34487074+Userbit@users.noreply.github.com> Date: Fri, 25 Oct 2019 17:08:21 +0300 Subject: [PATCH 142/170] Some corrections in **Concurrency** section --- README.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3dbd1f9c..b04ec960 100644 --- a/README.md +++ b/README.md @@ -1908,11 +1908,11 @@ import { writeFile } from "fs"; get( "https://en.wikipedia.org/wiki/Robert_Cecil_Martin", - (requestErr, response) => { + (requestErr, response, body) => { if (requestErr) { console.error(requestErr); } else { - writeFile("article.html", response.body, writeErr => { + writeFile("article.html", body, writeErr => { if (writeErr) { console.error(writeErr); } else { @@ -1927,12 +1927,12 @@ get( **Good:** ```javascript -import { get } from "request"; -import { writeFile } from "fs"; +import { get } from "request-promise"; +import { writeFile } from "fs-extra"; get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") - .then(response => { - return writeFile("article.html", response); + .then(body => { + return writeFile("article.html", body); }) .then(() => { console.log("File written"); @@ -1956,11 +1956,11 @@ today! ```javascript import { get } from "request-promise"; -import { writeFile } from "fs-promise"; +import { writeFile } from "fs-extra"; get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") - .then(response => { - return writeFile("article.html", response); + .then(body => { + return writeFile("article.html", body); }) .then(() => { console.log("File written"); @@ -1974,19 +1974,21 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") ```javascript import { get } from "request-promise"; -import { writeFile } from "fs-promise"; +import { writeFile } from "fs-extra"; async function getCleanCodeArticle() { try { - const response = await get( + const body = await get( "https://en.wikipedia.org/wiki/Robert_Cecil_Martin" ); - await writeFile("article.html", response); + await writeFile("article.html", body); console.log("File written"); } catch (err) { console.error(err); } } + +getCleanCodeArticle() ``` **[⬆ back to top](#table-of-contents)** From 12b650949effe96e1d06e75cb795f44a1b8428b6 Mon Sep 17 00:00:00 2001 From: "TZU-YEN, CHANG" Date: Mon, 18 Nov 2019 20:56:08 +0800 Subject: [PATCH 143/170] Add Traditional Chinese --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b04ec960..f0a520ae 100644 --- a/README.md +++ b/README.md @@ -2356,9 +2356,10 @@ This is also available in other languages: - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) -- ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**: +- ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Simplified Chinese**: - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) +- ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Traditional Chinese**: [AllJointTW/clean-code-javascript](https://github.com/AllJointTW/clean-code-javascript) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) @@ -2367,7 +2368,7 @@ This is also available in other languages: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) -- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesian**: +- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) From 0762d8fa077705b879cd52e7ea3d7de9ffc8a5ac Mon Sep 17 00:00:00 2001 From: Fakhruddin Ali Hussain Date: Thu, 21 Nov 2019 11:40:44 -0800 Subject: [PATCH 144/170] Fixing bad code example for over-optimization --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0a520ae..e7c01dbf 100644 --- a/README.md +++ b/README.md @@ -980,7 +980,7 @@ they are fixed if they can be. ```javascript // On old browsers, each iteration with uncached `list.length` would be costly // because of `list.length` recomputation. In modern browsers, this is optimized. -for (let i = 0; len = list.length; i < len; i++) { +for (let i = 0, len = list.length; i < len; i++) { // ... } ``` From 425d4c9f2a56ad253bc27891ec617b6d14bede92 Mon Sep 17 00:00:00 2001 From: "Md. Sabbir Alam" Date: Wed, 1 Jan 2020 13:00:05 +0600 Subject: [PATCH 145/170] Added source link for bengali translations --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7c01dbf..1e710a99 100644 --- a/README.md +++ b/README.md @@ -2372,5 +2372,7 @@ This is also available in other languages: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) - +- ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: + [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) + **[⬆ back to top](#table-of-contents)** From 4503a25d5c91555db4392d7777818df9efc38514 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine OGER Date: Fri, 10 Jan 2020 14:06:03 +0100 Subject: [PATCH 146/170] Add advantage in function parameters --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e710a99..258eb49a 100644 --- a/README.md +++ b/README.md @@ -251,11 +251,12 @@ destructuring syntax. This has a few advantages: 1. When someone looks at the function signature, it's immediately clear what properties are being used. -2. Destructuring also clones the specified primitive values of the argument +2. It can be used to simulate named parameters. +3. Destructuring also clones the specified primitive values of the argument object passed into the function. This can help prevent side effects. Note: objects and arrays that are destructured from the argument object are NOT cloned. -3. Linters can warn you about unused properties, which would be impossible +4. Linters can warn you about unused properties, which would be impossible without destructuring. **Bad:** @@ -264,6 +265,9 @@ destructuring syntax. This has a few advantages: function createMenu(title, body, buttonText, cancellable) { // ... } + +createMenu("Foo", "Bar", "Baz", true); + ``` **Good:** From 3afe391aa0a1a053d1b16b895097ea51b39af27e Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 18 Jan 2020 17:14:32 +0330 Subject: [PATCH 147/170] Added number separator for improve readability --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e710a99..374b4bab 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ setTimeout(blastOff, 86400000); ```javascript // Declare them as capitalized named constants. -const MILLISECONDS_IN_A_DAY = 86400000; +const MILLISECONDS_IN_A_DAY = 86_400_000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` From 8491866a87641708ee4253a1b89b5d8a0243d426 Mon Sep 17 00:00:00 2001 From: Bimochan Shrestha Date: Tue, 21 Jan 2020 14:13:48 +0545 Subject: [PATCH 148/170] Change argument name from inner variable name --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 950f1d20..1a03e71a 100644 --- a/README.md +++ b/README.md @@ -543,7 +543,7 @@ const menuConfig = { }; function createMenu(config) { - config = Object.assign( + let finalConfig = Object.assign( { title: "Foo", body: "Bar", @@ -552,7 +552,7 @@ function createMenu(config) { }, config ); - + return finalConfig // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } From ba9797eb939b0f5edad0b19babc904f697bced58 Mon Sep 17 00:00:00 2001 From: Justin Lee Date: Tue, 21 Jan 2020 13:17:12 -0800 Subject: [PATCH 149/170] Update README.md Small grammar correction. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 950f1d20..08a33104 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ createMenu({ This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. -When you can isolate a function to just one action, they can be refactored +When you can isolate a function to just one action, it can be refactored easily and your code will read much cleaner. If you take nothing else away from this guide other than this, you'll be ahead of many developers. From 0ab8ebaed48ac72c4f27680f7c680dc7e6cf407d Mon Sep 17 00:00:00 2001 From: Justin Lee Date: Tue, 21 Jan 2020 13:20:28 -0800 Subject: [PATCH 150/170] Update README.md Small grammar correction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 950f1d20..c7f7459b 100644 --- a/README.md +++ b/README.md @@ -650,7 +650,7 @@ 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 +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" From c18b9766f4330f301d8fb68de8f38286f1c42cef Mon Sep 17 00:00:00 2001 From: Raigo Jerva Date: Wed, 22 Jan 2020 00:19:37 +0000 Subject: [PATCH 151/170] sort translations --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 08a33104..0aac9ff5 100644 --- a/README.md +++ b/README.md @@ -2355,28 +2355,24 @@ const actions = function() { This is also available in other languages: -- ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: - [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr) +- ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) -- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) -- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Simplified Chinese**: - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Traditional Chinese**: [AllJointTW/clean-code-javascript](https://github.com/AllJointTW/clean-code-javascript) +- ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) +- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) +- ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) +- ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) +- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) +- ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) -- ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) -- ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: - [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) -- ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: - [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) -- ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: - [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) - + **[⬆ back to top](#table-of-contents)** From a812aa93c8e7eeca7bfa27e930244987644a5a90 Mon Sep 17 00:00:00 2001 From: Raigo Jerva Date: Wed, 22 Jan 2020 00:20:22 +0000 Subject: [PATCH 152/170] add armenian translation to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0aac9ff5..514956db 100644 --- a/README.md +++ b/README.md @@ -2355,6 +2355,7 @@ const actions = function() { This is also available in other languages: +- ![am](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Armenia.png) **Armenian**: [hanumanum/clean-code-javascript/](https://github.com/hanumanum/clean-code-javascript) - ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Simplified Chinese**: From 5f8daa0121e4dfab1eb0d685226e669da0984e50 Mon Sep 17 00:00:00 2001 From: Risto McIntosh Date: Thu, 6 Feb 2020 21:44:02 -0600 Subject: [PATCH 153/170] Fixed a small typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 514956db..db2a4a25 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ saveCityZipCode( ```javascript const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; -const [, city, zipCode] = address.match(cityZipCodeRegex) || []; +const [city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` From c90b109b38fe6174c68f0c30d02b1b32149a5371 Mon Sep 17 00:00:00 2001 From: Risto McIntosh Date: Fri, 7 Feb 2020 08:12:47 -0600 Subject: [PATCH 154/170] Add an underscore to "Use explanatory variables" This makes the destructuring a little more readable --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db2a4a25..02c46fde 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ saveCityZipCode( ```javascript const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; -const [city, zipCode] = address.match(cityZipCodeRegex) || []; +const [_, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` From 2edfeef74002f7aeaa15a7bea4adb047b85dd539 Mon Sep 17 00:00:00 2001 From: Burak Sonmez Date: Thu, 26 Mar 2020 08:35:58 +0300 Subject: [PATCH 155/170] turkish added --- README.md | 901 +++++++++++++++++++++--------------------------------- 1 file changed, 355 insertions(+), 546 deletions(-) diff --git a/README.md b/README.md index bbe7a083..d7747ff9 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,55 @@ # clean-code-javascript -## Table of Contents +## İçindekiler -1. [Introduction](#introduction) -2. [Variables](#variables) -3. [Functions](#functions) -4. [Objects and Data Structures](#objects-and-data-structures) -5. [Classes](#classes) +1. [Giriş](#Giriş) +2. [Değişkenler](#Değişkenler) +3. [Fonksiyonlar](#Fonksiyonlar) +4. [Nesneler ve Veri Yapıları](#nesneler-ve-veri-yapıları) +5. [Sınıflar (Class)](#sınıflar) 6. [SOLID](#solid) -7. [Testing](#testing) -8. [Concurrency](#concurrency) -9. [Error Handling](#error-handling) -10. [Formatting](#formatting) -11. [Comments](#comments) -12. [Translation](#translation) +7. [Testler](#testler) +8. [Tutarlılık](#Tutarlılık) +9. [Hata Yönetimi](#Hata-Yönetimi) +10. [Biçimlendirme](#Biçimlendirme) +11. [Yorumlar](#yorumlar) +12. [Çeviriler](#çeviriler) -## Introduction +## Giriş -![Humorous image of software quality estimation as a count of how many expletives -you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg) +![Kod okurken kaç defa bağıracağınızın bir sayısı olarak yazılım kalite tahmininin görüntüsü](https://www.osnews.com/images/comics/wtfm.jpg) -Software engineering principles, from Robert C. Martin's book -[_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adapted for JavaScript. This is not a style guide. It's a guide to producing -[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. +Robert C. Martin'nın kitabı olan ve yazılım mühendisliği ilkerini barındıran [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)'un JavaScript için uyarlanmış hali. Bu bir tarz rehberi değil. JavaScript'te [okunabilir, yeniden kullanılabilir ve yeniden düzenlenebilir](https://github.com/ryanmcdermott/3rs-of-software-architecture) yazılımlar üretmeye yönelik bir kılavuzdur. -Not every principle herein has to be strictly followed, and even fewer will be -universally agreed upon. These are guidelines and nothing more, but they are -ones codified over many years of collective experience by the authors of -_Clean Code_. +Buradaki her ilkeye kesinlikle uyulmak zorunda değildir ve daha azı evrensel olarak kabul edilecektir. Bunlar önerilerden başka bir şey değildir, fakat bunlar _Clean Code_ yazarları tarafından uzun yıllar süren tecrübeler ile kodlandı. -Our craft of software engineering is just a bit over 50 years old, and we are -still learning a lot. When software architecture is as old as architecture -itself, maybe then we will have harder rules to follow. For now, let these -guidelines serve as a touchstone by which to assess the quality of the -JavaScript code that you and your team produce. +Yazılım mühendisliği zanaatımız 50 yaşın biraz üzerinde ve hala çok şey öğreniyoruz. Yazılım mimarisi, mimarinin kendisi kadar eski olduğunda, belki de takip edilmesi daha zor kurallarımız olacak. Şimdi, bu önerilerin sizin ve ekibinizin ürettiği JavaScript kodunun kalitesini değerlendirmek için bir mihenk taşı işlevi görmesine izin verin. -One more thing: knowing these won't immediately make you a better software -developer, and working with them for many years doesn't mean you won't make -mistakes. Every piece of code starts as a first draft, like wet clay getting -shaped into its final form. Finally, we chisel away the imperfections when -we review it with our peers. Don't beat yourself up for first drafts that need -improvement. Beat up the code instead! +Bir şey daha: bunların bilinmesi sizi hemen daha iyi bir yazılım geliştiricisi yapmaz ve uzun yıllar onlarla çalışmak hata yapmayacağınız anlamına gelmez. -## **Variables** +Her kod parçası ilk taslak olarak başlar, örneğin ıslak kil son halini alır. Son olarak, akranlarımızla gözden geçirdiğimizde kusurları kesiyoruz. İyileştirilmesi gereken ilk taslaklar için kendinize yüklenmeyin. Onun yerine kodunuza yüklenin. -### Use meaningful and pronounceable variable names +## **Değişkenler** -**Bad:** +### Anlamlı ve belirgin değişken adları kullanın + +**Kötü:** ```javascript const yyyymmdstr = moment().format("YYYY/MM/DD"); ``` -**Good:** +**İyi:** ```javascript const currentDate = moment().format("YYYY/MM/DD"); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use the same vocabulary for the same type of variable +### Aynı değişken türü için aynı kelimeleri kullanın -**Bad:** +**Kötü:** ```javascript getUserInfo(); @@ -71,45 +57,41 @@ getClientData(); getCustomerRecord(); ``` -**Good:** +**İyi:** ```javascript getUser(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use searchable names +### Aranabilir isimler kullanın -We will read more code than we will ever write. It's important that the code we -do write is readable and searchable. By _not_ naming variables that end up -being meaningful for understanding our program, we hurt our readers. -Make your names searchable. Tools like -[buddy.js](https://github.com/danielstjules/buddy.js) and -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) -can help identify unnamed constants. +Yazacağımızdan daha fazla kod okuyacağız. Yazdığımız kodun okunabilir ve aranabilir olması önemlidir. Değişkenleri anlamlı ve anlaşılabilir _isimlendirmemekle_, okuyucuya zarar veriyoruz. +Adlarınızı aranabilir hale getirin. [buddy.js](https://github.com/danielstjules/buddy.js) ve +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) gibi araçlar isimlendirilmemiş sabitlerinizi belirlemenize yardımcı olacaktır. -**Bad:** +**Kötü:** ```javascript -// What the heck is 86400000 for? +// 86400000 neydi ? setTimeout(blastOff, 86400000); ``` -**Good:** +**İyi:** ```javascript -// Declare them as capitalized named constants. +// Bunları büyük harfle adlandırılmış sabitler olarak bildirin. const MILLISECONDS_IN_A_DAY = 86_400_000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use explanatory variables +### Açıklayıcı değişkenler kullan -**Bad:** +**Kötü:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -120,7 +102,7 @@ saveCityZipCode( ); ``` -**Good:** +**İyi:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -129,13 +111,13 @@ const [_, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid Mental Mapping +### Kafadan planmaktan kaçının -Explicit is better than implicit. +Açık olmak, ima etmekten iyidir. -**Bad:** +**Kötü:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -145,12 +127,12 @@ locations.forEach(l => { // ... // ... // ... - // Wait, what is `l` for again? + // Bir dakika, 'l' neydi? dispatch(l); }); ``` -**Good:** +**İyi:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -164,14 +146,13 @@ locations.forEach(location => { }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't add unneeded context +### Gereksiz içerikler eklemeyin -If your class/object name tells you something, don't repeat that in your -variable name. +Sınıf / nesne adınız size bir şey söylüyorsa, bunu değişken adınızda tekrarlamayın. -**Bad:** +**Kötü:** ```javascript const Car = { @@ -185,7 +166,7 @@ function paintCar(car) { } ``` -**Good:** +**İyi:** ```javascript const Car = { @@ -199,16 +180,13 @@ function paintCar(car) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use default arguments instead of short circuiting or conditionals +### Kısa devre veya şartlar yerine önceden tanımlanmış argümanlar kullanın -Default arguments are often cleaner than short circuiting. Be aware that if you -use them, your function will only provide default values for `undefined` -arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and -`NaN`, will not be replaced by a default value. +Önceden tanımlanan argümanlar genellikle kısa devrelere göre daha temizdir. Bunları kullanırsanız şunun farkında olun, fonksiyonunuz sadece `tanımsız` _(undefined)_ değerler için önceden tanımlanmış argümana kullanacaktır. `''`, `""`, `false`, `null`, `0`, ve `NaN` gibi "yanlış denilebilecek" değerler önceden tanımlanmış bir değerle değiştirilmez. -**Bad:** +**Kötü:** ```javascript function createMicrobrewery(name) { @@ -217,7 +195,7 @@ function createMicrobrewery(name) { } ``` -**Good:** +**İyi:** ```javascript function createMicrobrewery(name = "Hipster Brew Co.") { @@ -225,41 +203,27 @@ function createMicrobrewery(name = "Hipster Brew Co.") { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Functions** +## **Fonksiyonlar** -### Function arguments (2 or fewer ideally) +### Fonksiyonlar argümanları (Tercihen 2 veya daha az) -Limiting the amount of function parameters is incredibly important because it -makes testing your function easier. Having more than three leads to a -combinatorial explosion where you have to test tons of different cases with -each separate argument. +Fonksiyon parametrelerinin sayısını sınırlamak inanılmaz derecede önemlidir, çünkü fonksiyonunuzu test etmeyi kolaylaştırır. Üçten fazlasına sahip olmak, her bir argümanla tonlarca farklı vakayı test etmeniz gereken bir kombinasyonel patlamaya yol açar. -One or two arguments is the ideal case, and three should be avoided if possible. -Anything more than that should be consolidated. Usually, if you have -more than two arguments then your function is trying to do too much. In cases -where it's not, most of the time a higher-level object will suffice as an -argument. +Bir veya iki argüman ideal durumdur ve mümkünse üç tanesinden kaçınılmalıdır. Bundan daha fazlası birleştirilmeldir. Genellikle, +ikiden fazla argüman sonra fonksiyonunuz çok işlem yapmaya çalışıyor. Olmadığı durumlarda, çoğu zaman daha üst düzey bir nesne argüman olarak yeterli olacaktır. -Since JavaScript allows you to make objects on the fly, without a lot of class -boilerplate, you can use an object if you are finding yourself needing a -lot of arguments. +JavaScript havada nesne yapmanıza olanak sağladığı için, bir çok sınıf yapısına gerek kalmadan, bir nesneyi birden fazla nesne kullanmadan kullanabilirsiniz. -To make it obvious what properties the function expects, you can use the ES2015/ES6 -destructuring syntax. This has a few advantages: +Fonksiyonun hangi özellikleri beklediğini netleştirmek için ES2015 / ES6 ayrıştırma sintaksını kullanabilirsiniz. Bunun birkaç avantajı vardır: -1. When someone looks at the function signature, it's immediately clear what - properties are being used. -2. It can be used to simulate named parameters. -3. Destructuring also clones the specified primitive values of the argument - object passed into the function. This can help prevent side effects. Note: - objects and arrays that are destructured from the argument object are NOT - cloned. -4. Linters can warn you about unused properties, which would be impossible - without destructuring. +1. Birisi fonksiyonun imzasına baktığında, hangi özelliklerin kullanıldığını hemen anlar.. +2. Adlandırılmış parametreleri simüle etmek için kullanılabilir. +3. Ayrıştırma işlemi ayrıca fonksiyona iletilen argüman nesnesinin belirtilen ilk değerlerini de klonlar. Bu, yan etkilerin önlenmesine yardımcı olabilir. Not: Argüman nesnelerinden ayrıştırılan nesneler ve diziler klonlanmaz! +4. Linters, sizi kullanılmayan değerler için uyarabilir bu da ayrıştırmadan imkansız olurdu. -**Bad:** +**Kötü:** ```javascript function createMenu(title, body, buttonText, cancellable) { @@ -270,7 +234,7 @@ createMenu("Foo", "Bar", "Baz", true); ``` -**Good:** +**İyi:** ```javascript function createMenu({ title, body, buttonText, cancellable }) { @@ -285,17 +249,13 @@ createMenu({ }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Functions should do one thing +### Fonksiyonlar bir şey yapmalı -This is by far the most important rule in software engineering. When functions -do more than one thing, they are harder to compose, test, and reason about. -When you can isolate a function to just one action, it can be refactored -easily and your code will read much cleaner. If you take nothing else away from -this guide other than this, you'll be ahead of many developers. +Bu, yazılım mühendisliğinde açık ara en önemli kuraldır. Fonksiyonlar birden fazla şey yaptığında, birleştirmesi, test etmesi ve anlamdırılması daha zordur. Bir fonksiyonu yalnızca bir eyleme ayırabildiğinizde, kolayca yeniden düzenlenebilir ve kodunuz çok daha temiz okunur. Bu yazıdan başka bir şey almazsanız dahi sadece bununla birçok geliştiricinin önünde olacaksınız. -**Bad:** +**Kötü:** ```javascript function emailClients(clients) { @@ -308,7 +268,7 @@ function emailClients(clients) { } ``` -**Good:** +**İyi:** ```javascript function emailActiveClients(clients) { @@ -321,11 +281,11 @@ function isActiveClient(client) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Function names should say what they do +### Fonksiyon isimleri ne yaptıklarını anlatmalı -**Bad:** +**Kötü:** ```javascript function addToDate(date, month) { @@ -334,11 +294,11 @@ function addToDate(date, month) { const date = new Date(); -// It's hard to tell from the function name what is added +// Fonksiyonun adından ne eklendiğini söylemek zor addToDate(date, 1); ``` -**Good:** +**İyi:** ```javascript function addMonthToDate(month, date) { @@ -349,15 +309,13 @@ const date = new Date(); addMonthToDate(1, date); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Functions should only be one level of abstraction +### Fonksiyonlar soyutlaştırmadan sadece bir seviye uzak olmalıdır -When you have more than one level of abstraction your function is usually -doing too much. Splitting up functions leads to reusability and easier -testing. +Birden fazla soyutlama seviyeniz olduğunda fonksiyonunuz genellikle çok fazla şey yapar. Fonksiyonların bölünmesi, yeniden kullanılabilirliği ve daha kolay test yapılmasını sağlayacak. -**Bad:** +**Kötü:** ```javascript function parseBetterJSAlternative(code) { @@ -384,7 +342,7 @@ function parseBetterJSAlternative(code) { } ``` -**Good:** +**İyi:** ```javascript function parseBetterJSAlternative(code) { @@ -421,32 +379,19 @@ function parse(tokens) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Remove duplicate code +### Tekrarlanan kodu sil -Do your absolute best to avoid duplicate code. Duplicate code is bad because it -means that there's more than one place to alter something if you need to change -some logic. +Tekrarlanan kodlardan kaçınmak için elinizden geleni yapın. Tekrarlanan kod kötü çünkü bazı mantığı değiştirmeniz gerekirse bir şeyi değiştirmek için birden fazla yer olduğu anlamına geliyor. -Imagine if you run a restaurant and you keep track of your inventory: all your -tomatoes, onions, garlic, spices, etc. If you have multiple lists that -you keep this on, then all have to be updated when you serve a dish with -tomatoes in them. If you only have one list, there's only one place to update! +Bir restoran işlettiğinizi ve stoklarınızı takip ettiğinizi düşünün: tüm domates, soğan, sarımsak, baharat, vb. onlara. Yalnızca bir listeniz varsa, güncellenecek tek bir yer vardır! -Oftentimes you have duplicate code because you have two or more slightly -different things, that share a lot in common, but their differences force you -to have two or more separate functions that do much of the same things. Removing -duplicate code means creating an abstraction that can handle this set of -different things with just one function/module/class. +Çoğunlukla yinelenen kodunuz vardır, çünkü çoğu şeyi ortak paylaşsalar dahi çok küçük bir kaç farklılık vardır, ancak farklılıkları sizi aynı şeylerin çoğunu yapan iki veya daha fazla ayrı fonksiyona sahip olmaya zorlar. Tekrarlanan kodun kaldırılması, bu farklı şeyleri tek bir fonksiyon / modül / sınıfla işleyebilecek bir soyutlama oluşturmak anlamına gelir. -Getting the abstraction right is critical, that's why you should follow the -SOLID principles laid out in the _Classes_ section. Bad abstractions can be -worse than duplicate code, so be careful! Having said this, if you can make -a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself -updating multiple places anytime you want to change one thing. +Soyutlamayı doğru yapmak kritik öneme sahiptir, bu yüzden _Sınıflar_ bölümünde belirtilen SOLID ilkelerini izlemelisiniz. Kötü soyutlamalar yinelenen koddan daha kötü olabilir, bu yüzden dikkatli olun! Bunu söyledikten sonra, eğer iyi bir soyutlama yapabilirseniz, yapın! Kendinizi tekrarlamayın, aksi takdirde bir şeyi değiştirmek istediğinizde kendinizi birden fazla yeri güncellerken bulacaksınız. -**Bad:** +**Kötü:** ```javascript function showDeveloperList(developers) { @@ -480,7 +425,7 @@ function showManagerList(managers) { } ``` -**Good:** +**İyi:** ```javascript function showEmployeeList(employees) { @@ -507,11 +452,11 @@ function showEmployeeList(employees) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Set default objects with Object.assign +### Object.assign ile varsayılan nesneleri ayarlama -**Bad:** +**Kötü:** ```javascript const menuConfig = { @@ -532,12 +477,12 @@ function createMenu(config) { createMenu(menuConfig); ``` -**Good:** +**İyi:** ```javascript const menuConfig = { title: "Order", - // User did not include 'body' key + // Kullanıcı 'body' eklemedi buttonText: "Send", cancellable: true }; @@ -553,20 +498,20 @@ function createMenu(config) { config ); - // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config çıktısı şimdi : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't use flags as function parameters +### Fonksiyon parametrelerinde işaretleme kullanma -Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. +İşaretlemeler fonksiyonunuzun birden fazla şey yaptığını gösterir. Fonkisyonlar sadece tek şey yapmalılar. Eğer aşağıdakine benzer değişiklere ve mantıksal operatorlere sahip fonksiyonunuz varsa fonksiyonlarınızı ayırın. -**Bad:** +**Kötü:** ```javascript function createFile(name, temp) { @@ -578,7 +523,7 @@ function createFile(name, temp) { } ``` -**Good:** +**İyi:** ```javascript function createFile(name) { @@ -590,30 +535,21 @@ function createTempFile(name) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid Side Effects (part 1) +### Yan Etkilerden Kaçının (Bölüm 1) -A function produces a side effect if it does anything other than take a value in -and return another value or values. A side effect could be writing to a file, -modifying some global variable, or accidentally wiring all your money to a -stranger. +Bir fonksiyon, bir değeri alıp başka bir değer veya değer döndürmekten başka bir şey yaparsa yan etki üretir. Bir yan etki, bir dosyaya yazma, bazı global değişkeni değiştirme veya yanlışlıkla tüm paranızı bir yabancıya gönderme olabilir. -Now, you do need to have side effects in a program on occasion. Like the previous -example, you might need to write to a file. What you want to do is to -centralize where you are doing this. Don't have several functions and classes -that write to a particular file. Have one service that does it. One and only one. +Şimdi, zaman zaman bir programda yan etkilere sahip olmanız gerekir. Önceki örnekte olduğu gibi, bir dosyaya yazmanız gerekebilir. Yapmak istediğiniz, bunu yapacağınız yeri merkezileştirmektir. Birden fazla yazma işlemi yapan fonksiyonlar veya sınıflar yapmayın. Bunu yapan bir servisiniz olsun. Bir ve sadece bir. -The main point is to avoid common pitfalls like sharing state between objects -without any structure, using mutable data types that can be written to by anything, -and not centralizing where your side effects occur. If you can do this, you will -be happier than the vast majority of other programmers. +Buradaki ana nokta, herhangi bir yapıya sahip olmayan nesneler arasında durumu(state) paylaşmak, herhangi bir şeyle yazılabilen değiştirilebilir(mutable) veri türlerini kullanmak ve yan etkilerinizin nerede ortaya çıktığını merkezileştirmemek gibi yaygın tuzaklardan kaçınmaktır. Bunu yapabilirseniz, diğer programcıların büyük çoğunluğundan daha mutlu olacaksınız. -**Bad:** +**Kötü:** ```javascript -// 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. +// Aşağıdaki fonksiyon Global değişkeni refere alıyor +// Bu adı kullanan başka bir fonksiyonumuz olsaydı, şimdi bir dizi olurdu ve onu bozacaktı. let name = "Ryan McDermott"; function splitIntoFirstAndLastName() { @@ -625,7 +561,7 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**Good:** +**İyi:** ```javascript function splitIntoFirstAndLastName(name) { @@ -639,44 +575,27 @@ console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid Side Effects (part 2) +### Yan Etkilerden Kaçının (Bölüm 2) -In JavaScript, primitives are passed by value and objects/arrays are passed by -reference. In the case of objects and arrays, if your function makes a change -in a shopping cart array, for example, by adding an item to purchase, -then any other function that uses that `cart` array will be affected by this -addition. That may be great, however it can be bad too. Let's imagine a bad -situation: +JavaScript'te, temel öğeler değerlerle aktarılır (passed by value) ve objeler/diziler referans ile aktarılır (passed by reference). -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. +Bu durumda, nesnelerde ve dizilerde fonksiyonunuz bir değişiklik yaparsa +örneğin, bir alışveriş sepeti dizisinde, satın almak için bir öğe ekleyerek, +bu `cart` dizisini kullanan diğer fonksiyonlar bu eklemeden etkilenecektir. +Bu harika olabilir, ancak kötü de olabilir. Kötü bir hayal edelim +durum: -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. +Kullanıcı, 'Satın Al' butonuna basar ve bu `satınal` fonksiyonu sunucuya bir istek atarak `sepet` dizisini sunucuya gönderir. Kötü bir ağ bağlantısı nedeniyle, `satınal` işlevi isteği yeniden denemeye devam etmelidir. Şimdi, bu arada kullanıcı yanlışlıkla ağ isteği başlamadan istemedikleri bir öğenin üzerine "Sepete Ekle" düğmesini tıklarsa ne olur? Bu olursa ve ağ isteği başlarsa, yanlışlıkla satın alınan öğeyi gönderir çünkü alışveriş sepeti dizisine, `ürünüSepeteEkle` işlevinin istenmeyen bir öğe ekleyerek değiştirdiği bir referansı vardır. `ürünüSepeteEkle` ın her zaman `sepeti` klonlaması, düzenlemesi ve klonu döndürmesi için harika bir çözüm olacaktır. Bu, alışveriş sepetinin referansını tutan başka hiçbir işlevin herhangi bir değişiklikten etkilenmemesini sağlar. -Two caveats to mention to this approach: +Bu yaklaşıma değinecek iki uyarı: -1. 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! +1. Girilen nesnesini gerçekten değiştirmek istediğiniz durumlar olabilir, ancak bunu uyguladığınızda, bu vakaların oldukça nadir olduğunu göreceksiniz. Çoğu şeyin yan etkisi olmayacak şekilde yeniden düzenlenebilir! -2. 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](https://facebook.github.io/immutable-js/) 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. +2. Büyük nesneleri klonlamak performans açısından çok pahalı olabilir. Neyse ki, bu pratikte büyük bir sorun değildir, çünkü bu tür programlama yaklaşımlarını manuel yapmaktansa daha hızlı olmasını ve büyük nesneleri ve dizileri klonlanlarken daha az bellek harcayan [harika kütüphaneler](https://facebook.github.io/immutable-js/) vardır. -**Bad:** +**Kötü:** ```javascript const addItemToCart = (cart, item) => { @@ -684,7 +603,7 @@ const addItemToCart = (cart, item) => { }; ``` -**Good:** +**İyi:** ```javascript const addItemToCart = (cart, item) => { @@ -692,21 +611,17 @@ const addItemToCart = (cart, item) => { }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +### Global fonksiyonlar yazma -### Don't write to global functions +Globalleri kirletmek JavaScript'te kötü bir uygulamadır, çünkü başka bir kütüphaneyle çakıştırabilirsiniz ve API'lerinizi kullananlar kişiler canlıya çıkana kadar bu aksi durumların farkında olmayabilir. -Polluting globals is a bad practice in JavaScript because you could clash with another -library and the user of your API would be none-the-wiser until they get an -exception in production. Let's think about an example: what if you wanted to -extend JavaScript's native Array method to have a `diff` method that could -show the difference between two arrays? You could write your new function -to the `Array.prototype`, but it could clash with another library that tried -to do the same thing. What if that other library was just using `diff` to find -the difference between the first and last elements of an array? This is why it -would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. +Bir örnek düşünelim: JavaScript'in yerel kütüphanesindeki dizi `diff` methodunu genişletmek ve iki dizinin farklılıklarını göstermek istediğinizi var sayalım. JavaScript'in yerel Array yöntemini iki dizi arasındaki farkı gösterebilecek bir "diff" yöntemine genişletmek mi istiyorsunuz? -**Bad:** +Yeni fonksiyonunuzu `Array.prototype`'e yazabilirsiniz, ancak aynı şeyi yapmaya çalışan başka bir kütüphane ile çakışabilir. Ya diğer kütüphane bir 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 `Array` globalini genişletmek çok daha iyi olurdu. + +**Kötü:** ```javascript Array.prototype.diff = function diff(comparisonArray) { @@ -715,7 +630,7 @@ Array.prototype.diff = function diff(comparisonArray) { }; ``` -**Good:** +**İyi:** ```javascript class SuperArray extends Array { @@ -726,15 +641,13 @@ class SuperArray extends Array { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Favor functional programming over imperative programming +### Zorunlu programlama yerine fonksiyonel programlamayı tercih edin -JavaScript isn't a functional language in the way that Haskell is, but it has -a functional flavor to it. Functional languages can be cleaner and easier to test. -Favor this style of programming when you can. +JavaScript, Haskell'in olduğu gibi işlevsel bir dil değildir, ancak işlevsel bir tadı vardır. İşlevsel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiğinizde bu tarz bir programlama yapın. -**Bad:** +**Kötü:** ```javascript const programmerOutput = [ @@ -763,7 +676,7 @@ for (let i = 0; i < programmerOutput.length; i++) { } ``` -**Good:** +**İyi:** ```javascript const programmerOutput = [ @@ -791,11 +704,11 @@ const totalOutput = programmerOutput.reduce( ); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Encapsulate conditionals +### Koşulları kapsamak -**Bad:** +**Kötü:** ```javascript if (fsm.state === "fetching" && isEmpty(listNode)) { @@ -803,7 +716,7 @@ if (fsm.state === "fetching" && isEmpty(listNode)) { } ``` -**Good:** +**İyi:** ```javascript function shouldShowSpinner(fsm, listNode) { @@ -815,11 +728,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** ### Avoid negative conditionals -**Bad:** +**Kötü:** ```javascript function isDOMNodeNotPresent(node) { @@ -831,7 +744,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**Good:** +**İyi:** ```javascript function isDOMNodePresent(node) { @@ -843,20 +756,14 @@ if (isDOMNodePresent(node)) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid conditionals +### Koşullardan kaçının -This seems like an impossible task. Upon first hearing this, most people say, -"how am I supposed to do anything without an `if` statement?" The answer is that -you can use polymorphism to achieve the same task in many cases. The second -question is usually, "well that's great but why would I want to do that?" The -answer is a previous clean code concept we learned: a function should only do -one thing. When you have classes and functions that have `if` statements, you -are telling your user that your function does more than one thing. Remember, -just do one thing. +Bu imkansız bir görev gibi görünüyor. Bunu ilk kez duyduktan sonra, çoğu kişi "`if` ifadesi olmadan nasıl bir şey yapmam gerekiyor?" Cevap şu ki +birçok durumda aynı görevi yerine getirmek için polimorfizm kullanabilirsiniz. İkinci soru genellikle, "peki bu harika ama neden bunu yapmak isteyeyim ki?" Cevap, daha önce öğrendiğimiz temiz bir kod kavramıydı: bir fonksiyon sadece bir şey yapmalı. `If` ifadeleri olan sınıflarınız ve fonksiyonlarınız olduğunda, kullanıcılara işlevinizin birden fazla şey yaptığını söylüyor. Unutmayın, sadece bir şey yapın. -**Bad:** +**Kötü:** ```javascript class Airplane { @@ -874,7 +781,7 @@ class Airplane { } ``` -**Good:** +**İyi:** ```javascript class Airplane { @@ -903,16 +810,13 @@ class Cessna extends Airplane { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid type-checking (part 1) +### Yazım denetiminden kaçının (Bölüm 1) -JavaScript is untyped, which means your functions can take any type of argument. -Sometimes you are bitten by this freedom and it becomes tempting to do -type-checking in your functions. There are many ways to avoid having to do this. -The first thing to consider is consistent APIs. +JavaScript türsüzdür, yani işlevleriniz her türlü argümanı alabilir. Bazen bu özgürlük bizi oltaya getirebilir ve fonksiyonlarınızda tip kontrolü yapmak cazip hale gelir. Bunu yapmaktan kaçınmanın birçok yolu vardır. Dikkate alınması gereken ilk şey tutarlı API'lardır. -**Bad:** +**Kötü:** ```javascript function travelToTexas(vehicle) { @@ -924,7 +828,7 @@ function travelToTexas(vehicle) { } ``` -**Good:** +**İyi:** ```javascript function travelToTexas(vehicle) { @@ -932,21 +836,14 @@ function travelToTexas(vehicle) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid type-checking (part 2) +### Yazım denetiminden kaçının (Bölüm 2) -If you are working with basic primitive values like strings and integers, -and you can't use polymorphism but you still feel the need to type-check, -you should consider using TypeScript. It is an excellent alternative to normal -JavaScript, as it provides you with static typing on top of standard JavaScript -syntax. The problem with manually type-checking normal JavaScript is that -doing it well requires so much extra verbiage that the faux "type-safety" you get -doesn't make up for the lost readability. Keep your JavaScript clean, write -good tests, and have good code reviews. Otherwise, do all of that but with -TypeScript (which, like I said, is a great alternative!). +Dizeler ve tamsayılar gibi temel ilkel değerlerle çalışıyorsanız, +ve polimorfizm kullanamazsınız, ancak yine de yazım denetimi yapma gereğini hissediyorsanız, TypeScript kullanmayı düşünmelisiniz. Standart JavaScript sentaks üstünde statik yazım sağladığı için normal JavaScript'e mükemmel bir alternatiftir. Manuel olarak normal JavaScript'i kontrol etmeyle ilgili sorun, iyi yapmak o kadar fazla ayrıntılı gerektirir ki, elde ettiğiniz sahte "tip güvenliği" kaybettiğiniz okunabilirliği telafi etmez. JavaScript'inizi temiz tutun, iyi testler yazın ve iyi kod incelemelerine sahip olun. Aksi takdirde, hepsini TypeScript ile yapın (dediğim gibi harika bir alternatif!). -**Bad:** +**Kötü:** ```javascript function combine(val1, val2) { @@ -961,7 +858,7 @@ function combine(val1, val2) { } ``` -**Good:** +**İyi:** ```javascript function combine(val1, val2) { @@ -969,27 +866,25 @@ function combine(val1, val2) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +### Aşırı optimizasyon yapma -### Don't over-optimize +Modern tarayıcılar çalışma zamanında çok sayıda optimizasyon yapar. Çoğu zaman, eğer optimize ediyorsanız, sadece zamanınızı boşa harcıyorsunuz demektir. -Modern browsers do a lot of optimization under-the-hood at runtime. A lot of -times, if you are optimizing then you are just wasting your time. [There are good -resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) -for seeing where optimization is lacking. Target those in the meantime, until -they are fixed if they can be. +Optimizasyonun nerede olmadığını görmek için [iyi kaynaklar](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) vardır. Bunları optimize edene kadar işaretleyebilirsiniz. -**Bad:** +**Kötü:** ```javascript -// On old browsers, each iteration with uncached `list.length` would be costly -// because of `list.length` recomputation. In modern browsers, this is optimized. +// Eski tarayıcılarda, önbelleğe alınmamış "list.length" içeren her yineleme maliyetli olacaktır +// list.length yeniden hesaplanması nedeniyle. Modern tarayıcılarda bu optimize edilmiştir. for (let i = 0, len = list.length; i < len; i++) { // ... } ``` -**Good:** +**İyi:** ```javascript for (let i = 0; i < list.length; i++) { @@ -997,15 +892,13 @@ for (let i = 0; i < list.length; i++) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Remove dead code +### Kullanılmayan kodları silin -Dead code is just as bad as duplicate code. There's no reason to keep it in -your codebase. If it's not being called, get rid of it! It will still be safe -in your version history if you still need it. +Ölü kod, yinelenen kod kadar kötü. Kod tabanınızda tutmak için bir neden yoktur. Eğer çağrılmazsa, ondan kurtulun! Hala ihtiyacınız varsa sürüm geçmişinizde güvende olacaktır. -**Bad:** +**Kötü:** ```javascript function oldRequestModule(url) { @@ -1020,7 +913,7 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**Good:** +**İyi:** ```javascript function newRequestModule(url) { @@ -1031,25 +924,21 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Objects and Data Structures** +## **Nesneler ve Veri Yapıları** -### Use getters and setters +### Alıcıları ve ayarlayıcıları kullanma (Getters & Setters) -Using getters and setters to access data on objects could be better than simply -looking for a property on an object. "Why?" you might ask. Well, here's an -unorganized list of reasons why: +Nesnelerdeki verilere erişmek için alıcıları ve ayarlayıcıları kullanmak, bir nesnedeki bir özelliği aramaktan daha iyi olabilir. "Neden?" sorabilirsiniz. Pekala, işte organize edilmeden nedenlerin bir listesi: -- When you want to do more beyond getting an object property, you don't have - to look up and change every accessor in your codebase. -- Makes adding validation simple when doing a `set`. -- Encapsulates the internal representation. -- Easy to add logging and error handling when getting and setting. -- You can lazy load your object's properties, let's say getting it from a - server. +- Bir nesne özelliği almanın ötesinde daha fazlasını yapmak istediğinizde, kod tabanınızdaki her erişimciye bakmanız ve değiştirmeniz gerekmez. +- `set`yaparken doğrulama basitçe yapılabilir. +- Dahili gösterimi içine alır. +- Alma ve ayarlama sırasında kayıtlamayı(logging) ve hata yönetimi(error handling) eklemek kolaydır. +- Diyelimki sunucudan alıyorsunuz, nesnenizin özelliklerini tembel(lazy load) olarak yükleyebilirsiniz. -**Bad:** +**Kötü:** ```javascript function makeBankAccount() { @@ -1065,19 +954,19 @@ const account = makeBankAccount(); account.balance = 100; ``` -**Good:** +**İyi:** ```javascript function makeBankAccount() { - // this one is private + // Bu fonksiyon özelinde (private) let balance = 0; - // a "getter", made public via the returned object below + // "alıcı"yı, genel(public) objeyi döndürerek yap function getBalance() { return balance; } - - // a "setter", made public via the returned object below + + // "ayarlayıcı"yı, genel(public) objeyi döndürerek yap function setBalance(amount) { // ... validate before updating the balance balance = amount; @@ -1094,13 +983,13 @@ const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Make objects have private members +### Nesnelerin özel üyeleri olmasını sağlama This can be accomplished through closures (for ES5 and below). -**Bad:** +**Kötü:** ```javascript const Employee = function(name) { @@ -1117,7 +1006,7 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**Good:** +**İyi:** ```javascript function makeEmployee(name) { @@ -1134,18 +1023,15 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Classes** +## **Sınıflar** -### Prefer ES2015/ES6 classes over ES5 plain functions +### ES5 düz fonksiyonlar yerine ES2015 / ES6 sınıflarını tercih et -It's very difficult to get readable class inheritance, construction, and method -definitions for classical ES5 classes. If you need inheritance (and be aware -that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over -classes until you find yourself needing larger and more complex objects. +Klasik ES5 sınıfları için okunabilir sınıf mirası, yapısı ve yöntem tanımları almak çok zordur. Miras almaya(inheritance) ihtiyacınız varsa (ve gerekmeyebileceğini unutmayın), ES2015 / ES6 sınıflarını tercih edin. Bununla birlikte, kendinizi daha büyük ve daha karmaşık nesnelere ihtiyaç duyana kadar küçük işlevlere tercih edin. -**Bad:** +**Kötü:** ```javascript const Animal = function(age) { @@ -1185,7 +1071,7 @@ Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ``` -**Good:** +**İyi:** ```javascript class Animal { @@ -1221,17 +1107,13 @@ class Human extends Mammal { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use method chaining +### Yöntem zincirini kullan -This pattern is very useful in JavaScript and you see it in many libraries such -as jQuery and Lodash. It allows your code to be expressive, and less verbose. -For that reason, I say, use method chaining and take a look at how clean your code -will be. In your class functions, simply return `this` at the end of every function, -and you can chain further class methods onto it. +Bu desen JavaScript'te çok kullanışlıdır ve jQuery ve Lodash gibi birçok kütüphanede görürsünüz. Kodunuzun etkileyici ve daha az detaylı olmasını sağlar. Bu nedenle, diyorum ki, yöntem zincirleme kullanın ve kodunuzun ne kadar temiz olacağını göreceksiniz. Sınıf fonksiyonlarınızda, her fonksiyonun sonunda `this`'i döndürmeniz yeterlidir ve daha fazla sınıf yöntemini buna zincirleyebilirsiniz. -**Bad:** +**Kötü:** ```javascript class Car { @@ -1263,7 +1145,7 @@ car.setColor("pink"); car.save(); ``` -**Good:** +**İyi:** ```javascript class Car { @@ -1275,25 +1157,25 @@ class Car { setMake(make) { this.make = make; - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } setModel(model) { this.model = model; - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } setColor(color) { this.color = color; - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } save() { console.log(this.make, this.model, this.color); - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } } @@ -1301,28 +1183,19 @@ class Car { const car = new Car("Ford", "F-150", "red").setColor("pink").save(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Prefer composition over inheritance +### Miras yerine kompozisyon tercih et -As stated famously in [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, -you should prefer composition over inheritance where you can. There are lots of -good reasons to use inheritance and lots of good reasons to use composition. -The main point for this maxim is that if your mind instinctively goes for -inheritance, try to think if composition could model your problem better. In some -cases it can. +Dörtlü çete tarafından başlatılan ünlü [_Tasarım Desenleri_](https://en.wikipedia.org/wiki/Design_Patterns) gibi, siz de kompozisyonu, miras bırakmaya yerine göre tercih etmelisiniz. Miras kullanmak için birçok iyi neden ve kompozisyon kullanmak için birçok iyi neden vardır. Bu maksimum nokta için ana nokta, zihniniz içgüdüsel olarak miras kullanma için giderse, kompozisyon sorununuzu daha iyi modelleyip değiştiremeyeceğini düşünmeye çalışın. Bazı durumlarda olabilir. -You might be wondering then, "when should I use inheritance?" It -depends on your problem at hand, but this is a decent list of when inheritance -makes more sense than composition: +O zaman "ne zaman miras kullanmalıyım?" diye merak ediyor olabilirsiniz. O eldeki probleminize bağlıdır, ancak bu mirasın kompozisyondan daha mantıklı olduğu iyi bir listedir: -1. Your inheritance represents an "is-a" relationship and not a "has-a" - relationship (Human->Animal vs. User->UserDetails). -2. You can reuse code from the base classes (Humans can move like all animals). -3. You want to make global changes to derived classes by changing a base class. - (Change the caloric expenditure of all animals when they move). +1. Mirasınız "has-a" değil, "is-a" ilişkisini temsil eder ilişki (İnsan-> Hayvan ve Kullanıcı-> KullanıcıAyrıntıları). +2. Temel sınıflardan kodu yeniden kullanabilirsiniz (İnsanlar tüm hayvanlar gibi hareket edebilir). +3. Temel sınıfı değiştirerek türetilmiş sınıflarda genel değişiklikler yapmak istiyorsunuz. (Hareket ettiklerinde tüm hayvanların kalori harcamalarını değiştirin). -**Bad:** +**Kötü:** ```javascript class Employee { @@ -1334,7 +1207,7 @@ class Employee { // ... } -// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee +// Kötü çünkü Çalışanların(Employees) vergi bilgisi 'var'. ÇalışanVergiBilgisi(EmployeeTaxData) bir çeşit Çalışan(Employee) değil. class EmployeeTaxData extends Employee { constructor(ssn, salary) { super(); @@ -1346,7 +1219,7 @@ class EmployeeTaxData extends Employee { } ``` -**Good:** +**İyi:** ```javascript class EmployeeTaxData { @@ -1371,22 +1244,15 @@ class Employee { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** ## **SOLID** -### Single Responsibility Principle (SRP) +### Tek Sorumluluk İlkesi (SRP) -As stated in Clean Code, "There should never be more than one reason for a class -to change". It's tempting to jam-pack a class with a lot of functionality, like -when you can only take one suitcase on your flight. The issue with this is -that your class won't be conceptually cohesive and it will give it many reasons -to change. Minimizing the amount of times you need to change a class is important. -It's important because if too much functionality is in one class and you modify -a piece of it, it can be difficult to understand how that will affect other -dependent modules in your codebase. +Temiz Kod'da belirtildiği gibi, "Bir sınıfın değişmesi için asla birden fazla sebep olmamalıdır". Bir sınıfı tıklım tıklım bir çok işlevsellikle doldurmak çekici gelebilir, tıpkı uçuşlarda yanına alabileceğiniz bir valiz gibi. Bununla ilgili sorun, sınıfınızın kavramsal olarak uyumlu olmayacağı ve değişmesi için birçok neden vereceği yönündedir. Bir sınıfı değiştirmek için ihtiyaç duyduğunuz sayıyı en aza indirmek önemlidir. Bir sınıfta çok fazla işlevsellik varsa 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. -**Bad:** +**Kötü:** ```javascript class UserSettings { @@ -1406,7 +1272,7 @@ class UserSettings { } ``` -**Good:** +**İyi:** ```javascript class UserAuth { @@ -1433,16 +1299,13 @@ class UserSettings { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Open/Closed Principle (OCP) +### Açık / Kapalı Prensibi (OCP) -As stated by Bertrand Meyer, "software entities (classes, modules, functions, -etc.) should be open for extension, but closed for modification." What does that -mean though? This principle basically states that you should allow users to -add new functionalities without changing existing code. +Bertrand Meyer tarafından belirtildiği gibi, "yazılım varlıkları (sınıflar, modüller, işlevler, vb.) Genişletme 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. -**Bad:** +**Kötü:** ```javascript class AjaxAdapter extends Adapter { @@ -1486,7 +1349,7 @@ function makeHttpCall(url) { } ``` -**Good:** +**İyi:** ```javascript class AjaxAdapter extends Adapter { @@ -1524,24 +1387,15 @@ class HttpRequester { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Liskov Substitution Principle (LSP) +### Liskov’un Yerine Geçme Prensibi (LSP) -This is a scary term for a very simple concept. It's formally defined as "If S -is a subtype of T, then objects of type T may be replaced with objects of type S -(i.e., objects of type S may substitute objects of type T) without altering any -of the desirable properties of that program (correctness, task performed, -etc.)." That's an even scarier definition. +Bu çok basit bir kavram için korkutucu bir terimdir.Resmi olarak "S, T'nin bir alt tipiyse, o zaman T tipi nesnelerin yerine S tipi nesneler (yani, S tipi nesneler T programındaki nesnelerin yerine geçebilir), bu programın istenen özelliklerini değiştirmeden değiştirilebilir (doğruluk, yapılan görev vb.) " Bu daha da korkunç bir tanım. -The best explanation for this is if you have a parent class and a child class, -then the base class and child class can be used interchangeably without getting -incorrect results. This might still be confusing, so let's take a look at the -classic Square-Rectangle example. Mathematically, a square is a rectangle, but -if you model it using the "is-a" relationship via inheritance, you quickly -get into trouble. +Bunun için en iyi açıklama, bir üst 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 elde etmeden birbirinin yerine kullanılabilir.Bu hala kafa karıştırıcı olabilir, bu yüzden klasik Kare Dikdörtgen örneğine bakalım. Matematiksel olarak, bir kare bir dikdörtgendir, ancak miras yoluyla "is-a" ilişkisini kullanarak model verirseniz, hızlı bir şekilde sorun yaşarsınız. -**Bad:** +**Kötü:** ```javascript class Rectangle { @@ -1587,7 +1441,7 @@ function renderLargeRectangles(rectangles) { rectangles.forEach(rectangle => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. + const area = rectangle.getArea(); // KÖTÜ: Kare için 25 değerini döndürür. 20 olmalı. rectangle.render(area); }); } @@ -1596,7 +1450,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**Good:** +**İyi:** ```javascript class Shape { @@ -1643,25 +1497,17 @@ const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Interface Segregation Principle (ISP) +### Arayüzlerin Ayrımı Prensibi (ISP) -JavaScript doesn't have interfaces so this principle doesn't apply as strictly -as others. However, it's important and relevant even with JavaScript's lack of -type system. +JavaScript'in arayüzleri yoktur, bu nedenle bu ilke diğerleri kadar kesin olarak geçerli değildir. Bununla birlikte, JavaScript'in tür sistemi eksikliğinde bile önemli ve alâkalıdır. -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. +ISP, "kullanıcılar kullanmadığı arabirimlere bağımlı olmaya zorlanmamalıdır." der. Arabirimler, `Duck Typing` yüzünden JavaScript'de üstü kapalı anlaşmalardır. -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". +Bu ilkeyi JavaScript'te gösteren iyi bir örnek, sınıflar büyük ayar nesneleri gerektirir. Kullanıcıların büyük miktarda seçenek ayarlamalarını istememek gerekli değildir, çünkü çoğu zaman tüm ayarlara ihtiyaç duymazlar. Bunları isteğe bağlı yapmak, bir "büyük arayüzü" olmasını önlemeye yardımcı olur. -**Bad:** +**Kötü:** ```javascript class DOMTraverser { @@ -1682,12 +1528,12 @@ class DOMTraverser { const $ = new DOMTraverser({ rootNode: document.getElementsByTagName("body"), - animationModule() {} // Most of the time, we won't need to animate when traversing. + animationModule() {} // Coğunlukla, bunu canlandırmamız gerekmeyecek // ... }); ``` -**Good:** +**İyi:** ```javascript class DOMTraverser { @@ -1721,32 +1567,24 @@ const $ = new DOMTraverser({ }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Dependency Inversion Principle (DIP) +### Bağlılığı Tersine Çevirme Prensibi (DIP) -This principle states two essential things: +Bu ilke iki temel şeyi ifade eder: -1. High-level modules should not depend on low-level modules. Both should - depend on abstractions. -2. Abstractions should not depend upon details. Details should depend on - abstractions. +1. Yüksek seviyeli modüller, düşük seviyeli modüllere bağlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır. +2. Soyutlamalar detaylara bağlı olmamalıdır. Ayrıntılar soyutlamalara bağlı olmalıdır. -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. +İlk başta bunu anlamak zor olabilir, ancak AngularJS ile çalıştıysanız, bu prensibin Bağımlılık Enjeksiyonu (DI) şeklinde bir uygulamasını gördünüz. Aynı kavramlar olmasalar da, DIP yüksek seviyede modüllerin düşük seviyeli modüllerinin detaylarını bilmelerini ve ayarlamalarını sağlar. +Bunu DI ile başarabilir. Bunun büyük bir yararı, modüller arasındaki bağlantıyı azaltmasıdır. Eşleştirme(Coupling) çok kötü bir gelişme modelidir çünkü +kodunuzu yeniden düzenleme zorlaştırır. -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. +Daha önce belirtildiği gibi, JavaScript'in arayüzleri yoktur, bu nedenle soyutlamalar örtük sözleşmelere bağlıdır. +Yani, bir nesnenin / sınıfın başka bir nesneye / sınıfa maruz bıraktığı yöntemler ve özellikler. +Aşağıdaki örnekte, örtük sözleşme, bir `InventoryTracker` için herhangi bir Request modülünün `requestItems` yöntemine sahip olacağıdır. -**Bad:** +**Kötü:** ```javascript class InventoryRequester { @@ -1763,8 +1601,8 @@ 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` + // KÖTÜ: Belirli bir istek uygulamasına bağımlılık yarattık. +    // requestItems sadece `request` 'e bağlımlı olmalıdır. this.requester = new InventoryRequester(); } @@ -1779,7 +1617,7 @@ const inventoryTracker = new InventoryTracker(["apples", "bananas"]); inventoryTracker.requestItems(); ``` -**Good:** +**İyi:** ```javascript class InventoryTracker { @@ -1815,8 +1653,8 @@ class InventoryRequesterV2 { } } -// By constructing our dependencies externally and injecting them, we can easily -// substitute our request module for a fancy new one that uses WebSockets. +// Bağımlılıklarımızı harici olarak yapılandırarak ve enjekte ederek +// istek modülümüzü WebSockets kullanan yeni ve havalı bir modülle kolayca değiştirebiliriz. const inventoryTracker = new InventoryTracker( ["apples", "bananas"], new InventoryRequesterV2() @@ -1824,28 +1662,19 @@ const inventoryTracker = new InventoryTracker( inventoryTracker.requestItems(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +## **Testler** -## **Testing** +Test etmek canlıya çıkmaktan bile önemlidir. Eğer testiniz yoksa veya yetersiz bir miktardaysa, kodu her gönderdiğinizde hiçbir şeyin bozulmadığına emin olmazsınız. Neyin yeterli bir miktar oluşturduğuna karar vermek takımınıza bağlıdır, %100 kapsama sahip olmak (tüm ifadeler ve şubeler) size güven ve gönül rahatlığı sağlar. Bu, harika bir test framework'üne sahip olmanın yanı sıra, [iyi bir kapsama aracı](https://gotwarlost.github.io/istanbul/) kullanmanız gerektiği anlamına gelir -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](https://gotwarlost.github.io/istanbul/). +Test yazmamanın mazereti yoktur. Çok sayıda [iyi JS test framework'ü](https://jstherightway.org/#testing-tools) vardır, bu yüzden ekibinize uyan birini bulun. -There's no excuse to not write tests. There are [plenty of good JS test frameworks](https://jstherightway.org/#testing-tools), 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. +Ekibiniz için uygun olanı bulduğunuzda, kullanılan her yeni özellik / modül için daima testler yazmayı hedefleyin. Tercih ettiğiniz yöntem Test Odaklı Geliştirme (TDD) ise, bu harika, ancak asıl mesele, herhangi bir özelliği başlatmadan veya mevcut bir özelliği yeniden düzenlemeden önce kapsama hedeflerinize ulaştığınızdan emin olmaktır. -### Single concept per test +### Her test için tek konsept -**Bad:** +**Kötü:** ```javascript import assert from "assert"; @@ -1869,7 +1698,7 @@ describe("MomentJS", () => { }); ``` -**Good:** +**İyi:** ```javascript import assert from "assert"; @@ -1895,16 +1724,15 @@ describe("MomentJS", () => { }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Concurrency** +## **Tutarlılık** -### Use Promises, not callbacks +### Promises kullanın, callback değil -Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, -Promises are a built-in global type. Use them! +'Callback'ler temiz değildir ve aşırı miktarda iç içe geçmeye neden olurlar. ES2015 / ES6 ile Promise'ler yerleşik bir global tiptir. Onları kullan! -**Bad:** +**Kötü:** ```javascript import { get } from "request"; @@ -1928,7 +1756,7 @@ get( ); ``` -**Good:** +**İyi:** ```javascript import { get } from "request-promise"; @@ -1946,17 +1774,13 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Async/Await are even cleaner than Promises +### Async/Await kullanmak Promis kullanmaktan bile daha temiz -Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await -which offer an even cleaner solution. All you need is a function that is prefixed -in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features -today! +Promise'ler callback'lere göte çok daha temiz alternatiflerdir, ama ES2017/ES8 async ve await getirdi ve bu daha temiz bir çözüm sunuyor. Tek yapmanız gereken fonksiyonun başına `async` eklemek ve sonrasında mantıksal kullanımızı `then` zinciri kulanmadan yazabilirsiniz. Bugün ES2017 / ES8 özelliklerinden yararlanabiliyorsanız bunu kullanın! -**Bad:** +**Kötü:** ```javascript import { get } from "request-promise"; @@ -1974,7 +1798,7 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**Good:** +**İyi:** ```javascript import { get } from "request-promise"; @@ -1995,25 +1819,21 @@ async function getCleanCodeArticle() { getCleanCodeArticle() ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +## **Hata Yönetimi** + +Atılan hatalar iyi bir şey! Bunlar, programınızdaki bir şey yanlış gittiğinde çalışma zamanının başarıyla tanımlandığı anlamına gelir ve sizi geçerli yığında (stack) fonksiyonu çalıştırmayı, işlevi dururup size konsolda yığın izlemede haber vererek yapar. -## **Error Handling** +--- HERE -Thrown errors are a good thing! They mean the runtime has successfully -identified when something in your program has gone wrong and it's letting -you know by stopping function execution on the current stack, killing the -process (in Node), and notifying you in the console with a stack trace. +### Yakalanmış hataları görmemezlikten gelmeyin -### Don't ignore caught errors +Yakalanan bir hatayla hiçbir şey yapmamanız size söz konusu hatayı düzeltebilme veya tepki gösterme yeteneği vermez. Hatayı konsola (`console.log`) kaydetmek, konsola yazdırılan bir şey denizinde kaybolabileceği sıklıkta daha iyi değildir. -Doing nothing with a caught error doesn't give you the ability to ever fix -or react to said error. Logging the error to the console (`console.log`) -isn't much better as often times it can get lost in a sea of things printed -to the console. If you wrap any bit of code in a `try/catch` it means you -think an error may occur there and therefore you should have a plan, -or create a code path, for when it occurs. +Herhangi bir kod parçasını `try/catch` içerisinde kullanıyorsanız, orada bir hata olabileceğini düşündüğünüz anlamına gelir ve bu nedenle gerçekleştiği zaman için bir planınız olması veya bir kod yolu oluşturmanız gerekir. -**Bad:** +**Kötü:** ```javascript try { @@ -2023,28 +1843,27 @@ try { } ``` -**Good:** +**İyi:** ```javascript try { functionThatMightThrow(); } catch (error) { - // One option (more noisy than console.log): + // Bir secenek (console.log'dan daha dikkat çekici) console.error(error); - // Another option: + // Bir secenek daha notifyUserOfError(error); - // Another option: + // Bir secenek daha reportErrorToService(error); - // OR do all three! + // veya hepsini bir yapın } ``` -### Don't ignore rejected promises +### Reddedilmiş promisleri görmemezlikten gelmeyin -For the same reason you shouldn't ignore caught errors -from `try/catch`. +Aynı sebeplerden dolayı, `try/catch`'de oluşan hataları yok saymamalısınız -**Bad:** +**Kötü:** ```javascript getdata() @@ -2056,7 +1875,7 @@ getdata() }); ``` -**Good:** +**İyi:** ```javascript getdata() @@ -2064,36 +1883,29 @@ getdata() functionThatMightThrow(data); }) .catch(error => { - // One option (more noisy than console.log): + // Bir secenek (console.log'dan daha dikkat çekici) console.error(error); - // Another option: + // Bir secenek daha notifyUserOfError(error); - // Another option: + // Bir secenek daha reportErrorToService(error); - // OR do all three! + // veya hepsini bir yapın }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Formatting** +## **Biçimlendirme** -Formatting is subjective. Like many rules herein, there is no hard and fast -rule that you must follow. The main point is DO NOT ARGUE over formatting. -There are [tons of tools](https://standardjs.com/rules.html) to automate this. -Use one! It's a waste of time and money for engineers to argue over formatting. +Biçimlendirme özneldir. Buradaki birçok kural gibi, uygulamanız gereken zor ve hızlı bir kural yoktur. Ana nokta biçimlendirme üzerinde tartışma DEĞİLDİR. Bunu otomatikleştirmek için [tonlarca araç](https://standardjs.com/rules.html) vardır. Birini kullan! Mühendislerin biçimlendirme konusunda tartışmaları zaman ve para kaybıdır. -For things that don't fall under the purview of automatic formatting -(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here -for some guidance. +Otomatik biçimlendirme (girintileme, sekmeler ve boşluklar, çift veya tek tırnak işaretleri vb.) Kapsamına girmeyen şeyler için bazı rehberlik için buraya bakın. -### Use consistent capitalization +### Tutarlı büyük harf kullanımı -JavaScript is untyped, so capitalization tells you a lot about your variables, -functions, etc. These rules are subjective, so your team can choose whatever -they want. The point is, no matter what you all choose, just be consistent. +JavaScript türsüzdür, bu nedenle büyük / küçük harf kullanımı değişkenleriniz, işlevleriniz vb. Hakkında çok şey anlatır. Bu kurallar özneldir, böylece ekibiniz istediklerini seçebilir. Mesele şu ki, ne seçerseniz seçin, tutarlı olun. -**Bad:** +**Kötü:** ```javascript const DAYS_IN_WEEK = 7; @@ -2109,7 +1921,7 @@ class animal {} class Alpaca {} ``` -**Good:** +**İyi:** ```javascript const DAYS_IN_WEEK = 7; @@ -2125,15 +1937,13 @@ class Animal {} class Alpaca {} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Function callers and callees should be close +### Fonksion çağıranları ve çağrılanları yakın olmalı -If a function calls another, keep those functions vertically close in the source -file. Ideally, keep the caller right above the callee. We tend to read code from -top-to-bottom, like a newspaper. Because of this, make your code read that way. +Eğer bir fonksiyon diğer fonksiyonu çağırıyorsa, dikey olarak bu fonksiyonları kaynak dosyasında yakın tutun. İdeal olan, fonksiyonu kullanan kullandığı fonksiyonun hemen üstünde olmasıdır. We tend to read code from top-to-bottom, like a newspaper.Bu nedenle, kodunuzu bu şekilde okuyun. -**Bad:** +**Kötü:** ```javascript class PerformanceReview { @@ -2173,7 +1983,7 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**Good:** +**İyi:** ```javascript class PerformanceReview { @@ -2213,37 +2023,37 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Comments** +## **Yorumlar** -### Only comment things that have business logic complexity. +### Yalnızca iş mantığı karmaşıklığı olan şeyleri yorumlayın -Comments are an apology, not a requirement. Good code _mostly_ documents itself. +Yorumlar aslında bir özür, şart değil. İyi kod _çoğunlukla_ kendini belgelemektedir. -**Bad:** +**Kötü:** ```javascript function hashIt(data) { - // The hash + // Karma let hash = 0; - // Length of string + // String uzunluğu const length = data.length; - // Loop through every character in data + // Verilerdeki her karakteri gözden geçirin for (let i = 0; i < length; i++) { - // Get character code. + // Karakter kodunu al const char = data.charCodeAt(i); - // Make the hash + // Karıştır hash = (hash << 5) - hash + char; - // Convert to 32-bit integer + // 32-bit tam sayıya dönüştür hash &= hash; } } ``` -**Good:** +**İyi:** ```javascript function hashIt(data) { @@ -2254,19 +2064,19 @@ function hashIt(data) { const char = data.charCodeAt(i); hash = (hash << 5) - hash + char; - // Convert to 32-bit integer + // 32-bit tam sayıya dönüştür hash &= hash; } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't leave commented out code in your codebase +### Kodlarınızı yorum olarak bırakmayın -Version control exists for a reason. Leave old code in your history. +Versiyon kontrol'un var olmasının bir sebebi var. Eski kodlarınızı tarihin tozlu sayfalarında bırakın. -**Bad:** +**Kötü:** ```javascript doStuff(); @@ -2275,20 +2085,19 @@ doStuff(); // doSoMuchStuff(); ``` -**Good:** +**İyi:** ```javascript doStuff(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't have journal comments +### Günlük yorumları yapmayın -Remember, use version control! There's no need for dead code, commented code, -and especially journal comments. Use `git log` to get history! +Hatırlayın, versiyon kontrol kullanın! Kullanılmayan koda, yoruma alınmış koda ve özellikle günlük kodlarına gerek yok. Geçmiş için `git log` kullanın. -**Bad:** +**Kötü:** ```javascript /** @@ -2302,7 +2111,7 @@ function combine(a, b) { } ``` -**Good:** +**İyi:** ```javascript function combine(a, b) { @@ -2310,14 +2119,13 @@ function combine(a, b) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid positional markers +### Yer belirleyicilerden kaçının -They usually just add noise. Let the functions and variable names along with the -proper indentation and formatting give the visual structure to your code. +Bunlar genellikle kirlilik yaratır. Fonksiyonların ve değişken adlarının yanı sıra uygun girinti ve biçimlendirme kodunuza görsel yapı kazandırsın. -**Bad:** +**Kötü:** ```javascript //////////////////////////////////////////////////////////////////////////////// @@ -2336,7 +2144,7 @@ const actions = function() { }; ``` -**Good:** +**İyi:** ```javascript $scope.model = { @@ -2349,11 +2157,11 @@ const actions = function() { }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## Translation +## Çeviriler -This is also available in other languages: +Ayrıca diğer dillerde de: - ![am](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Armenia.png) **Armenian**: [hanumanum/clean-code-javascript/](https://github.com/hanumanum/clean-code-javascript) - ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) @@ -2374,6 +2182,7 @@ This is also available in other languages: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) +- ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** From 87185d9b797a1de7d48902f6561a20f79a64c8e3 Mon Sep 17 00:00:00 2001 From: Burak Sonmez Date: Thu, 26 Mar 2020 08:42:09 +0300 Subject: [PATCH 156/170] Revert "turkish added" This reverts commit 2edfeef74002f7aeaa15a7bea4adb047b85dd539. --- README.md | 901 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 546 insertions(+), 355 deletions(-) diff --git a/README.md b/README.md index d7747ff9..bbe7a083 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,69 @@ # clean-code-javascript -## İçindekiler +## Table of Contents -1. [Giriş](#Giriş) -2. [Değişkenler](#Değişkenler) -3. [Fonksiyonlar](#Fonksiyonlar) -4. [Nesneler ve Veri Yapıları](#nesneler-ve-veri-yapıları) -5. [Sınıflar (Class)](#sınıflar) +1. [Introduction](#introduction) +2. [Variables](#variables) +3. [Functions](#functions) +4. [Objects and Data Structures](#objects-and-data-structures) +5. [Classes](#classes) 6. [SOLID](#solid) -7. [Testler](#testler) -8. [Tutarlılık](#Tutarlılık) -9. [Hata Yönetimi](#Hata-Yönetimi) -10. [Biçimlendirme](#Biçimlendirme) -11. [Yorumlar](#yorumlar) -12. [Çeviriler](#çeviriler) +7. [Testing](#testing) +8. [Concurrency](#concurrency) +9. [Error Handling](#error-handling) +10. [Formatting](#formatting) +11. [Comments](#comments) +12. [Translation](#translation) -## Giriş +## Introduction -![Kod okurken kaç defa bağıracağınızın bir sayısı olarak yazılım kalite tahmininin görüntüsü](https://www.osnews.com/images/comics/wtfm.jpg) +![Humorous image of software quality estimation as a count of how many expletives +you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg) -Robert C. Martin'nın kitabı olan ve yazılım mühendisliği ilkerini barındıran [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)'un JavaScript için uyarlanmış hali. Bu bir tarz rehberi değil. JavaScript'te [okunabilir, yeniden kullanılabilir ve yeniden düzenlenebilir](https://github.com/ryanmcdermott/3rs-of-software-architecture) yazılımlar üretmeye yönelik bir kılavuzdur. +Software engineering principles, from Robert C. Martin's book +[_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), +adapted for JavaScript. This is not a style guide. It's a guide to producing +[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. -Buradaki her ilkeye kesinlikle uyulmak zorunda değildir ve daha azı evrensel olarak kabul edilecektir. Bunlar önerilerden başka bir şey değildir, fakat bunlar _Clean Code_ yazarları tarafından uzun yıllar süren tecrübeler ile kodlandı. +Not every principle herein has to be strictly followed, and even fewer will be +universally agreed upon. These are guidelines and nothing more, but they are +ones codified over many years of collective experience by the authors of +_Clean Code_. -Yazılım mühendisliği zanaatımız 50 yaşın biraz üzerinde ve hala çok şey öğreniyoruz. Yazılım mimarisi, mimarinin kendisi kadar eski olduğunda, belki de takip edilmesi daha zor kurallarımız olacak. Şimdi, bu önerilerin sizin ve ekibinizin ürettiği JavaScript kodunun kalitesini değerlendirmek için bir mihenk taşı işlevi görmesine izin verin. +Our craft of software engineering is just a bit over 50 years old, and we are +still learning a lot. When software architecture is as old as architecture +itself, maybe then we will have harder rules to follow. For now, let these +guidelines serve as a touchstone by which to assess the quality of the +JavaScript code that you and your team produce. -Bir şey daha: bunların bilinmesi sizi hemen daha iyi bir yazılım geliştiricisi yapmaz ve uzun yıllar onlarla çalışmak hata yapmayacağınız anlamına gelmez. +One more thing: knowing these won't immediately make you a better software +developer, and working with them for many years doesn't mean you won't make +mistakes. Every piece of code starts as a first draft, like wet clay getting +shaped into its final form. Finally, we chisel away the imperfections when +we review it with our peers. Don't beat yourself up for first drafts that need +improvement. Beat up the code instead! -Her kod parçası ilk taslak olarak başlar, örneğin ıslak kil son halini alır. Son olarak, akranlarımızla gözden geçirdiğimizde kusurları kesiyoruz. İyileştirilmesi gereken ilk taslaklar için kendinize yüklenmeyin. Onun yerine kodunuza yüklenin. +## **Variables** -## **Değişkenler** +### Use meaningful and pronounceable variable names -### Anlamlı ve belirgin değişken adları kullanın - -**Kötü:** +**Bad:** ```javascript const yyyymmdstr = moment().format("YYYY/MM/DD"); ``` -**İyi:** +**Good:** ```javascript const currentDate = moment().format("YYYY/MM/DD"); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Aynı değişken türü için aynı kelimeleri kullanın +### Use the same vocabulary for the same type of variable -**Kötü:** +**Bad:** ```javascript getUserInfo(); @@ -57,41 +71,45 @@ getClientData(); getCustomerRecord(); ``` -**İyi:** +**Good:** ```javascript getUser(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Aranabilir isimler kullanın +### Use searchable names -Yazacağımızdan daha fazla kod okuyacağız. Yazdığımız kodun okunabilir ve aranabilir olması önemlidir. Değişkenleri anlamlı ve anlaşılabilir _isimlendirmemekle_, okuyucuya zarar veriyoruz. -Adlarınızı aranabilir hale getirin. [buddy.js](https://github.com/danielstjules/buddy.js) ve -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) gibi araçlar isimlendirilmemiş sabitlerinizi belirlemenize yardımcı olacaktır. +We will read more code than we will ever write. It's important that the code we +do write is readable and searchable. By _not_ naming variables that end up +being meaningful for understanding our program, we hurt our readers. +Make your names searchable. Tools like +[buddy.js](https://github.com/danielstjules/buddy.js) and +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) +can help identify unnamed constants. -**Kötü:** +**Bad:** ```javascript -// 86400000 neydi ? +// What the heck is 86400000 for? setTimeout(blastOff, 86400000); ``` -**İyi:** +**Good:** ```javascript -// Bunları büyük harfle adlandırılmış sabitler olarak bildirin. +// Declare them as capitalized named constants. const MILLISECONDS_IN_A_DAY = 86_400_000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Açıklayıcı değişkenler kullan +### Use explanatory variables -**Kötü:** +**Bad:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -102,7 +120,7 @@ saveCityZipCode( ); ``` -**İyi:** +**Good:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -111,13 +129,13 @@ const [_, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kafadan planmaktan kaçının +### Avoid Mental Mapping -Açık olmak, ima etmekten iyidir. +Explicit is better than implicit. -**Kötü:** +**Bad:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -127,12 +145,12 @@ locations.forEach(l => { // ... // ... // ... - // Bir dakika, 'l' neydi? + // Wait, what is `l` for again? dispatch(l); }); ``` -**İyi:** +**Good:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -146,13 +164,14 @@ locations.forEach(location => { }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Gereksiz içerikler eklemeyin +### Don't add unneeded context -Sınıf / nesne adınız size bir şey söylüyorsa, bunu değişken adınızda tekrarlamayın. +If your class/object name tells you something, don't repeat that in your +variable name. -**Kötü:** +**Bad:** ```javascript const Car = { @@ -166,7 +185,7 @@ function paintCar(car) { } ``` -**İyi:** +**Good:** ```javascript const Car = { @@ -180,13 +199,16 @@ function paintCar(car) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kısa devre veya şartlar yerine önceden tanımlanmış argümanlar kullanın +### Use default arguments instead of short circuiting or conditionals -Önceden tanımlanan argümanlar genellikle kısa devrelere göre daha temizdir. Bunları kullanırsanız şunun farkında olun, fonksiyonunuz sadece `tanımsız` _(undefined)_ değerler için önceden tanımlanmış argümana kullanacaktır. `''`, `""`, `false`, `null`, `0`, ve `NaN` gibi "yanlış denilebilecek" değerler önceden tanımlanmış bir değerle değiştirilmez. +Default arguments are often cleaner than short circuiting. Be aware that if you +use them, your function will only provide default values for `undefined` +arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and +`NaN`, will not be replaced by a default value. -**Kötü:** +**Bad:** ```javascript function createMicrobrewery(name) { @@ -195,7 +217,7 @@ function createMicrobrewery(name) { } ``` -**İyi:** +**Good:** ```javascript function createMicrobrewery(name = "Hipster Brew Co.") { @@ -203,27 +225,41 @@ function createMicrobrewery(name = "Hipster Brew Co.") { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Fonksiyonlar** +## **Functions** -### Fonksiyonlar argümanları (Tercihen 2 veya daha az) +### Function arguments (2 or fewer ideally) -Fonksiyon parametrelerinin sayısını sınırlamak inanılmaz derecede önemlidir, çünkü fonksiyonunuzu test etmeyi kolaylaştırır. Üçten fazlasına sahip olmak, her bir argümanla tonlarca farklı vakayı test etmeniz gereken bir kombinasyonel patlamaya yol açar. +Limiting the amount of function parameters is incredibly important because it +makes testing your function easier. Having more than three leads to a +combinatorial explosion where you have to test tons of different cases with +each separate argument. -Bir veya iki argüman ideal durumdur ve mümkünse üç tanesinden kaçınılmalıdır. Bundan daha fazlası birleştirilmeldir. Genellikle, -ikiden fazla argüman sonra fonksiyonunuz çok işlem yapmaya çalışıyor. Olmadığı durumlarda, çoğu zaman daha üst düzey bir nesne argüman olarak yeterli olacaktır. +One or two arguments is the ideal case, and three should be avoided if possible. +Anything more than that should be consolidated. Usually, if you have +more than two arguments then your function is trying to do too much. In cases +where it's not, most of the time a higher-level object will suffice as an +argument. -JavaScript havada nesne yapmanıza olanak sağladığı için, bir çok sınıf yapısına gerek kalmadan, bir nesneyi birden fazla nesne kullanmadan kullanabilirsiniz. +Since JavaScript allows you to make objects on the fly, without a lot of class +boilerplate, you can use an object if you are finding yourself needing a +lot of arguments. -Fonksiyonun hangi özellikleri beklediğini netleştirmek için ES2015 / ES6 ayrıştırma sintaksını kullanabilirsiniz. Bunun birkaç avantajı vardır: +To make it obvious what properties the function expects, you can use the ES2015/ES6 +destructuring syntax. This has a few advantages: -1. Birisi fonksiyonun imzasına baktığında, hangi özelliklerin kullanıldığını hemen anlar.. -2. Adlandırılmış parametreleri simüle etmek için kullanılabilir. -3. Ayrıştırma işlemi ayrıca fonksiyona iletilen argüman nesnesinin belirtilen ilk değerlerini de klonlar. Bu, yan etkilerin önlenmesine yardımcı olabilir. Not: Argüman nesnelerinden ayrıştırılan nesneler ve diziler klonlanmaz! -4. Linters, sizi kullanılmayan değerler için uyarabilir bu da ayrıştırmadan imkansız olurdu. +1. When someone looks at the function signature, it's immediately clear what + properties are being used. +2. It can be used to simulate named parameters. +3. Destructuring also clones the specified primitive values of the argument + object passed into the function. This can help prevent side effects. Note: + objects and arrays that are destructured from the argument object are NOT + cloned. +4. Linters can warn you about unused properties, which would be impossible + without destructuring. -**Kötü:** +**Bad:** ```javascript function createMenu(title, body, buttonText, cancellable) { @@ -234,7 +270,7 @@ createMenu("Foo", "Bar", "Baz", true); ``` -**İyi:** +**Good:** ```javascript function createMenu({ title, body, buttonText, cancellable }) { @@ -249,13 +285,17 @@ createMenu({ }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyonlar bir şey yapmalı +### Functions should do one thing -Bu, yazılım mühendisliğinde açık ara en önemli kuraldır. Fonksiyonlar birden fazla şey yaptığında, birleştirmesi, test etmesi ve anlamdırılması daha zordur. Bir fonksiyonu yalnızca bir eyleme ayırabildiğinizde, kolayca yeniden düzenlenebilir ve kodunuz çok daha temiz okunur. Bu yazıdan başka bir şey almazsanız dahi sadece bununla birçok geliştiricinin önünde olacaksınız. +This is by far the most important rule in software engineering. When functions +do more than one thing, they are harder to compose, test, and reason about. +When you can isolate a function to just one action, it can be refactored +easily and your code will read much cleaner. If you take nothing else away from +this guide other than this, you'll be ahead of many developers. -**Kötü:** +**Bad:** ```javascript function emailClients(clients) { @@ -268,7 +308,7 @@ function emailClients(clients) { } ``` -**İyi:** +**Good:** ```javascript function emailActiveClients(clients) { @@ -281,11 +321,11 @@ function isActiveClient(client) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyon isimleri ne yaptıklarını anlatmalı +### Function names should say what they do -**Kötü:** +**Bad:** ```javascript function addToDate(date, month) { @@ -294,11 +334,11 @@ function addToDate(date, month) { const date = new Date(); -// Fonksiyonun adından ne eklendiğini söylemek zor +// It's hard to tell from the function name what is added addToDate(date, 1); ``` -**İyi:** +**Good:** ```javascript function addMonthToDate(month, date) { @@ -309,13 +349,15 @@ const date = new Date(); addMonthToDate(1, date); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyonlar soyutlaştırmadan sadece bir seviye uzak olmalıdır +### Functions should only be one level of abstraction -Birden fazla soyutlama seviyeniz olduğunda fonksiyonunuz genellikle çok fazla şey yapar. Fonksiyonların bölünmesi, yeniden kullanılabilirliği ve daha kolay test yapılmasını sağlayacak. +When you have more than one level of abstraction your function is usually +doing too much. Splitting up functions leads to reusability and easier +testing. -**Kötü:** +**Bad:** ```javascript function parseBetterJSAlternative(code) { @@ -342,7 +384,7 @@ function parseBetterJSAlternative(code) { } ``` -**İyi:** +**Good:** ```javascript function parseBetterJSAlternative(code) { @@ -379,19 +421,32 @@ function parse(tokens) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Tekrarlanan kodu sil +### Remove duplicate code -Tekrarlanan kodlardan kaçınmak için elinizden geleni yapın. Tekrarlanan kod kötü çünkü bazı mantığı değiştirmeniz gerekirse bir şeyi değiştirmek için birden fazla yer olduğu anlamına geliyor. +Do your absolute best to avoid duplicate code. Duplicate code is bad because it +means that there's more than one place to alter something if you need to change +some logic. -Bir restoran işlettiğinizi ve stoklarınızı takip ettiğinizi düşünün: tüm domates, soğan, sarımsak, baharat, vb. onlara. Yalnızca bir listeniz varsa, güncellenecek tek bir yer vardır! +Imagine if you run a restaurant and you keep track of your inventory: all your +tomatoes, onions, garlic, spices, etc. If you have multiple lists that +you keep this on, then all have to be updated when you serve a dish with +tomatoes in them. If you only have one list, there's only one place to update! -Çoğunlukla yinelenen kodunuz vardır, çünkü çoğu şeyi ortak paylaşsalar dahi çok küçük bir kaç farklılık vardır, ancak farklılıkları sizi aynı şeylerin çoğunu yapan iki veya daha fazla ayrı fonksiyona sahip olmaya zorlar. Tekrarlanan kodun kaldırılması, bu farklı şeyleri tek bir fonksiyon / modül / sınıfla işleyebilecek bir soyutlama oluşturmak anlamına gelir. +Oftentimes you have duplicate code because you have two or more slightly +different things, that share a lot in common, but their differences force you +to have two or more separate functions that do much of the same things. Removing +duplicate code means creating an abstraction that can handle this set of +different things with just one function/module/class. -Soyutlamayı doğru yapmak kritik öneme sahiptir, bu yüzden _Sınıflar_ bölümünde belirtilen SOLID ilkelerini izlemelisiniz. Kötü soyutlamalar yinelenen koddan daha kötü olabilir, bu yüzden dikkatli olun! Bunu söyledikten sonra, eğer iyi bir soyutlama yapabilirseniz, yapın! Kendinizi tekrarlamayın, aksi takdirde bir şeyi değiştirmek istediğinizde kendinizi birden fazla yeri güncellerken bulacaksınız. +Getting the abstraction right is critical, that's why you should follow the +SOLID principles laid out in the _Classes_ section. Bad abstractions can be +worse than duplicate code, so be careful! Having said this, if you can make +a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself +updating multiple places anytime you want to change one thing. -**Kötü:** +**Bad:** ```javascript function showDeveloperList(developers) { @@ -425,7 +480,7 @@ function showManagerList(managers) { } ``` -**İyi:** +**Good:** ```javascript function showEmployeeList(employees) { @@ -452,11 +507,11 @@ function showEmployeeList(employees) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Object.assign ile varsayılan nesneleri ayarlama +### Set default objects with Object.assign -**Kötü:** +**Bad:** ```javascript const menuConfig = { @@ -477,12 +532,12 @@ function createMenu(config) { createMenu(menuConfig); ``` -**İyi:** +**Good:** ```javascript const menuConfig = { title: "Order", - // Kullanıcı 'body' eklemedi + // User did not include 'body' key buttonText: "Send", cancellable: true }; @@ -498,20 +553,20 @@ function createMenu(config) { config ); - // config çıktısı şimdi : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyon parametrelerinde işaretleme kullanma +### Don't use flags as function parameters -İşaretlemeler fonksiyonunuzun birden fazla şey yaptığını gösterir. Fonkisyonlar sadece tek şey yapmalılar. Eğer aşağıdakine benzer değişiklere ve mantıksal operatorlere sahip fonksiyonunuz varsa fonksiyonlarınızı ayırın. +Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. -**Kötü:** +**Bad:** ```javascript function createFile(name, temp) { @@ -523,7 +578,7 @@ function createFile(name, temp) { } ``` -**İyi:** +**Good:** ```javascript function createFile(name) { @@ -535,21 +590,30 @@ function createTempFile(name) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yan Etkilerden Kaçının (Bölüm 1) +### Avoid Side Effects (part 1) -Bir fonksiyon, bir değeri alıp başka bir değer veya değer döndürmekten başka bir şey yaparsa yan etki üretir. Bir yan etki, bir dosyaya yazma, bazı global değişkeni değiştirme veya yanlışlıkla tüm paranızı bir yabancıya gönderme olabilir. +A function produces a side effect if it does anything other than take a value in +and return another value or values. A side effect could be writing to a file, +modifying some global variable, or accidentally wiring all your money to a +stranger. -Şimdi, zaman zaman bir programda yan etkilere sahip olmanız gerekir. Önceki örnekte olduğu gibi, bir dosyaya yazmanız gerekebilir. Yapmak istediğiniz, bunu yapacağınız yeri merkezileştirmektir. Birden fazla yazma işlemi yapan fonksiyonlar veya sınıflar yapmayın. Bunu yapan bir servisiniz olsun. Bir ve sadece bir. +Now, you do need to have side effects in a program on occasion. Like the previous +example, you might need to write to a file. What you want to do is to +centralize where you are doing this. Don't have several functions and classes +that write to a particular file. Have one service that does it. One and only one. -Buradaki ana nokta, herhangi bir yapıya sahip olmayan nesneler arasında durumu(state) paylaşmak, herhangi bir şeyle yazılabilen değiştirilebilir(mutable) veri türlerini kullanmak ve yan etkilerinizin nerede ortaya çıktığını merkezileştirmemek gibi yaygın tuzaklardan kaçınmaktır. Bunu yapabilirseniz, diğer programcıların büyük çoğunluğundan daha mutlu olacaksınız. +The main point is to avoid common pitfalls like sharing state between objects +without any structure, using mutable data types that can be written to by anything, +and not centralizing where your side effects occur. If you can do this, you will +be happier than the vast majority of other programmers. -**Kötü:** +**Bad:** ```javascript -// Aşağıdaki fonksiyon Global değişkeni refere alıyor -// Bu adı kullanan başka bir fonksiyonumuz olsaydı, şimdi bir dizi olurdu ve onu bozacaktı. +// 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 name = "Ryan McDermott"; function splitIntoFirstAndLastName() { @@ -561,7 +625,7 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**İyi:** +**Good:** ```javascript function splitIntoFirstAndLastName(name) { @@ -575,27 +639,44 @@ console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yan Etkilerden Kaçının (Bölüm 2) +### Avoid Side Effects (part 2) -JavaScript'te, temel öğeler değerlerle aktarılır (passed by value) ve objeler/diziler referans ile aktarılır (passed by reference). +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: -Bu durumda, nesnelerde ve dizilerde fonksiyonunuz bir değişiklik yaparsa -örneğin, bir alışveriş sepeti dizisinde, satın almak için bir öğe ekleyerek, -bu `cart` dizisini kullanan diğer fonksiyonlar bu eklemeden etkilenecektir. -Bu harika olabilir, ancak kötü de olabilir. Kötü bir hayal edelim -durum: +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. -Kullanıcı, 'Satın Al' butonuna basar ve bu `satınal` fonksiyonu sunucuya bir istek atarak `sepet` dizisini sunucuya gönderir. Kötü bir ağ bağlantısı nedeniyle, `satınal` işlevi isteği yeniden denemeye devam etmelidir. Şimdi, bu arada kullanıcı yanlışlıkla ağ isteği başlamadan istemedikleri bir öğenin üzerine "Sepete Ekle" düğmesini tıklarsa ne olur? Bu olursa ve ağ isteği başlarsa, yanlışlıkla satın alınan öğeyi gönderir çünkü alışveriş sepeti dizisine, `ürünüSepeteEkle` işlevinin istenmeyen bir öğe ekleyerek değiştirdiği bir referansı vardır. `ürünüSepeteEkle` ın her zaman `sepeti` klonlaması, düzenlemesi ve klonu döndürmesi için harika bir çözüm olacaktır. Bu, alışveriş sepetinin referansını tutan başka hiçbir işlevin herhangi bir değişiklikten etkilenmemesini sağlar. +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. -Bu yaklaşıma değinecek iki uyarı: +Two caveats to mention to this approach: -1. Girilen nesnesini gerçekten değiştirmek istediğiniz durumlar olabilir, ancak bunu uyguladığınızda, bu vakaların oldukça nadir olduğunu göreceksiniz. Çoğu şeyin yan etkisi olmayacak şekilde yeniden düzenlenebilir! +1. 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! -2. Büyük nesneleri klonlamak performans açısından çok pahalı olabilir. Neyse ki, bu pratikte büyük bir sorun değildir, çünkü bu tür programlama yaklaşımlarını manuel yapmaktansa daha hızlı olmasını ve büyük nesneleri ve dizileri klonlanlarken daha az bellek harcayan [harika kütüphaneler](https://facebook.github.io/immutable-js/) vardır. +2. 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](https://facebook.github.io/immutable-js/) 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ü:** +**Bad:** ```javascript const addItemToCart = (cart, item) => { @@ -603,7 +684,7 @@ const addItemToCart = (cart, item) => { }; ``` -**İyi:** +**Good:** ```javascript const addItemToCart = (cart, item) => { @@ -611,17 +692,21 @@ const addItemToCart = (cart, item) => { }; ``` -**[⬆ Başa dön](#İçindekiler)** - -### Global fonksiyonlar yazma +**[⬆ back to top](#table-of-contents)** -Globalleri kirletmek JavaScript'te kötü bir uygulamadır, çünkü başka bir kütüphaneyle çakıştırabilirsiniz ve API'lerinizi kullananlar kişiler canlıya çıkana kadar bu aksi durumların farkında olmayabilir. +### Don't write to global functions -Bir örnek düşünelim: JavaScript'in yerel kütüphanesindeki dizi `diff` methodunu genişletmek ve iki dizinin farklılıklarını göstermek istediğinizi var sayalım. JavaScript'in yerel Array yöntemini iki dizi arasındaki farkı gösterebilecek bir "diff" yöntemine genişletmek mi istiyorsunuz? +Polluting globals is a bad practice in JavaScript because you could clash with another +library and the user of your API would be none-the-wiser until they get an +exception in production. Let's think about an example: what if you wanted to +extend JavaScript's native Array method to have a `diff` method that could +show the difference between two arrays? You could write your new function +to the `Array.prototype`, but it could clash with another library that tried +to do the same thing. What if that other library was just using `diff` to find +the difference between the first and last elements of an array? This is why it +would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. -Yeni fonksiyonunuzu `Array.prototype`'e yazabilirsiniz, ancak aynı şeyi yapmaya çalışan başka bir kütüphane ile çakışabilir. Ya diğer kütüphane bir 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 `Array` globalini genişletmek çok daha iyi olurdu. - -**Kötü:** +**Bad:** ```javascript Array.prototype.diff = function diff(comparisonArray) { @@ -630,7 +715,7 @@ Array.prototype.diff = function diff(comparisonArray) { }; ``` -**İyi:** +**Good:** ```javascript class SuperArray extends Array { @@ -641,13 +726,15 @@ class SuperArray extends Array { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Zorunlu programlama yerine fonksiyonel programlamayı tercih edin +### Favor functional programming over imperative programming -JavaScript, Haskell'in olduğu gibi işlevsel bir dil değildir, ancak işlevsel bir tadı vardır. İşlevsel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiğinizde bu tarz bir programlama yapın. +JavaScript isn't a functional language in the way that Haskell is, but it has +a functional flavor to it. Functional languages can be cleaner and easier to test. +Favor this style of programming when you can. -**Kötü:** +**Bad:** ```javascript const programmerOutput = [ @@ -676,7 +763,7 @@ for (let i = 0; i < programmerOutput.length; i++) { } ``` -**İyi:** +**Good:** ```javascript const programmerOutput = [ @@ -704,11 +791,11 @@ const totalOutput = programmerOutput.reduce( ); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Koşulları kapsamak +### Encapsulate conditionals -**Kötü:** +**Bad:** ```javascript if (fsm.state === "fetching" && isEmpty(listNode)) { @@ -716,7 +803,7 @@ if (fsm.state === "fetching" && isEmpty(listNode)) { } ``` -**İyi:** +**Good:** ```javascript function shouldShowSpinner(fsm, listNode) { @@ -728,11 +815,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** ### Avoid negative conditionals -**Kötü:** +**Bad:** ```javascript function isDOMNodeNotPresent(node) { @@ -744,7 +831,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**İyi:** +**Good:** ```javascript function isDOMNodePresent(node) { @@ -756,14 +843,20 @@ if (isDOMNodePresent(node)) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Koşullardan kaçının +### Avoid conditionals -Bu imkansız bir görev gibi görünüyor. Bunu ilk kez duyduktan sonra, çoğu kişi "`if` ifadesi olmadan nasıl bir şey yapmam gerekiyor?" Cevap şu ki -birçok durumda aynı görevi yerine getirmek için polimorfizm kullanabilirsiniz. İkinci soru genellikle, "peki bu harika ama neden bunu yapmak isteyeyim ki?" Cevap, daha önce öğrendiğimiz temiz bir kod kavramıydı: bir fonksiyon sadece bir şey yapmalı. `If` ifadeleri olan sınıflarınız ve fonksiyonlarınız olduğunda, kullanıcılara işlevinizin birden fazla şey yaptığını söylüyor. Unutmayın, sadece bir şey yapın. +This seems like an impossible task. Upon first hearing this, most people say, +"how am I supposed to do anything without an `if` statement?" The answer is that +you can use polymorphism to achieve the same task in many cases. The second +question is usually, "well that's great but why would I want to do that?" The +answer is a previous clean code concept we learned: a function should only do +one thing. When you have classes and functions that have `if` statements, you +are telling your user that your function does more than one thing. Remember, +just do one thing. -**Kötü:** +**Bad:** ```javascript class Airplane { @@ -781,7 +874,7 @@ class Airplane { } ``` -**İyi:** +**Good:** ```javascript class Airplane { @@ -810,13 +903,16 @@ class Cessna extends Airplane { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yazım denetiminden kaçının (Bölüm 1) +### Avoid type-checking (part 1) -JavaScript türsüzdür, yani işlevleriniz her türlü argümanı alabilir. Bazen bu özgürlük bizi oltaya getirebilir ve fonksiyonlarınızda tip kontrolü yapmak cazip hale gelir. Bunu yapmaktan kaçınmanın birçok yolu vardır. Dikkate alınması gereken ilk şey tutarlı API'lardır. +JavaScript is untyped, which means your functions can take any type of argument. +Sometimes you are bitten by this freedom and it becomes tempting to do +type-checking in your functions. There are many ways to avoid having to do this. +The first thing to consider is consistent APIs. -**Kötü:** +**Bad:** ```javascript function travelToTexas(vehicle) { @@ -828,7 +924,7 @@ function travelToTexas(vehicle) { } ``` -**İyi:** +**Good:** ```javascript function travelToTexas(vehicle) { @@ -836,14 +932,21 @@ function travelToTexas(vehicle) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yazım denetiminden kaçının (Bölüm 2) +### Avoid type-checking (part 2) -Dizeler ve tamsayılar gibi temel ilkel değerlerle çalışıyorsanız, -ve polimorfizm kullanamazsınız, ancak yine de yazım denetimi yapma gereğini hissediyorsanız, TypeScript kullanmayı düşünmelisiniz. Standart JavaScript sentaks üstünde statik yazım sağladığı için normal JavaScript'e mükemmel bir alternatiftir. Manuel olarak normal JavaScript'i kontrol etmeyle ilgili sorun, iyi yapmak o kadar fazla ayrıntılı gerektirir ki, elde ettiğiniz sahte "tip güvenliği" kaybettiğiniz okunabilirliği telafi etmez. JavaScript'inizi temiz tutun, iyi testler yazın ve iyi kod incelemelerine sahip olun. Aksi takdirde, hepsini TypeScript ile yapın (dediğim gibi harika bir alternatif!). +If you are working with basic primitive values like strings and integers, +and you can't use polymorphism but you still feel the need to type-check, +you should consider using TypeScript. It is an excellent alternative to normal +JavaScript, as it provides you with static typing on top of standard JavaScript +syntax. The problem with manually type-checking normal JavaScript is that +doing it well requires so much extra verbiage that the faux "type-safety" you get +doesn't make up for the lost readability. Keep your JavaScript clean, write +good tests, and have good code reviews. Otherwise, do all of that but with +TypeScript (which, like I said, is a great alternative!). -**Kötü:** +**Bad:** ```javascript function combine(val1, val2) { @@ -858,7 +961,7 @@ function combine(val1, val2) { } ``` -**İyi:** +**Good:** ```javascript function combine(val1, val2) { @@ -866,25 +969,27 @@ function combine(val1, val2) { } ``` -**[⬆ Başa dön](#İçindekiler)** - -### Aşırı optimizasyon yapma +**[⬆ back to top](#table-of-contents)** -Modern tarayıcılar çalışma zamanında çok sayıda optimizasyon yapar. Çoğu zaman, eğer optimize ediyorsanız, sadece zamanınızı boşa harcıyorsunuz demektir. +### Don't over-optimize -Optimizasyonun nerede olmadığını görmek için [iyi kaynaklar](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) vardır. Bunları optimize edene kadar işaretleyebilirsiniz. +Modern browsers do a lot of optimization under-the-hood at runtime. A lot of +times, if you are optimizing then you are just wasting your time. [There are good +resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) +for seeing where optimization is lacking. Target those in the meantime, until +they are fixed if they can be. -**Kötü:** +**Bad:** ```javascript -// Eski tarayıcılarda, önbelleğe alınmamış "list.length" içeren her yineleme maliyetli olacaktır -// list.length yeniden hesaplanması nedeniyle. Modern tarayıcılarda bu optimize edilmiştir. +// On old browsers, each iteration with uncached `list.length` would be costly +// because of `list.length` recomputation. In modern browsers, this is optimized. for (let i = 0, len = list.length; i < len; i++) { // ... } ``` -**İyi:** +**Good:** ```javascript for (let i = 0; i < list.length; i++) { @@ -892,13 +997,15 @@ for (let i = 0; i < list.length; i++) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kullanılmayan kodları silin +### Remove dead code -Ölü kod, yinelenen kod kadar kötü. Kod tabanınızda tutmak için bir neden yoktur. Eğer çağrılmazsa, ondan kurtulun! Hala ihtiyacınız varsa sürüm geçmişinizde güvende olacaktır. +Dead code is just as bad as duplicate code. There's no reason to keep it in +your codebase. If it's not being called, get rid of it! It will still be safe +in your version history if you still need it. -**Kötü:** +**Bad:** ```javascript function oldRequestModule(url) { @@ -913,7 +1020,7 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**İyi:** +**Good:** ```javascript function newRequestModule(url) { @@ -924,21 +1031,25 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Nesneler ve Veri Yapıları** +## **Objects and Data Structures** -### Alıcıları ve ayarlayıcıları kullanma (Getters & Setters) +### Use getters and setters -Nesnelerdeki verilere erişmek için alıcıları ve ayarlayıcıları kullanmak, bir nesnedeki bir özelliği aramaktan daha iyi olabilir. "Neden?" sorabilirsiniz. Pekala, işte organize edilmeden nedenlerin bir listesi: +Using getters and setters to access data on objects could be better than simply +looking for a property on an object. "Why?" you might ask. Well, here's an +unorganized list of reasons why: -- Bir nesne özelliği almanın ötesinde daha fazlasını yapmak istediğinizde, kod tabanınızdaki her erişimciye bakmanız ve değiştirmeniz gerekmez. -- `set`yaparken doğrulama basitçe yapılabilir. -- Dahili gösterimi içine alır. -- Alma ve ayarlama sırasında kayıtlamayı(logging) ve hata yönetimi(error handling) eklemek kolaydır. -- Diyelimki sunucudan alıyorsunuz, nesnenizin özelliklerini tembel(lazy load) olarak yükleyebilirsiniz. +- When you want to do more beyond getting an object property, you don't have + to look up and change every accessor in your codebase. +- Makes adding validation simple when doing a `set`. +- Encapsulates the internal representation. +- Easy to add logging and error handling when getting and setting. +- You can lazy load your object's properties, let's say getting it from a + server. -**Kötü:** +**Bad:** ```javascript function makeBankAccount() { @@ -954,19 +1065,19 @@ const account = makeBankAccount(); account.balance = 100; ``` -**İyi:** +**Good:** ```javascript function makeBankAccount() { - // Bu fonksiyon özelinde (private) + // this one is private let balance = 0; - // "alıcı"yı, genel(public) objeyi döndürerek yap + // a "getter", made public via the returned object below function getBalance() { return balance; } - - // "ayarlayıcı"yı, genel(public) objeyi döndürerek yap + + // a "setter", made public via the returned object below function setBalance(amount) { // ... validate before updating the balance balance = amount; @@ -983,13 +1094,13 @@ const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Nesnelerin özel üyeleri olmasını sağlama +### Make objects have private members This can be accomplished through closures (for ES5 and below). -**Kötü:** +**Bad:** ```javascript const Employee = function(name) { @@ -1006,7 +1117,7 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**İyi:** +**Good:** ```javascript function makeEmployee(name) { @@ -1023,15 +1134,18 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Sınıflar** +## **Classes** -### ES5 düz fonksiyonlar yerine ES2015 / ES6 sınıflarını tercih et +### Prefer ES2015/ES6 classes over ES5 plain functions -Klasik ES5 sınıfları için okunabilir sınıf mirası, yapısı ve yöntem tanımları almak çok zordur. Miras almaya(inheritance) ihtiyacınız varsa (ve gerekmeyebileceğini unutmayın), ES2015 / ES6 sınıflarını tercih edin. Bununla birlikte, kendinizi daha büyük ve daha karmaşık nesnelere ihtiyaç duyana kadar küçük işlevlere tercih edin. +It's very difficult to get readable class inheritance, construction, and method +definitions for classical ES5 classes. If you need inheritance (and be aware +that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over +classes until you find yourself needing larger and more complex objects. -**Kötü:** +**Bad:** ```javascript const Animal = function(age) { @@ -1071,7 +1185,7 @@ Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ``` -**İyi:** +**Good:** ```javascript class Animal { @@ -1107,13 +1221,17 @@ class Human extends Mammal { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yöntem zincirini kullan +### Use method chaining -Bu desen JavaScript'te çok kullanışlıdır ve jQuery ve Lodash gibi birçok kütüphanede görürsünüz. Kodunuzun etkileyici ve daha az detaylı olmasını sağlar. Bu nedenle, diyorum ki, yöntem zincirleme kullanın ve kodunuzun ne kadar temiz olacağını göreceksiniz. Sınıf fonksiyonlarınızda, her fonksiyonun sonunda `this`'i döndürmeniz yeterlidir ve daha fazla sınıf yöntemini buna zincirleyebilirsiniz. +This pattern is very useful in JavaScript and you see it in many libraries such +as jQuery and Lodash. It allows your code to be expressive, and less verbose. +For that reason, I say, use method chaining and take a look at how clean your code +will be. In your class functions, simply return `this` at the end of every function, +and you can chain further class methods onto it. -**Kötü:** +**Bad:** ```javascript class Car { @@ -1145,7 +1263,7 @@ car.setColor("pink"); car.save(); ``` -**İyi:** +**Good:** ```javascript class Car { @@ -1157,25 +1275,25 @@ class Car { setMake(make) { this.make = make; - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } setModel(model) { this.model = model; - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } setColor(color) { this.color = color; - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } save() { console.log(this.make, this.model, this.color); - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } } @@ -1183,19 +1301,28 @@ class Car { const car = new Car("Ford", "F-150", "red").setColor("pink").save(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Miras yerine kompozisyon tercih et +### Prefer composition over inheritance -Dörtlü çete tarafından başlatılan ünlü [_Tasarım Desenleri_](https://en.wikipedia.org/wiki/Design_Patterns) gibi, siz de kompozisyonu, miras bırakmaya yerine göre tercih etmelisiniz. Miras kullanmak için birçok iyi neden ve kompozisyon kullanmak için birçok iyi neden vardır. Bu maksimum nokta için ana nokta, zihniniz içgüdüsel olarak miras kullanma için giderse, kompozisyon sorununuzu daha iyi modelleyip değiştiremeyeceğini düşünmeye çalışın. Bazı durumlarda olabilir. +As stated famously in [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, +you should prefer composition over inheritance where you can. There are lots of +good reasons to use inheritance and lots of good reasons to use composition. +The main point for this maxim is that if your mind instinctively goes for +inheritance, try to think if composition could model your problem better. In some +cases it can. -O zaman "ne zaman miras kullanmalıyım?" diye merak ediyor olabilirsiniz. O eldeki probleminize bağlıdır, ancak bu mirasın kompozisyondan daha mantıklı olduğu iyi bir listedir: +You might be wondering then, "when should I use inheritance?" It +depends on your problem at hand, but this is a decent list of when inheritance +makes more sense than composition: -1. Mirasınız "has-a" değil, "is-a" ilişkisini temsil eder ilişki (İnsan-> Hayvan ve Kullanıcı-> KullanıcıAyrıntıları). -2. Temel sınıflardan kodu yeniden kullanabilirsiniz (İnsanlar tüm hayvanlar gibi hareket edebilir). -3. Temel sınıfı değiştirerek türetilmiş sınıflarda genel değişiklikler yapmak istiyorsunuz. (Hareket ettiklerinde tüm hayvanların kalori harcamalarını değiştirin). +1. Your inheritance represents an "is-a" relationship and not a "has-a" + relationship (Human->Animal vs. User->UserDetails). +2. You can reuse code from the base classes (Humans can move like all animals). +3. You want to make global changes to derived classes by changing a base class. + (Change the caloric expenditure of all animals when they move). -**Kötü:** +**Bad:** ```javascript class Employee { @@ -1207,7 +1334,7 @@ class Employee { // ... } -// Kötü çünkü Çalışanların(Employees) vergi bilgisi 'var'. ÇalışanVergiBilgisi(EmployeeTaxData) bir çeşit Çalışan(Employee) değil. +// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee class EmployeeTaxData extends Employee { constructor(ssn, salary) { super(); @@ -1219,7 +1346,7 @@ class EmployeeTaxData extends Employee { } ``` -**İyi:** +**Good:** ```javascript class EmployeeTaxData { @@ -1244,15 +1371,22 @@ class Employee { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** ## **SOLID** -### Tek Sorumluluk İlkesi (SRP) +### Single Responsibility Principle (SRP) -Temiz Kod'da belirtildiği gibi, "Bir sınıfın değişmesi için asla birden fazla sebep olmamalıdır". Bir sınıfı tıklım tıklım bir çok işlevsellikle doldurmak çekici gelebilir, tıpkı uçuşlarda yanına alabileceğiniz bir valiz gibi. Bununla ilgili sorun, sınıfınızın kavramsal olarak uyumlu olmayacağı ve değişmesi için birçok neden vereceği yönündedir. Bir sınıfı değiştirmek için ihtiyaç duyduğunuz sayıyı en aza indirmek önemlidir. Bir sınıfta çok fazla işlevsellik varsa 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. +As stated in Clean Code, "There should never be more than one reason for a class +to change". It's tempting to jam-pack a class with a lot of functionality, like +when you can only take one suitcase on your flight. The issue with this is +that your class won't be conceptually cohesive and it will give it many reasons +to change. Minimizing the amount of times you need to change a class is important. +It's important because if too much functionality is in one class and you modify +a piece of it, it can be difficult to understand how that will affect other +dependent modules in your codebase. -**Kötü:** +**Bad:** ```javascript class UserSettings { @@ -1272,7 +1406,7 @@ class UserSettings { } ``` -**İyi:** +**Good:** ```javascript class UserAuth { @@ -1299,13 +1433,16 @@ class UserSettings { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Açık / Kapalı Prensibi (OCP) +### Open/Closed Principle (OCP) -Bertrand Meyer tarafından belirtildiği gibi, "yazılım varlıkları (sınıflar, modüller, işlevler, vb.) Genişletme 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. +As stated by Bertrand Meyer, "software entities (classes, modules, functions, +etc.) should be open for extension, but closed for modification." What does that +mean though? This principle basically states that you should allow users to +add new functionalities without changing existing code. -**Kötü:** +**Bad:** ```javascript class AjaxAdapter extends Adapter { @@ -1349,7 +1486,7 @@ function makeHttpCall(url) { } ``` -**İyi:** +**Good:** ```javascript class AjaxAdapter extends Adapter { @@ -1387,15 +1524,24 @@ class HttpRequester { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Liskov’un Yerine Geçme Prensibi (LSP) +### Liskov Substitution Principle (LSP) -Bu çok basit bir kavram için korkutucu bir terimdir.Resmi olarak "S, T'nin bir alt tipiyse, o zaman T tipi nesnelerin yerine S tipi nesneler (yani, S tipi nesneler T programındaki nesnelerin yerine geçebilir), bu programın istenen özelliklerini değiştirmeden değiştirilebilir (doğruluk, yapılan görev vb.) " Bu daha da korkunç bir tanım. +This is a scary term for a very simple concept. It's formally defined as "If S +is a subtype of T, then objects of type T may be replaced with objects of type S +(i.e., objects of type S may substitute objects of type T) without altering any +of the desirable properties of that program (correctness, task performed, +etc.)." That's an even scarier definition. -Bunun için en iyi açıklama, bir üst 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 elde etmeden birbirinin yerine kullanılabilir.Bu hala kafa karıştırıcı olabilir, bu yüzden klasik Kare Dikdörtgen örneğine bakalım. Matematiksel olarak, bir kare bir dikdörtgendir, ancak miras yoluyla "is-a" ilişkisini kullanarak model verirseniz, hızlı bir şekilde sorun yaşarsınız. +The best explanation for this is if you have a parent class and a child class, +then the base class and child class can be used interchangeably without getting +incorrect results. This might still be confusing, so let's take a look at the +classic Square-Rectangle example. Mathematically, a square is a rectangle, but +if you model it using the "is-a" relationship via inheritance, you quickly +get into trouble. -**Kötü:** +**Bad:** ```javascript class Rectangle { @@ -1441,7 +1587,7 @@ function renderLargeRectangles(rectangles) { rectangles.forEach(rectangle => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // KÖTÜ: Kare için 25 değerini döndürür. 20 olmalı. + const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. rectangle.render(area); }); } @@ -1450,7 +1596,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**İyi:** +**Good:** ```javascript class Shape { @@ -1497,17 +1643,25 @@ const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Arayüzlerin Ayrımı Prensibi (ISP) +### Interface Segregation Principle (ISP) -JavaScript'in arayüzleri yoktur, bu nedenle bu ilke diğerleri kadar kesin olarak geçerli değildir. Bununla birlikte, JavaScript'in tür sistemi eksikliğinde bile önemli ve alâkalıdır. +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, "kullanıcılar kullanmadığı arabirimlere bağımlı olmaya zorlanmamalıdır." der. Arabirimler, `Duck Typing` yüzünden JavaScript'de üstü kapalı anlaşmalardır. +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. -Bu ilkeyi JavaScript'te gösteren iyi bir örnek, sınıflar büyük ayar nesneleri gerektirir. Kullanıcıların büyük miktarda seçenek ayarlamalarını istememek gerekli değildir, çünkü çoğu zaman tüm ayarlara ihtiyaç duymazlar. Bunları isteğe bağlı yapmak, bir "büyük arayüzü" olmasını önlemeye yardımcı olur. +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ü:** +**Bad:** ```javascript class DOMTraverser { @@ -1528,12 +1682,12 @@ class DOMTraverser { const $ = new DOMTraverser({ rootNode: document.getElementsByTagName("body"), - animationModule() {} // Coğunlukla, bunu canlandırmamız gerekmeyecek + animationModule() {} // Most of the time, we won't need to animate when traversing. // ... }); ``` -**İyi:** +**Good:** ```javascript class DOMTraverser { @@ -1567,24 +1721,32 @@ const $ = new DOMTraverser({ }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Bağlılığı Tersine Çevirme Prensibi (DIP) +### Dependency Inversion Principle (DIP) -Bu ilke iki temel şeyi ifade eder: +This principle states two essential things: -1. Yüksek seviyeli modüller, düşük seviyeli modüllere bağlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır. -2. Soyutlamalar detaylara bağlı olmamalıdır. Ayrıntılar soyutlamalara bağlı olmalıdır. +1. High-level modules should not depend on low-level modules. Both should + depend on abstractions. +2. Abstractions should not depend upon details. Details should depend on + abstractions. -İlk başta bunu anlamak zor olabilir, ancak AngularJS ile çalıştıysanız, bu prensibin Bağımlılık Enjeksiyonu (DI) şeklinde bir uygulamasını gördünüz. Aynı kavramlar olmasalar da, DIP yüksek seviyede modüllerin düşük seviyeli modüllerinin detaylarını bilmelerini ve ayarlamalarını sağlar. -Bunu DI ile başarabilir. Bunun büyük bir yararı, modüller arasındaki bağlantıyı azaltmasıdır. Eşleştirme(Coupling) çok kötü bir gelişme modelidir çünkü -kodunuzu yeniden düzenleme zorlaştırır. +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. -Daha önce belirtildiği gibi, JavaScript'in arayüzleri yoktur, bu nedenle soyutlamalar örtük sözleşmelere bağlıdır. -Yani, bir nesnenin / sınıfın başka bir nesneye / sınıfa maruz bıraktığı yöntemler ve özellikler. -Aşağıdaki örnekte, örtük sözleşme, bir `InventoryTracker` için herhangi bir Request modülünün `requestItems` yöntemine sahip olacağıdır. +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ü:** +**Bad:** ```javascript class InventoryRequester { @@ -1601,8 +1763,8 @@ class InventoryTracker { constructor(items) { this.items = items; - // KÖTÜ: Belirli bir istek uygulamasına bağımlılık yarattık. -    // requestItems sadece `request` 'e bağlımlı olmalıdır. + // 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(); } @@ -1617,7 +1779,7 @@ const inventoryTracker = new InventoryTracker(["apples", "bananas"]); inventoryTracker.requestItems(); ``` -**İyi:** +**Good:** ```javascript class InventoryTracker { @@ -1653,8 +1815,8 @@ class InventoryRequesterV2 { } } -// Bağımlılıklarımızı harici olarak yapılandırarak ve enjekte ederek -// istek modülümüzü WebSockets kullanan yeni ve havalı bir modülle kolayca değiştirebiliriz. +// 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() @@ -1662,19 +1824,28 @@ const inventoryTracker = new InventoryTracker( inventoryTracker.requestItems(); ``` -**[⬆ Başa dön](#İçindekiler)** - -## **Testler** +**[⬆ back to top](#table-of-contents)** -Test etmek canlıya çıkmaktan bile önemlidir. Eğer testiniz yoksa veya yetersiz bir miktardaysa, kodu her gönderdiğinizde hiçbir şeyin bozulmadığına emin olmazsınız. Neyin yeterli bir miktar oluşturduğuna karar vermek takımınıza bağlıdır, %100 kapsama sahip olmak (tüm ifadeler ve şubeler) size güven ve gönül rahatlığı sağlar. Bu, harika bir test framework'üne sahip olmanın yanı sıra, [iyi bir kapsama aracı](https://gotwarlost.github.io/istanbul/) kullanmanız gerektiği anlamına gelir +## **Testing** -Test yazmamanın mazereti yoktur. Çok sayıda [iyi JS test framework'ü](https://jstherightway.org/#testing-tools) vardır, bu yüzden ekibinize uyan birini bulun. +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](https://gotwarlost.github.io/istanbul/). -Ekibiniz için uygun olanı bulduğunuzda, kullanılan her yeni özellik / modül için daima testler yazmayı hedefleyin. Tercih ettiğiniz yöntem Test Odaklı Geliştirme (TDD) ise, bu harika, ancak asıl mesele, herhangi bir özelliği başlatmadan veya mevcut bir özelliği yeniden düzenlemeden önce kapsama hedeflerinize ulaştığınızdan emin olmaktır. +There's no excuse to not write tests. There are [plenty of good JS test frameworks](https://jstherightway.org/#testing-tools), 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. -### Her test için tek konsept +### Single concept per test -**Kötü:** +**Bad:** ```javascript import assert from "assert"; @@ -1698,7 +1869,7 @@ describe("MomentJS", () => { }); ``` -**İyi:** +**Good:** ```javascript import assert from "assert"; @@ -1724,15 +1895,16 @@ describe("MomentJS", () => { }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Tutarlılık** +## **Concurrency** -### Promises kullanın, callback değil +### Use Promises, not callbacks -'Callback'ler temiz değildir ve aşırı miktarda iç içe geçmeye neden olurlar. ES2015 / ES6 ile Promise'ler yerleşik bir global tiptir. Onları kullan! +Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, +Promises are a built-in global type. Use them! -**Kötü:** +**Bad:** ```javascript import { get } from "request"; @@ -1756,7 +1928,7 @@ get( ); ``` -**İyi:** +**Good:** ```javascript import { get } from "request-promise"; @@ -1774,13 +1946,17 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Async/Await kullanmak Promis kullanmaktan bile daha temiz +### Async/Await are even cleaner than Promises -Promise'ler callback'lere göte çok daha temiz alternatiflerdir, ama ES2017/ES8 async ve await getirdi ve bu daha temiz bir çözüm sunuyor. Tek yapmanız gereken fonksiyonun başına `async` eklemek ve sonrasında mantıksal kullanımızı `then` zinciri kulanmadan yazabilirsiniz. Bugün ES2017 / ES8 özelliklerinden yararlanabiliyorsanız bunu kullanın! +Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await +which offer an even cleaner solution. All you need is a function that is prefixed +in an `async` keyword, and then you can write your logic imperatively without +a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features +today! -**Kötü:** +**Bad:** ```javascript import { get } from "request-promise"; @@ -1798,7 +1974,7 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**İyi:** +**Good:** ```javascript import { get } from "request-promise"; @@ -1819,21 +1995,25 @@ async function getCleanCodeArticle() { getCleanCodeArticle() ``` -**[⬆ Başa dön](#İçindekiler)** - -## **Hata Yönetimi** - -Atılan hatalar iyi bir şey! Bunlar, programınızdaki bir şey yanlış gittiğinde çalışma zamanının başarıyla tanımlandığı anlamına gelir ve sizi geçerli yığında (stack) fonksiyonu çalıştırmayı, işlevi dururup size konsolda yığın izlemede haber vererek yapar. +**[⬆ back to top](#table-of-contents)** ---- HERE +## **Error Handling** -### Yakalanmış hataları görmemezlikten gelmeyin +Thrown errors are a good thing! They mean the runtime has successfully +identified when something in your program has gone wrong and it's letting +you know by stopping function execution on the current stack, killing the +process (in Node), and notifying you in the console with a stack trace. -Yakalanan bir hatayla hiçbir şey yapmamanız size söz konusu hatayı düzeltebilme veya tepki gösterme yeteneği vermez. Hatayı konsola (`console.log`) kaydetmek, konsola yazdırılan bir şey denizinde kaybolabileceği sıklıkta daha iyi değildir. +### Don't ignore caught errors -Herhangi bir kod parçasını `try/catch` içerisinde kullanıyorsanız, orada bir hata olabileceğini düşündüğünüz anlamına gelir ve bu nedenle gerçekleştiği zaman için bir planınız olması veya bir kod yolu oluşturmanız gerekir. +Doing nothing with a caught error doesn't give you the ability to ever fix +or react to said error. Logging the error to the console (`console.log`) +isn't much better as often times it can get lost in a sea of things printed +to the console. If you wrap any bit of code in a `try/catch` it means you +think an error may occur there and therefore you should have a plan, +or create a code path, for when it occurs. -**Kötü:** +**Bad:** ```javascript try { @@ -1843,27 +2023,28 @@ try { } ``` -**İyi:** +**Good:** ```javascript try { functionThatMightThrow(); } catch (error) { - // Bir secenek (console.log'dan daha dikkat çekici) + // One option (more noisy than console.log): console.error(error); - // Bir secenek daha + // Another option: notifyUserOfError(error); - // Bir secenek daha + // Another option: reportErrorToService(error); - // veya hepsini bir yapın + // OR do all three! } ``` -### Reddedilmiş promisleri görmemezlikten gelmeyin +### Don't ignore rejected promises -Aynı sebeplerden dolayı, `try/catch`'de oluşan hataları yok saymamalısınız +For the same reason you shouldn't ignore caught errors +from `try/catch`. -**Kötü:** +**Bad:** ```javascript getdata() @@ -1875,7 +2056,7 @@ getdata() }); ``` -**İyi:** +**Good:** ```javascript getdata() @@ -1883,29 +2064,36 @@ getdata() functionThatMightThrow(data); }) .catch(error => { - // Bir secenek (console.log'dan daha dikkat çekici) + // One option (more noisy than console.log): console.error(error); - // Bir secenek daha + // Another option: notifyUserOfError(error); - // Bir secenek daha + // Another option: reportErrorToService(error); - // veya hepsini bir yapın + // OR do all three! }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Biçimlendirme** +## **Formatting** -Biçimlendirme özneldir. Buradaki birçok kural gibi, uygulamanız gereken zor ve hızlı bir kural yoktur. Ana nokta biçimlendirme üzerinde tartışma DEĞİLDİR. Bunu otomatikleştirmek için [tonlarca araç](https://standardjs.com/rules.html) vardır. Birini kullan! Mühendislerin biçimlendirme konusunda tartışmaları zaman ve para kaybıdır. +Formatting is subjective. Like many rules herein, there is no hard and fast +rule that you must follow. The main point is DO NOT ARGUE over formatting. +There are [tons of tools](https://standardjs.com/rules.html) to automate this. +Use one! It's a waste of time and money for engineers to argue over formatting. -Otomatik biçimlendirme (girintileme, sekmeler ve boşluklar, çift veya tek tırnak işaretleri vb.) Kapsamına girmeyen şeyler için bazı rehberlik için buraya bakın. +For things that don't fall under the purview of automatic formatting +(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here +for some guidance. -### Tutarlı büyük harf kullanımı +### Use consistent capitalization -JavaScript türsüzdür, bu nedenle büyük / küçük harf kullanımı değişkenleriniz, işlevleriniz vb. Hakkında çok şey anlatır. Bu kurallar özneldir, böylece ekibiniz istediklerini seçebilir. Mesele şu ki, ne seçerseniz seçin, tutarlı olun. +JavaScript is untyped, so capitalization tells you a lot about your variables, +functions, etc. These rules are subjective, so your team can choose whatever +they want. The point is, no matter what you all choose, just be consistent. -**Kötü:** +**Bad:** ```javascript const DAYS_IN_WEEK = 7; @@ -1921,7 +2109,7 @@ class animal {} class Alpaca {} ``` -**İyi:** +**Good:** ```javascript const DAYS_IN_WEEK = 7; @@ -1937,13 +2125,15 @@ class Animal {} class Alpaca {} ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksion çağıranları ve çağrılanları yakın olmalı +### Function callers and callees should be close -Eğer bir fonksiyon diğer fonksiyonu çağırıyorsa, dikey olarak bu fonksiyonları kaynak dosyasında yakın tutun. İdeal olan, fonksiyonu kullanan kullandığı fonksiyonun hemen üstünde olmasıdır. We tend to read code from top-to-bottom, like a newspaper.Bu nedenle, kodunuzu bu şekilde okuyun. +If a function calls another, keep those functions vertically close in the source +file. Ideally, keep the caller right above the callee. We tend to read code from +top-to-bottom, like a newspaper. Because of this, make your code read that way. -**Kötü:** +**Bad:** ```javascript class PerformanceReview { @@ -1983,7 +2173,7 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**İyi:** +**Good:** ```javascript class PerformanceReview { @@ -2023,37 +2213,37 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Yorumlar** +## **Comments** -### Yalnızca iş mantığı karmaşıklığı olan şeyleri yorumlayın +### Only comment things that have business logic complexity. -Yorumlar aslında bir özür, şart değil. İyi kod _çoğunlukla_ kendini belgelemektedir. +Comments are an apology, not a requirement. Good code _mostly_ documents itself. -**Kötü:** +**Bad:** ```javascript function hashIt(data) { - // Karma + // The hash let hash = 0; - // String uzunluğu + // Length of string const length = data.length; - // Verilerdeki her karakteri gözden geçirin + // Loop through every character in data for (let i = 0; i < length; i++) { - // Karakter kodunu al + // Get character code. const char = data.charCodeAt(i); - // Karıştır + // Make the hash hash = (hash << 5) - hash + char; - // 32-bit tam sayıya dönüştür + // Convert to 32-bit integer hash &= hash; } } ``` -**İyi:** +**Good:** ```javascript function hashIt(data) { @@ -2064,19 +2254,19 @@ function hashIt(data) { const char = data.charCodeAt(i); hash = (hash << 5) - hash + char; - // 32-bit tam sayıya dönüştür + // Convert to 32-bit integer hash &= hash; } } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kodlarınızı yorum olarak bırakmayın +### Don't leave commented out code in your codebase -Versiyon kontrol'un var olmasının bir sebebi var. Eski kodlarınızı tarihin tozlu sayfalarında bırakın. +Version control exists for a reason. Leave old code in your history. -**Kötü:** +**Bad:** ```javascript doStuff(); @@ -2085,19 +2275,20 @@ doStuff(); // doSoMuchStuff(); ``` -**İyi:** +**Good:** ```javascript doStuff(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Günlük yorumları yapmayın +### Don't have journal comments -Hatırlayın, versiyon kontrol kullanın! Kullanılmayan koda, yoruma alınmış koda ve özellikle günlük kodlarına gerek yok. Geçmiş için `git log` kullanın. +Remember, use version control! There's no need for dead code, commented code, +and especially journal comments. Use `git log` to get history! -**Kötü:** +**Bad:** ```javascript /** @@ -2111,7 +2302,7 @@ function combine(a, b) { } ``` -**İyi:** +**Good:** ```javascript function combine(a, b) { @@ -2119,13 +2310,14 @@ function combine(a, b) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yer belirleyicilerden kaçının +### Avoid positional markers -Bunlar genellikle kirlilik yaratır. Fonksiyonların ve değişken adlarının yanı sıra uygun girinti ve biçimlendirme kodunuza görsel yapı kazandırsın. +They usually just add noise. Let the functions and variable names along with the +proper indentation and formatting give the visual structure to your code. -**Kötü:** +**Bad:** ```javascript //////////////////////////////////////////////////////////////////////////////// @@ -2144,7 +2336,7 @@ const actions = function() { }; ``` -**İyi:** +**Good:** ```javascript $scope.model = { @@ -2157,11 +2349,11 @@ const actions = function() { }; ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## Çeviriler +## Translation -Ayrıca diğer dillerde de: +This is also available in other languages: - ![am](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Armenia.png) **Armenian**: [hanumanum/clean-code-javascript/](https://github.com/hanumanum/clean-code-javascript) - ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) @@ -2182,7 +2374,6 @@ Ayrıca diğer dillerde de: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) -- ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** From 60a33571e766b3aa05ac5c7cd0d871f035802d3e Mon Sep 17 00:00:00 2001 From: Burak Sonmez Date: Thu, 26 Mar 2020 08:43:25 +0300 Subject: [PATCH 157/170] Turkish added --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bbe7a083..af68982e 100644 --- a/README.md +++ b/README.md @@ -2374,6 +2374,7 @@ This is also available in other languages: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) +- ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) **[⬆ back to top](#table-of-contents)** From 1470b0b5e4ca00feeb19cba28af4c8ee183ea305 Mon Sep 17 00:00:00 2001 From: Burak Sonmez Date: Thu, 26 Mar 2020 08:46:06 +0300 Subject: [PATCH 158/170] Turkish Added --- README.md | 900 +++++++++++++++++++++--------------------------------- 1 file changed, 354 insertions(+), 546 deletions(-) diff --git a/README.md b/README.md index af68982e..d7747ff9 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,55 @@ # clean-code-javascript -## Table of Contents +## İçindekiler -1. [Introduction](#introduction) -2. [Variables](#variables) -3. [Functions](#functions) -4. [Objects and Data Structures](#objects-and-data-structures) -5. [Classes](#classes) +1. [Giriş](#Giriş) +2. [Değişkenler](#Değişkenler) +3. [Fonksiyonlar](#Fonksiyonlar) +4. [Nesneler ve Veri Yapıları](#nesneler-ve-veri-yapıları) +5. [Sınıflar (Class)](#sınıflar) 6. [SOLID](#solid) -7. [Testing](#testing) -8. [Concurrency](#concurrency) -9. [Error Handling](#error-handling) -10. [Formatting](#formatting) -11. [Comments](#comments) -12. [Translation](#translation) +7. [Testler](#testler) +8. [Tutarlılık](#Tutarlılık) +9. [Hata Yönetimi](#Hata-Yönetimi) +10. [Biçimlendirme](#Biçimlendirme) +11. [Yorumlar](#yorumlar) +12. [Çeviriler](#çeviriler) -## Introduction +## Giriş -![Humorous image of software quality estimation as a count of how many expletives -you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg) +![Kod okurken kaç defa bağıracağınızın bir sayısı olarak yazılım kalite tahmininin görüntüsü](https://www.osnews.com/images/comics/wtfm.jpg) -Software engineering principles, from Robert C. Martin's book -[_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), -adapted for JavaScript. This is not a style guide. It's a guide to producing -[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. +Robert C. Martin'nın kitabı olan ve yazılım mühendisliği ilkerini barındıran [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)'un JavaScript için uyarlanmış hali. Bu bir tarz rehberi değil. JavaScript'te [okunabilir, yeniden kullanılabilir ve yeniden düzenlenebilir](https://github.com/ryanmcdermott/3rs-of-software-architecture) yazılımlar üretmeye yönelik bir kılavuzdur. -Not every principle herein has to be strictly followed, and even fewer will be -universally agreed upon. These are guidelines and nothing more, but they are -ones codified over many years of collective experience by the authors of -_Clean Code_. +Buradaki her ilkeye kesinlikle uyulmak zorunda değildir ve daha azı evrensel olarak kabul edilecektir. Bunlar önerilerden başka bir şey değildir, fakat bunlar _Clean Code_ yazarları tarafından uzun yıllar süren tecrübeler ile kodlandı. -Our craft of software engineering is just a bit over 50 years old, and we are -still learning a lot. When software architecture is as old as architecture -itself, maybe then we will have harder rules to follow. For now, let these -guidelines serve as a touchstone by which to assess the quality of the -JavaScript code that you and your team produce. +Yazılım mühendisliği zanaatımız 50 yaşın biraz üzerinde ve hala çok şey öğreniyoruz. Yazılım mimarisi, mimarinin kendisi kadar eski olduğunda, belki de takip edilmesi daha zor kurallarımız olacak. Şimdi, bu önerilerin sizin ve ekibinizin ürettiği JavaScript kodunun kalitesini değerlendirmek için bir mihenk taşı işlevi görmesine izin verin. -One more thing: knowing these won't immediately make you a better software -developer, and working with them for many years doesn't mean you won't make -mistakes. Every piece of code starts as a first draft, like wet clay getting -shaped into its final form. Finally, we chisel away the imperfections when -we review it with our peers. Don't beat yourself up for first drafts that need -improvement. Beat up the code instead! +Bir şey daha: bunların bilinmesi sizi hemen daha iyi bir yazılım geliştiricisi yapmaz ve uzun yıllar onlarla çalışmak hata yapmayacağınız anlamına gelmez. -## **Variables** +Her kod parçası ilk taslak olarak başlar, örneğin ıslak kil son halini alır. Son olarak, akranlarımızla gözden geçirdiğimizde kusurları kesiyoruz. İyileştirilmesi gereken ilk taslaklar için kendinize yüklenmeyin. Onun yerine kodunuza yüklenin. -### Use meaningful and pronounceable variable names +## **Değişkenler** -**Bad:** +### Anlamlı ve belirgin değişken adları kullanın + +**Kötü:** ```javascript const yyyymmdstr = moment().format("YYYY/MM/DD"); ``` -**Good:** +**İyi:** ```javascript const currentDate = moment().format("YYYY/MM/DD"); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use the same vocabulary for the same type of variable +### Aynı değişken türü için aynı kelimeleri kullanın -**Bad:** +**Kötü:** ```javascript getUserInfo(); @@ -71,45 +57,41 @@ getClientData(); getCustomerRecord(); ``` -**Good:** +**İyi:** ```javascript getUser(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use searchable names +### Aranabilir isimler kullanın -We will read more code than we will ever write. It's important that the code we -do write is readable and searchable. By _not_ naming variables that end up -being meaningful for understanding our program, we hurt our readers. -Make your names searchable. Tools like -[buddy.js](https://github.com/danielstjules/buddy.js) and -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) -can help identify unnamed constants. +Yazacağımızdan daha fazla kod okuyacağız. Yazdığımız kodun okunabilir ve aranabilir olması önemlidir. Değişkenleri anlamlı ve anlaşılabilir _isimlendirmemekle_, okuyucuya zarar veriyoruz. +Adlarınızı aranabilir hale getirin. [buddy.js](https://github.com/danielstjules/buddy.js) ve +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) gibi araçlar isimlendirilmemiş sabitlerinizi belirlemenize yardımcı olacaktır. -**Bad:** +**Kötü:** ```javascript -// What the heck is 86400000 for? +// 86400000 neydi ? setTimeout(blastOff, 86400000); ``` -**Good:** +**İyi:** ```javascript -// Declare them as capitalized named constants. +// Bunları büyük harfle adlandırılmış sabitler olarak bildirin. const MILLISECONDS_IN_A_DAY = 86_400_000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use explanatory variables +### Açıklayıcı değişkenler kullan -**Bad:** +**Kötü:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -120,7 +102,7 @@ saveCityZipCode( ); ``` -**Good:** +**İyi:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -129,13 +111,13 @@ const [_, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid Mental Mapping +### Kafadan planmaktan kaçının -Explicit is better than implicit. +Açık olmak, ima etmekten iyidir. -**Bad:** +**Kötü:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -145,12 +127,12 @@ locations.forEach(l => { // ... // ... // ... - // Wait, what is `l` for again? + // Bir dakika, 'l' neydi? dispatch(l); }); ``` -**Good:** +**İyi:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -164,14 +146,13 @@ locations.forEach(location => { }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't add unneeded context +### Gereksiz içerikler eklemeyin -If your class/object name tells you something, don't repeat that in your -variable name. +Sınıf / nesne adınız size bir şey söylüyorsa, bunu değişken adınızda tekrarlamayın. -**Bad:** +**Kötü:** ```javascript const Car = { @@ -185,7 +166,7 @@ function paintCar(car) { } ``` -**Good:** +**İyi:** ```javascript const Car = { @@ -199,16 +180,13 @@ function paintCar(car) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use default arguments instead of short circuiting or conditionals +### Kısa devre veya şartlar yerine önceden tanımlanmış argümanlar kullanın -Default arguments are often cleaner than short circuiting. Be aware that if you -use them, your function will only provide default values for `undefined` -arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and -`NaN`, will not be replaced by a default value. +Önceden tanımlanan argümanlar genellikle kısa devrelere göre daha temizdir. Bunları kullanırsanız şunun farkında olun, fonksiyonunuz sadece `tanımsız` _(undefined)_ değerler için önceden tanımlanmış argümana kullanacaktır. `''`, `""`, `false`, `null`, `0`, ve `NaN` gibi "yanlış denilebilecek" değerler önceden tanımlanmış bir değerle değiştirilmez. -**Bad:** +**Kötü:** ```javascript function createMicrobrewery(name) { @@ -217,7 +195,7 @@ function createMicrobrewery(name) { } ``` -**Good:** +**İyi:** ```javascript function createMicrobrewery(name = "Hipster Brew Co.") { @@ -225,41 +203,27 @@ function createMicrobrewery(name = "Hipster Brew Co.") { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Functions** +## **Fonksiyonlar** -### Function arguments (2 or fewer ideally) +### Fonksiyonlar argümanları (Tercihen 2 veya daha az) -Limiting the amount of function parameters is incredibly important because it -makes testing your function easier. Having more than three leads to a -combinatorial explosion where you have to test tons of different cases with -each separate argument. +Fonksiyon parametrelerinin sayısını sınırlamak inanılmaz derecede önemlidir, çünkü fonksiyonunuzu test etmeyi kolaylaştırır. Üçten fazlasına sahip olmak, her bir argümanla tonlarca farklı vakayı test etmeniz gereken bir kombinasyonel patlamaya yol açar. -One or two arguments is the ideal case, and three should be avoided if possible. -Anything more than that should be consolidated. Usually, if you have -more than two arguments then your function is trying to do too much. In cases -where it's not, most of the time a higher-level object will suffice as an -argument. +Bir veya iki argüman ideal durumdur ve mümkünse üç tanesinden kaçınılmalıdır. Bundan daha fazlası birleştirilmeldir. Genellikle, +ikiden fazla argüman sonra fonksiyonunuz çok işlem yapmaya çalışıyor. Olmadığı durumlarda, çoğu zaman daha üst düzey bir nesne argüman olarak yeterli olacaktır. -Since JavaScript allows you to make objects on the fly, without a lot of class -boilerplate, you can use an object if you are finding yourself needing a -lot of arguments. +JavaScript havada nesne yapmanıza olanak sağladığı için, bir çok sınıf yapısına gerek kalmadan, bir nesneyi birden fazla nesne kullanmadan kullanabilirsiniz. -To make it obvious what properties the function expects, you can use the ES2015/ES6 -destructuring syntax. This has a few advantages: +Fonksiyonun hangi özellikleri beklediğini netleştirmek için ES2015 / ES6 ayrıştırma sintaksını kullanabilirsiniz. Bunun birkaç avantajı vardır: -1. When someone looks at the function signature, it's immediately clear what - properties are being used. -2. It can be used to simulate named parameters. -3. Destructuring also clones the specified primitive values of the argument - object passed into the function. This can help prevent side effects. Note: - objects and arrays that are destructured from the argument object are NOT - cloned. -4. Linters can warn you about unused properties, which would be impossible - without destructuring. +1. Birisi fonksiyonun imzasına baktığında, hangi özelliklerin kullanıldığını hemen anlar.. +2. Adlandırılmış parametreleri simüle etmek için kullanılabilir. +3. Ayrıştırma işlemi ayrıca fonksiyona iletilen argüman nesnesinin belirtilen ilk değerlerini de klonlar. Bu, yan etkilerin önlenmesine yardımcı olabilir. Not: Argüman nesnelerinden ayrıştırılan nesneler ve diziler klonlanmaz! +4. Linters, sizi kullanılmayan değerler için uyarabilir bu da ayrıştırmadan imkansız olurdu. -**Bad:** +**Kötü:** ```javascript function createMenu(title, body, buttonText, cancellable) { @@ -270,7 +234,7 @@ createMenu("Foo", "Bar", "Baz", true); ``` -**Good:** +**İyi:** ```javascript function createMenu({ title, body, buttonText, cancellable }) { @@ -285,17 +249,13 @@ createMenu({ }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Functions should do one thing +### Fonksiyonlar bir şey yapmalı -This is by far the most important rule in software engineering. When functions -do more than one thing, they are harder to compose, test, and reason about. -When you can isolate a function to just one action, it can be refactored -easily and your code will read much cleaner. If you take nothing else away from -this guide other than this, you'll be ahead of many developers. +Bu, yazılım mühendisliğinde açık ara en önemli kuraldır. Fonksiyonlar birden fazla şey yaptığında, birleştirmesi, test etmesi ve anlamdırılması daha zordur. Bir fonksiyonu yalnızca bir eyleme ayırabildiğinizde, kolayca yeniden düzenlenebilir ve kodunuz çok daha temiz okunur. Bu yazıdan başka bir şey almazsanız dahi sadece bununla birçok geliştiricinin önünde olacaksınız. -**Bad:** +**Kötü:** ```javascript function emailClients(clients) { @@ -308,7 +268,7 @@ function emailClients(clients) { } ``` -**Good:** +**İyi:** ```javascript function emailActiveClients(clients) { @@ -321,11 +281,11 @@ function isActiveClient(client) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Function names should say what they do +### Fonksiyon isimleri ne yaptıklarını anlatmalı -**Bad:** +**Kötü:** ```javascript function addToDate(date, month) { @@ -334,11 +294,11 @@ function addToDate(date, month) { const date = new Date(); -// It's hard to tell from the function name what is added +// Fonksiyonun adından ne eklendiğini söylemek zor addToDate(date, 1); ``` -**Good:** +**İyi:** ```javascript function addMonthToDate(month, date) { @@ -349,15 +309,13 @@ const date = new Date(); addMonthToDate(1, date); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Functions should only be one level of abstraction +### Fonksiyonlar soyutlaştırmadan sadece bir seviye uzak olmalıdır -When you have more than one level of abstraction your function is usually -doing too much. Splitting up functions leads to reusability and easier -testing. +Birden fazla soyutlama seviyeniz olduğunda fonksiyonunuz genellikle çok fazla şey yapar. Fonksiyonların bölünmesi, yeniden kullanılabilirliği ve daha kolay test yapılmasını sağlayacak. -**Bad:** +**Kötü:** ```javascript function parseBetterJSAlternative(code) { @@ -384,7 +342,7 @@ function parseBetterJSAlternative(code) { } ``` -**Good:** +**İyi:** ```javascript function parseBetterJSAlternative(code) { @@ -421,32 +379,19 @@ function parse(tokens) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Remove duplicate code +### Tekrarlanan kodu sil -Do your absolute best to avoid duplicate code. Duplicate code is bad because it -means that there's more than one place to alter something if you need to change -some logic. +Tekrarlanan kodlardan kaçınmak için elinizden geleni yapın. Tekrarlanan kod kötü çünkü bazı mantığı değiştirmeniz gerekirse bir şeyi değiştirmek için birden fazla yer olduğu anlamına geliyor. -Imagine if you run a restaurant and you keep track of your inventory: all your -tomatoes, onions, garlic, spices, etc. If you have multiple lists that -you keep this on, then all have to be updated when you serve a dish with -tomatoes in them. If you only have one list, there's only one place to update! +Bir restoran işlettiğinizi ve stoklarınızı takip ettiğinizi düşünün: tüm domates, soğan, sarımsak, baharat, vb. onlara. Yalnızca bir listeniz varsa, güncellenecek tek bir yer vardır! -Oftentimes you have duplicate code because you have two or more slightly -different things, that share a lot in common, but their differences force you -to have two or more separate functions that do much of the same things. Removing -duplicate code means creating an abstraction that can handle this set of -different things with just one function/module/class. +Çoğunlukla yinelenen kodunuz vardır, çünkü çoğu şeyi ortak paylaşsalar dahi çok küçük bir kaç farklılık vardır, ancak farklılıkları sizi aynı şeylerin çoğunu yapan iki veya daha fazla ayrı fonksiyona sahip olmaya zorlar. Tekrarlanan kodun kaldırılması, bu farklı şeyleri tek bir fonksiyon / modül / sınıfla işleyebilecek bir soyutlama oluşturmak anlamına gelir. -Getting the abstraction right is critical, that's why you should follow the -SOLID principles laid out in the _Classes_ section. Bad abstractions can be -worse than duplicate code, so be careful! Having said this, if you can make -a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself -updating multiple places anytime you want to change one thing. +Soyutlamayı doğru yapmak kritik öneme sahiptir, bu yüzden _Sınıflar_ bölümünde belirtilen SOLID ilkelerini izlemelisiniz. Kötü soyutlamalar yinelenen koddan daha kötü olabilir, bu yüzden dikkatli olun! Bunu söyledikten sonra, eğer iyi bir soyutlama yapabilirseniz, yapın! Kendinizi tekrarlamayın, aksi takdirde bir şeyi değiştirmek istediğinizde kendinizi birden fazla yeri güncellerken bulacaksınız. -**Bad:** +**Kötü:** ```javascript function showDeveloperList(developers) { @@ -480,7 +425,7 @@ function showManagerList(managers) { } ``` -**Good:** +**İyi:** ```javascript function showEmployeeList(employees) { @@ -507,11 +452,11 @@ function showEmployeeList(employees) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Set default objects with Object.assign +### Object.assign ile varsayılan nesneleri ayarlama -**Bad:** +**Kötü:** ```javascript const menuConfig = { @@ -532,12 +477,12 @@ function createMenu(config) { createMenu(menuConfig); ``` -**Good:** +**İyi:** ```javascript const menuConfig = { title: "Order", - // User did not include 'body' key + // Kullanıcı 'body' eklemedi buttonText: "Send", cancellable: true }; @@ -553,20 +498,20 @@ function createMenu(config) { config ); - // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config çıktısı şimdi : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't use flags as function parameters +### Fonksiyon parametrelerinde işaretleme kullanma -Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. +İşaretlemeler fonksiyonunuzun birden fazla şey yaptığını gösterir. Fonkisyonlar sadece tek şey yapmalılar. Eğer aşağıdakine benzer değişiklere ve mantıksal operatorlere sahip fonksiyonunuz varsa fonksiyonlarınızı ayırın. -**Bad:** +**Kötü:** ```javascript function createFile(name, temp) { @@ -578,7 +523,7 @@ function createFile(name, temp) { } ``` -**Good:** +**İyi:** ```javascript function createFile(name) { @@ -590,30 +535,21 @@ function createTempFile(name) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid Side Effects (part 1) +### Yan Etkilerden Kaçının (Bölüm 1) -A function produces a side effect if it does anything other than take a value in -and return another value or values. A side effect could be writing to a file, -modifying some global variable, or accidentally wiring all your money to a -stranger. +Bir fonksiyon, bir değeri alıp başka bir değer veya değer döndürmekten başka bir şey yaparsa yan etki üretir. Bir yan etki, bir dosyaya yazma, bazı global değişkeni değiştirme veya yanlışlıkla tüm paranızı bir yabancıya gönderme olabilir. -Now, you do need to have side effects in a program on occasion. Like the previous -example, you might need to write to a file. What you want to do is to -centralize where you are doing this. Don't have several functions and classes -that write to a particular file. Have one service that does it. One and only one. +Şimdi, zaman zaman bir programda yan etkilere sahip olmanız gerekir. Önceki örnekte olduğu gibi, bir dosyaya yazmanız gerekebilir. Yapmak istediğiniz, bunu yapacağınız yeri merkezileştirmektir. Birden fazla yazma işlemi yapan fonksiyonlar veya sınıflar yapmayın. Bunu yapan bir servisiniz olsun. Bir ve sadece bir. -The main point is to avoid common pitfalls like sharing state between objects -without any structure, using mutable data types that can be written to by anything, -and not centralizing where your side effects occur. If you can do this, you will -be happier than the vast majority of other programmers. +Buradaki ana nokta, herhangi bir yapıya sahip olmayan nesneler arasında durumu(state) paylaşmak, herhangi bir şeyle yazılabilen değiştirilebilir(mutable) veri türlerini kullanmak ve yan etkilerinizin nerede ortaya çıktığını merkezileştirmemek gibi yaygın tuzaklardan kaçınmaktır. Bunu yapabilirseniz, diğer programcıların büyük çoğunluğundan daha mutlu olacaksınız. -**Bad:** +**Kötü:** ```javascript -// 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. +// Aşağıdaki fonksiyon Global değişkeni refere alıyor +// Bu adı kullanan başka bir fonksiyonumuz olsaydı, şimdi bir dizi olurdu ve onu bozacaktı. let name = "Ryan McDermott"; function splitIntoFirstAndLastName() { @@ -625,7 +561,7 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**Good:** +**İyi:** ```javascript function splitIntoFirstAndLastName(name) { @@ -639,44 +575,27 @@ console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid Side Effects (part 2) +### Yan Etkilerden Kaçının (Bölüm 2) -In JavaScript, primitives are passed by value and objects/arrays are passed by -reference. In the case of objects and arrays, if your function makes a change -in a shopping cart array, for example, by adding an item to purchase, -then any other function that uses that `cart` array will be affected by this -addition. That may be great, however it can be bad too. Let's imagine a bad -situation: +JavaScript'te, temel öğeler değerlerle aktarılır (passed by value) ve objeler/diziler referans ile aktarılır (passed by reference). -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. +Bu durumda, nesnelerde ve dizilerde fonksiyonunuz bir değişiklik yaparsa +örneğin, bir alışveriş sepeti dizisinde, satın almak için bir öğe ekleyerek, +bu `cart` dizisini kullanan diğer fonksiyonlar bu eklemeden etkilenecektir. +Bu harika olabilir, ancak kötü de olabilir. Kötü bir hayal edelim +durum: -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. +Kullanıcı, 'Satın Al' butonuna basar ve bu `satınal` fonksiyonu sunucuya bir istek atarak `sepet` dizisini sunucuya gönderir. Kötü bir ağ bağlantısı nedeniyle, `satınal` işlevi isteği yeniden denemeye devam etmelidir. Şimdi, bu arada kullanıcı yanlışlıkla ağ isteği başlamadan istemedikleri bir öğenin üzerine "Sepete Ekle" düğmesini tıklarsa ne olur? Bu olursa ve ağ isteği başlarsa, yanlışlıkla satın alınan öğeyi gönderir çünkü alışveriş sepeti dizisine, `ürünüSepeteEkle` işlevinin istenmeyen bir öğe ekleyerek değiştirdiği bir referansı vardır. `ürünüSepeteEkle` ın her zaman `sepeti` klonlaması, düzenlemesi ve klonu döndürmesi için harika bir çözüm olacaktır. Bu, alışveriş sepetinin referansını tutan başka hiçbir işlevin herhangi bir değişiklikten etkilenmemesini sağlar. -Two caveats to mention to this approach: +Bu yaklaşıma değinecek iki uyarı: -1. 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! +1. Girilen nesnesini gerçekten değiştirmek istediğiniz durumlar olabilir, ancak bunu uyguladığınızda, bu vakaların oldukça nadir olduğunu göreceksiniz. Çoğu şeyin yan etkisi olmayacak şekilde yeniden düzenlenebilir! -2. 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](https://facebook.github.io/immutable-js/) 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. +2. Büyük nesneleri klonlamak performans açısından çok pahalı olabilir. Neyse ki, bu pratikte büyük bir sorun değildir, çünkü bu tür programlama yaklaşımlarını manuel yapmaktansa daha hızlı olmasını ve büyük nesneleri ve dizileri klonlanlarken daha az bellek harcayan [harika kütüphaneler](https://facebook.github.io/immutable-js/) vardır. -**Bad:** +**Kötü:** ```javascript const addItemToCart = (cart, item) => { @@ -684,7 +603,7 @@ const addItemToCart = (cart, item) => { }; ``` -**Good:** +**İyi:** ```javascript const addItemToCart = (cart, item) => { @@ -692,21 +611,17 @@ const addItemToCart = (cart, item) => { }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +### Global fonksiyonlar yazma -### Don't write to global functions +Globalleri kirletmek JavaScript'te kötü bir uygulamadır, çünkü başka bir kütüphaneyle çakıştırabilirsiniz ve API'lerinizi kullananlar kişiler canlıya çıkana kadar bu aksi durumların farkında olmayabilir. -Polluting globals is a bad practice in JavaScript because you could clash with another -library and the user of your API would be none-the-wiser until they get an -exception in production. Let's think about an example: what if you wanted to -extend JavaScript's native Array method to have a `diff` method that could -show the difference between two arrays? You could write your new function -to the `Array.prototype`, but it could clash with another library that tried -to do the same thing. What if that other library was just using `diff` to find -the difference between the first and last elements of an array? This is why it -would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. +Bir örnek düşünelim: JavaScript'in yerel kütüphanesindeki dizi `diff` methodunu genişletmek ve iki dizinin farklılıklarını göstermek istediğinizi var sayalım. JavaScript'in yerel Array yöntemini iki dizi arasındaki farkı gösterebilecek bir "diff" yöntemine genişletmek mi istiyorsunuz? -**Bad:** +Yeni fonksiyonunuzu `Array.prototype`'e yazabilirsiniz, ancak aynı şeyi yapmaya çalışan başka bir kütüphane ile çakışabilir. Ya diğer kütüphane bir 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 `Array` globalini genişletmek çok daha iyi olurdu. + +**Kötü:** ```javascript Array.prototype.diff = function diff(comparisonArray) { @@ -715,7 +630,7 @@ Array.prototype.diff = function diff(comparisonArray) { }; ``` -**Good:** +**İyi:** ```javascript class SuperArray extends Array { @@ -726,15 +641,13 @@ class SuperArray extends Array { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Favor functional programming over imperative programming +### Zorunlu programlama yerine fonksiyonel programlamayı tercih edin -JavaScript isn't a functional language in the way that Haskell is, but it has -a functional flavor to it. Functional languages can be cleaner and easier to test. -Favor this style of programming when you can. +JavaScript, Haskell'in olduğu gibi işlevsel bir dil değildir, ancak işlevsel bir tadı vardır. İşlevsel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiğinizde bu tarz bir programlama yapın. -**Bad:** +**Kötü:** ```javascript const programmerOutput = [ @@ -763,7 +676,7 @@ for (let i = 0; i < programmerOutput.length; i++) { } ``` -**Good:** +**İyi:** ```javascript const programmerOutput = [ @@ -791,11 +704,11 @@ const totalOutput = programmerOutput.reduce( ); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Encapsulate conditionals +### Koşulları kapsamak -**Bad:** +**Kötü:** ```javascript if (fsm.state === "fetching" && isEmpty(listNode)) { @@ -803,7 +716,7 @@ if (fsm.state === "fetching" && isEmpty(listNode)) { } ``` -**Good:** +**İyi:** ```javascript function shouldShowSpinner(fsm, listNode) { @@ -815,11 +728,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** ### Avoid negative conditionals -**Bad:** +**Kötü:** ```javascript function isDOMNodeNotPresent(node) { @@ -831,7 +744,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**Good:** +**İyi:** ```javascript function isDOMNodePresent(node) { @@ -843,20 +756,14 @@ if (isDOMNodePresent(node)) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid conditionals +### Koşullardan kaçının -This seems like an impossible task. Upon first hearing this, most people say, -"how am I supposed to do anything without an `if` statement?" The answer is that -you can use polymorphism to achieve the same task in many cases. The second -question is usually, "well that's great but why would I want to do that?" The -answer is a previous clean code concept we learned: a function should only do -one thing. When you have classes and functions that have `if` statements, you -are telling your user that your function does more than one thing. Remember, -just do one thing. +Bu imkansız bir görev gibi görünüyor. Bunu ilk kez duyduktan sonra, çoğu kişi "`if` ifadesi olmadan nasıl bir şey yapmam gerekiyor?" Cevap şu ki +birçok durumda aynı görevi yerine getirmek için polimorfizm kullanabilirsiniz. İkinci soru genellikle, "peki bu harika ama neden bunu yapmak isteyeyim ki?" Cevap, daha önce öğrendiğimiz temiz bir kod kavramıydı: bir fonksiyon sadece bir şey yapmalı. `If` ifadeleri olan sınıflarınız ve fonksiyonlarınız olduğunda, kullanıcılara işlevinizin birden fazla şey yaptığını söylüyor. Unutmayın, sadece bir şey yapın. -**Bad:** +**Kötü:** ```javascript class Airplane { @@ -874,7 +781,7 @@ class Airplane { } ``` -**Good:** +**İyi:** ```javascript class Airplane { @@ -903,16 +810,13 @@ class Cessna extends Airplane { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid type-checking (part 1) +### Yazım denetiminden kaçının (Bölüm 1) -JavaScript is untyped, which means your functions can take any type of argument. -Sometimes you are bitten by this freedom and it becomes tempting to do -type-checking in your functions. There are many ways to avoid having to do this. -The first thing to consider is consistent APIs. +JavaScript türsüzdür, yani işlevleriniz her türlü argümanı alabilir. Bazen bu özgürlük bizi oltaya getirebilir ve fonksiyonlarınızda tip kontrolü yapmak cazip hale gelir. Bunu yapmaktan kaçınmanın birçok yolu vardır. Dikkate alınması gereken ilk şey tutarlı API'lardır. -**Bad:** +**Kötü:** ```javascript function travelToTexas(vehicle) { @@ -924,7 +828,7 @@ function travelToTexas(vehicle) { } ``` -**Good:** +**İyi:** ```javascript function travelToTexas(vehicle) { @@ -932,21 +836,14 @@ function travelToTexas(vehicle) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid type-checking (part 2) +### Yazım denetiminden kaçının (Bölüm 2) -If you are working with basic primitive values like strings and integers, -and you can't use polymorphism but you still feel the need to type-check, -you should consider using TypeScript. It is an excellent alternative to normal -JavaScript, as it provides you with static typing on top of standard JavaScript -syntax. The problem with manually type-checking normal JavaScript is that -doing it well requires so much extra verbiage that the faux "type-safety" you get -doesn't make up for the lost readability. Keep your JavaScript clean, write -good tests, and have good code reviews. Otherwise, do all of that but with -TypeScript (which, like I said, is a great alternative!). +Dizeler ve tamsayılar gibi temel ilkel değerlerle çalışıyorsanız, +ve polimorfizm kullanamazsınız, ancak yine de yazım denetimi yapma gereğini hissediyorsanız, TypeScript kullanmayı düşünmelisiniz. Standart JavaScript sentaks üstünde statik yazım sağladığı için normal JavaScript'e mükemmel bir alternatiftir. Manuel olarak normal JavaScript'i kontrol etmeyle ilgili sorun, iyi yapmak o kadar fazla ayrıntılı gerektirir ki, elde ettiğiniz sahte "tip güvenliği" kaybettiğiniz okunabilirliği telafi etmez. JavaScript'inizi temiz tutun, iyi testler yazın ve iyi kod incelemelerine sahip olun. Aksi takdirde, hepsini TypeScript ile yapın (dediğim gibi harika bir alternatif!). -**Bad:** +**Kötü:** ```javascript function combine(val1, val2) { @@ -961,7 +858,7 @@ function combine(val1, val2) { } ``` -**Good:** +**İyi:** ```javascript function combine(val1, val2) { @@ -969,27 +866,25 @@ function combine(val1, val2) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +### Aşırı optimizasyon yapma -### Don't over-optimize +Modern tarayıcılar çalışma zamanında çok sayıda optimizasyon yapar. Çoğu zaman, eğer optimize ediyorsanız, sadece zamanınızı boşa harcıyorsunuz demektir. -Modern browsers do a lot of optimization under-the-hood at runtime. A lot of -times, if you are optimizing then you are just wasting your time. [There are good -resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) -for seeing where optimization is lacking. Target those in the meantime, until -they are fixed if they can be. +Optimizasyonun nerede olmadığını görmek için [iyi kaynaklar](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) vardır. Bunları optimize edene kadar işaretleyebilirsiniz. -**Bad:** +**Kötü:** ```javascript -// On old browsers, each iteration with uncached `list.length` would be costly -// because of `list.length` recomputation. In modern browsers, this is optimized. +// Eski tarayıcılarda, önbelleğe alınmamış "list.length" içeren her yineleme maliyetli olacaktır +// list.length yeniden hesaplanması nedeniyle. Modern tarayıcılarda bu optimize edilmiştir. for (let i = 0, len = list.length; i < len; i++) { // ... } ``` -**Good:** +**İyi:** ```javascript for (let i = 0; i < list.length; i++) { @@ -997,15 +892,13 @@ for (let i = 0; i < list.length; i++) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Remove dead code +### Kullanılmayan kodları silin -Dead code is just as bad as duplicate code. There's no reason to keep it in -your codebase. If it's not being called, get rid of it! It will still be safe -in your version history if you still need it. +Ölü kod, yinelenen kod kadar kötü. Kod tabanınızda tutmak için bir neden yoktur. Eğer çağrılmazsa, ondan kurtulun! Hala ihtiyacınız varsa sürüm geçmişinizde güvende olacaktır. -**Bad:** +**Kötü:** ```javascript function oldRequestModule(url) { @@ -1020,7 +913,7 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**Good:** +**İyi:** ```javascript function newRequestModule(url) { @@ -1031,25 +924,21 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Objects and Data Structures** +## **Nesneler ve Veri Yapıları** -### Use getters and setters +### Alıcıları ve ayarlayıcıları kullanma (Getters & Setters) -Using getters and setters to access data on objects could be better than simply -looking for a property on an object. "Why?" you might ask. Well, here's an -unorganized list of reasons why: +Nesnelerdeki verilere erişmek için alıcıları ve ayarlayıcıları kullanmak, bir nesnedeki bir özelliği aramaktan daha iyi olabilir. "Neden?" sorabilirsiniz. Pekala, işte organize edilmeden nedenlerin bir listesi: -- When you want to do more beyond getting an object property, you don't have - to look up and change every accessor in your codebase. -- Makes adding validation simple when doing a `set`. -- Encapsulates the internal representation. -- Easy to add logging and error handling when getting and setting. -- You can lazy load your object's properties, let's say getting it from a - server. +- Bir nesne özelliği almanın ötesinde daha fazlasını yapmak istediğinizde, kod tabanınızdaki her erişimciye bakmanız ve değiştirmeniz gerekmez. +- `set`yaparken doğrulama basitçe yapılabilir. +- Dahili gösterimi içine alır. +- Alma ve ayarlama sırasında kayıtlamayı(logging) ve hata yönetimi(error handling) eklemek kolaydır. +- Diyelimki sunucudan alıyorsunuz, nesnenizin özelliklerini tembel(lazy load) olarak yükleyebilirsiniz. -**Bad:** +**Kötü:** ```javascript function makeBankAccount() { @@ -1065,19 +954,19 @@ const account = makeBankAccount(); account.balance = 100; ``` -**Good:** +**İyi:** ```javascript function makeBankAccount() { - // this one is private + // Bu fonksiyon özelinde (private) let balance = 0; - // a "getter", made public via the returned object below + // "alıcı"yı, genel(public) objeyi döndürerek yap function getBalance() { return balance; } - - // a "setter", made public via the returned object below + + // "ayarlayıcı"yı, genel(public) objeyi döndürerek yap function setBalance(amount) { // ... validate before updating the balance balance = amount; @@ -1094,13 +983,13 @@ const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Make objects have private members +### Nesnelerin özel üyeleri olmasını sağlama This can be accomplished through closures (for ES5 and below). -**Bad:** +**Kötü:** ```javascript const Employee = function(name) { @@ -1117,7 +1006,7 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**Good:** +**İyi:** ```javascript function makeEmployee(name) { @@ -1134,18 +1023,15 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Classes** +## **Sınıflar** -### Prefer ES2015/ES6 classes over ES5 plain functions +### ES5 düz fonksiyonlar yerine ES2015 / ES6 sınıflarını tercih et -It's very difficult to get readable class inheritance, construction, and method -definitions for classical ES5 classes. If you need inheritance (and be aware -that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over -classes until you find yourself needing larger and more complex objects. +Klasik ES5 sınıfları için okunabilir sınıf mirası, yapısı ve yöntem tanımları almak çok zordur. Miras almaya(inheritance) ihtiyacınız varsa (ve gerekmeyebileceğini unutmayın), ES2015 / ES6 sınıflarını tercih edin. Bununla birlikte, kendinizi daha büyük ve daha karmaşık nesnelere ihtiyaç duyana kadar küçük işlevlere tercih edin. -**Bad:** +**Kötü:** ```javascript const Animal = function(age) { @@ -1185,7 +1071,7 @@ Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ``` -**Good:** +**İyi:** ```javascript class Animal { @@ -1221,17 +1107,13 @@ class Human extends Mammal { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Use method chaining +### Yöntem zincirini kullan -This pattern is very useful in JavaScript and you see it in many libraries such -as jQuery and Lodash. It allows your code to be expressive, and less verbose. -For that reason, I say, use method chaining and take a look at how clean your code -will be. In your class functions, simply return `this` at the end of every function, -and you can chain further class methods onto it. +Bu desen JavaScript'te çok kullanışlıdır ve jQuery ve Lodash gibi birçok kütüphanede görürsünüz. Kodunuzun etkileyici ve daha az detaylı olmasını sağlar. Bu nedenle, diyorum ki, yöntem zincirleme kullanın ve kodunuzun ne kadar temiz olacağını göreceksiniz. Sınıf fonksiyonlarınızda, her fonksiyonun sonunda `this`'i döndürmeniz yeterlidir ve daha fazla sınıf yöntemini buna zincirleyebilirsiniz. -**Bad:** +**Kötü:** ```javascript class Car { @@ -1263,7 +1145,7 @@ car.setColor("pink"); car.save(); ``` -**Good:** +**İyi:** ```javascript class Car { @@ -1275,25 +1157,25 @@ class Car { setMake(make) { this.make = make; - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } setModel(model) { this.model = model; - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } setColor(color) { this.color = color; - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } save() { console.log(this.make, this.model, this.color); - // NOTE: Returning this for chaining + // NOT: Zincirleme için `this` döndür return this; } } @@ -1301,28 +1183,19 @@ class Car { const car = new Car("Ford", "F-150", "red").setColor("pink").save(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Prefer composition over inheritance +### Miras yerine kompozisyon tercih et -As stated famously in [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, -you should prefer composition over inheritance where you can. There are lots of -good reasons to use inheritance and lots of good reasons to use composition. -The main point for this maxim is that if your mind instinctively goes for -inheritance, try to think if composition could model your problem better. In some -cases it can. +Dörtlü çete tarafından başlatılan ünlü [_Tasarım Desenleri_](https://en.wikipedia.org/wiki/Design_Patterns) gibi, siz de kompozisyonu, miras bırakmaya yerine göre tercih etmelisiniz. Miras kullanmak için birçok iyi neden ve kompozisyon kullanmak için birçok iyi neden vardır. Bu maksimum nokta için ana nokta, zihniniz içgüdüsel olarak miras kullanma için giderse, kompozisyon sorununuzu daha iyi modelleyip değiştiremeyeceğini düşünmeye çalışın. Bazı durumlarda olabilir. -You might be wondering then, "when should I use inheritance?" It -depends on your problem at hand, but this is a decent list of when inheritance -makes more sense than composition: +O zaman "ne zaman miras kullanmalıyım?" diye merak ediyor olabilirsiniz. O eldeki probleminize bağlıdır, ancak bu mirasın kompozisyondan daha mantıklı olduğu iyi bir listedir: -1. Your inheritance represents an "is-a" relationship and not a "has-a" - relationship (Human->Animal vs. User->UserDetails). -2. You can reuse code from the base classes (Humans can move like all animals). -3. You want to make global changes to derived classes by changing a base class. - (Change the caloric expenditure of all animals when they move). +1. Mirasınız "has-a" değil, "is-a" ilişkisini temsil eder ilişki (İnsan-> Hayvan ve Kullanıcı-> KullanıcıAyrıntıları). +2. Temel sınıflardan kodu yeniden kullanabilirsiniz (İnsanlar tüm hayvanlar gibi hareket edebilir). +3. Temel sınıfı değiştirerek türetilmiş sınıflarda genel değişiklikler yapmak istiyorsunuz. (Hareket ettiklerinde tüm hayvanların kalori harcamalarını değiştirin). -**Bad:** +**Kötü:** ```javascript class Employee { @@ -1334,7 +1207,7 @@ class Employee { // ... } -// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee +// Kötü çünkü Çalışanların(Employees) vergi bilgisi 'var'. ÇalışanVergiBilgisi(EmployeeTaxData) bir çeşit Çalışan(Employee) değil. class EmployeeTaxData extends Employee { constructor(ssn, salary) { super(); @@ -1346,7 +1219,7 @@ class EmployeeTaxData extends Employee { } ``` -**Good:** +**İyi:** ```javascript class EmployeeTaxData { @@ -1371,22 +1244,15 @@ class Employee { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** ## **SOLID** -### Single Responsibility Principle (SRP) +### Tek Sorumluluk İlkesi (SRP) -As stated in Clean Code, "There should never be more than one reason for a class -to change". It's tempting to jam-pack a class with a lot of functionality, like -when you can only take one suitcase on your flight. The issue with this is -that your class won't be conceptually cohesive and it will give it many reasons -to change. Minimizing the amount of times you need to change a class is important. -It's important because if too much functionality is in one class and you modify -a piece of it, it can be difficult to understand how that will affect other -dependent modules in your codebase. +Temiz Kod'da belirtildiği gibi, "Bir sınıfın değişmesi için asla birden fazla sebep olmamalıdır". Bir sınıfı tıklım tıklım bir çok işlevsellikle doldurmak çekici gelebilir, tıpkı uçuşlarda yanına alabileceğiniz bir valiz gibi. Bununla ilgili sorun, sınıfınızın kavramsal olarak uyumlu olmayacağı ve değişmesi için birçok neden vereceği yönündedir. Bir sınıfı değiştirmek için ihtiyaç duyduğunuz sayıyı en aza indirmek önemlidir. Bir sınıfta çok fazla işlevsellik varsa 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. -**Bad:** +**Kötü:** ```javascript class UserSettings { @@ -1406,7 +1272,7 @@ class UserSettings { } ``` -**Good:** +**İyi:** ```javascript class UserAuth { @@ -1433,16 +1299,13 @@ class UserSettings { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Open/Closed Principle (OCP) +### Açık / Kapalı Prensibi (OCP) -As stated by Bertrand Meyer, "software entities (classes, modules, functions, -etc.) should be open for extension, but closed for modification." What does that -mean though? This principle basically states that you should allow users to -add new functionalities without changing existing code. +Bertrand Meyer tarafından belirtildiği gibi, "yazılım varlıkları (sınıflar, modüller, işlevler, vb.) Genişletme 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. -**Bad:** +**Kötü:** ```javascript class AjaxAdapter extends Adapter { @@ -1486,7 +1349,7 @@ function makeHttpCall(url) { } ``` -**Good:** +**İyi:** ```javascript class AjaxAdapter extends Adapter { @@ -1524,24 +1387,15 @@ class HttpRequester { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Liskov Substitution Principle (LSP) +### Liskov’un Yerine Geçme Prensibi (LSP) -This is a scary term for a very simple concept. It's formally defined as "If S -is a subtype of T, then objects of type T may be replaced with objects of type S -(i.e., objects of type S may substitute objects of type T) without altering any -of the desirable properties of that program (correctness, task performed, -etc.)." That's an even scarier definition. +Bu çok basit bir kavram için korkutucu bir terimdir.Resmi olarak "S, T'nin bir alt tipiyse, o zaman T tipi nesnelerin yerine S tipi nesneler (yani, S tipi nesneler T programındaki nesnelerin yerine geçebilir), bu programın istenen özelliklerini değiştirmeden değiştirilebilir (doğruluk, yapılan görev vb.) " Bu daha da korkunç bir tanım. -The best explanation for this is if you have a parent class and a child class, -then the base class and child class can be used interchangeably without getting -incorrect results. This might still be confusing, so let's take a look at the -classic Square-Rectangle example. Mathematically, a square is a rectangle, but -if you model it using the "is-a" relationship via inheritance, you quickly -get into trouble. +Bunun için en iyi açıklama, bir üst 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 elde etmeden birbirinin yerine kullanılabilir.Bu hala kafa karıştırıcı olabilir, bu yüzden klasik Kare Dikdörtgen örneğine bakalım. Matematiksel olarak, bir kare bir dikdörtgendir, ancak miras yoluyla "is-a" ilişkisini kullanarak model verirseniz, hızlı bir şekilde sorun yaşarsınız. -**Bad:** +**Kötü:** ```javascript class Rectangle { @@ -1587,7 +1441,7 @@ function renderLargeRectangles(rectangles) { rectangles.forEach(rectangle => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. + const area = rectangle.getArea(); // KÖTÜ: Kare için 25 değerini döndürür. 20 olmalı. rectangle.render(area); }); } @@ -1596,7 +1450,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**Good:** +**İyi:** ```javascript class Shape { @@ -1643,25 +1497,17 @@ const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Interface Segregation Principle (ISP) +### Arayüzlerin Ayrımı Prensibi (ISP) -JavaScript doesn't have interfaces so this principle doesn't apply as strictly -as others. However, it's important and relevant even with JavaScript's lack of -type system. +JavaScript'in arayüzleri yoktur, bu nedenle bu ilke diğerleri kadar kesin olarak geçerli değildir. Bununla birlikte, JavaScript'in tür sistemi eksikliğinde bile önemli ve alâkalıdır. -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. +ISP, "kullanıcılar kullanmadığı arabirimlere bağımlı olmaya zorlanmamalıdır." der. Arabirimler, `Duck Typing` yüzünden JavaScript'de üstü kapalı anlaşmalardır. -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". +Bu ilkeyi JavaScript'te gösteren iyi bir örnek, sınıflar büyük ayar nesneleri gerektirir. Kullanıcıların büyük miktarda seçenek ayarlamalarını istememek gerekli değildir, çünkü çoğu zaman tüm ayarlara ihtiyaç duymazlar. Bunları isteğe bağlı yapmak, bir "büyük arayüzü" olmasını önlemeye yardımcı olur. -**Bad:** +**Kötü:** ```javascript class DOMTraverser { @@ -1682,12 +1528,12 @@ class DOMTraverser { const $ = new DOMTraverser({ rootNode: document.getElementsByTagName("body"), - animationModule() {} // Most of the time, we won't need to animate when traversing. + animationModule() {} // Coğunlukla, bunu canlandırmamız gerekmeyecek // ... }); ``` -**Good:** +**İyi:** ```javascript class DOMTraverser { @@ -1721,32 +1567,24 @@ const $ = new DOMTraverser({ }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Dependency Inversion Principle (DIP) +### Bağlılığı Tersine Çevirme Prensibi (DIP) -This principle states two essential things: +Bu ilke iki temel şeyi ifade eder: -1. High-level modules should not depend on low-level modules. Both should - depend on abstractions. -2. Abstractions should not depend upon details. Details should depend on - abstractions. +1. Yüksek seviyeli modüller, düşük seviyeli modüllere bağlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır. +2. Soyutlamalar detaylara bağlı olmamalıdır. Ayrıntılar soyutlamalara bağlı olmalıdır. -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. +İlk başta bunu anlamak zor olabilir, ancak AngularJS ile çalıştıysanız, bu prensibin Bağımlılık Enjeksiyonu (DI) şeklinde bir uygulamasını gördünüz. Aynı kavramlar olmasalar da, DIP yüksek seviyede modüllerin düşük seviyeli modüllerinin detaylarını bilmelerini ve ayarlamalarını sağlar. +Bunu DI ile başarabilir. Bunun büyük bir yararı, modüller arasındaki bağlantıyı azaltmasıdır. Eşleştirme(Coupling) çok kötü bir gelişme modelidir çünkü +kodunuzu yeniden düzenleme zorlaştırır. -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. +Daha önce belirtildiği gibi, JavaScript'in arayüzleri yoktur, bu nedenle soyutlamalar örtük sözleşmelere bağlıdır. +Yani, bir nesnenin / sınıfın başka bir nesneye / sınıfa maruz bıraktığı yöntemler ve özellikler. +Aşağıdaki örnekte, örtük sözleşme, bir `InventoryTracker` için herhangi bir Request modülünün `requestItems` yöntemine sahip olacağıdır. -**Bad:** +**Kötü:** ```javascript class InventoryRequester { @@ -1763,8 +1601,8 @@ 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` + // KÖTÜ: Belirli bir istek uygulamasına bağımlılık yarattık. +    // requestItems sadece `request` 'e bağlımlı olmalıdır. this.requester = new InventoryRequester(); } @@ -1779,7 +1617,7 @@ const inventoryTracker = new InventoryTracker(["apples", "bananas"]); inventoryTracker.requestItems(); ``` -**Good:** +**İyi:** ```javascript class InventoryTracker { @@ -1815,8 +1653,8 @@ class InventoryRequesterV2 { } } -// By constructing our dependencies externally and injecting them, we can easily -// substitute our request module for a fancy new one that uses WebSockets. +// Bağımlılıklarımızı harici olarak yapılandırarak ve enjekte ederek +// istek modülümüzü WebSockets kullanan yeni ve havalı bir modülle kolayca değiştirebiliriz. const inventoryTracker = new InventoryTracker( ["apples", "bananas"], new InventoryRequesterV2() @@ -1824,28 +1662,19 @@ const inventoryTracker = new InventoryTracker( inventoryTracker.requestItems(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +## **Testler** -## **Testing** +Test etmek canlıya çıkmaktan bile önemlidir. Eğer testiniz yoksa veya yetersiz bir miktardaysa, kodu her gönderdiğinizde hiçbir şeyin bozulmadığına emin olmazsınız. Neyin yeterli bir miktar oluşturduğuna karar vermek takımınıza bağlıdır, %100 kapsama sahip olmak (tüm ifadeler ve şubeler) size güven ve gönül rahatlığı sağlar. Bu, harika bir test framework'üne sahip olmanın yanı sıra, [iyi bir kapsama aracı](https://gotwarlost.github.io/istanbul/) kullanmanız gerektiği anlamına gelir -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](https://gotwarlost.github.io/istanbul/). +Test yazmamanın mazereti yoktur. Çok sayıda [iyi JS test framework'ü](https://jstherightway.org/#testing-tools) vardır, bu yüzden ekibinize uyan birini bulun. -There's no excuse to not write tests. There are [plenty of good JS test frameworks](https://jstherightway.org/#testing-tools), 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. +Ekibiniz için uygun olanı bulduğunuzda, kullanılan her yeni özellik / modül için daima testler yazmayı hedefleyin. Tercih ettiğiniz yöntem Test Odaklı Geliştirme (TDD) ise, bu harika, ancak asıl mesele, herhangi bir özelliği başlatmadan veya mevcut bir özelliği yeniden düzenlemeden önce kapsama hedeflerinize ulaştığınızdan emin olmaktır. -### Single concept per test +### Her test için tek konsept -**Bad:** +**Kötü:** ```javascript import assert from "assert"; @@ -1869,7 +1698,7 @@ describe("MomentJS", () => { }); ``` -**Good:** +**İyi:** ```javascript import assert from "assert"; @@ -1895,16 +1724,15 @@ describe("MomentJS", () => { }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Concurrency** +## **Tutarlılık** -### Use Promises, not callbacks +### Promises kullanın, callback değil -Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, -Promises are a built-in global type. Use them! +'Callback'ler temiz değildir ve aşırı miktarda iç içe geçmeye neden olurlar. ES2015 / ES6 ile Promise'ler yerleşik bir global tiptir. Onları kullan! -**Bad:** +**Kötü:** ```javascript import { get } from "request"; @@ -1928,7 +1756,7 @@ get( ); ``` -**Good:** +**İyi:** ```javascript import { get } from "request-promise"; @@ -1946,17 +1774,13 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Async/Await are even cleaner than Promises +### Async/Await kullanmak Promis kullanmaktan bile daha temiz -Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await -which offer an even cleaner solution. All you need is a function that is prefixed -in an `async` keyword, and then you can write your logic imperatively without -a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features -today! +Promise'ler callback'lere göte çok daha temiz alternatiflerdir, ama ES2017/ES8 async ve await getirdi ve bu daha temiz bir çözüm sunuyor. Tek yapmanız gereken fonksiyonun başına `async` eklemek ve sonrasında mantıksal kullanımızı `then` zinciri kulanmadan yazabilirsiniz. Bugün ES2017 / ES8 özelliklerinden yararlanabiliyorsanız bunu kullanın! -**Bad:** +**Kötü:** ```javascript import { get } from "request-promise"; @@ -1974,7 +1798,7 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**Good:** +**İyi:** ```javascript import { get } from "request-promise"; @@ -1995,25 +1819,21 @@ async function getCleanCodeArticle() { getCleanCodeArticle() ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** + +## **Hata Yönetimi** + +Atılan hatalar iyi bir şey! Bunlar, programınızdaki bir şey yanlış gittiğinde çalışma zamanının başarıyla tanımlandığı anlamına gelir ve sizi geçerli yığında (stack) fonksiyonu çalıştırmayı, işlevi dururup size konsolda yığın izlemede haber vererek yapar. -## **Error Handling** +--- HERE -Thrown errors are a good thing! They mean the runtime has successfully -identified when something in your program has gone wrong and it's letting -you know by stopping function execution on the current stack, killing the -process (in Node), and notifying you in the console with a stack trace. +### Yakalanmış hataları görmemezlikten gelmeyin -### Don't ignore caught errors +Yakalanan bir hatayla hiçbir şey yapmamanız size söz konusu hatayı düzeltebilme veya tepki gösterme yeteneği vermez. Hatayı konsola (`console.log`) kaydetmek, konsola yazdırılan bir şey denizinde kaybolabileceği sıklıkta daha iyi değildir. -Doing nothing with a caught error doesn't give you the ability to ever fix -or react to said error. Logging the error to the console (`console.log`) -isn't much better as often times it can get lost in a sea of things printed -to the console. If you wrap any bit of code in a `try/catch` it means you -think an error may occur there and therefore you should have a plan, -or create a code path, for when it occurs. +Herhangi bir kod parçasını `try/catch` içerisinde kullanıyorsanız, orada bir hata olabileceğini düşündüğünüz anlamına gelir ve bu nedenle gerçekleştiği zaman için bir planınız olması veya bir kod yolu oluşturmanız gerekir. -**Bad:** +**Kötü:** ```javascript try { @@ -2023,28 +1843,27 @@ try { } ``` -**Good:** +**İyi:** ```javascript try { functionThatMightThrow(); } catch (error) { - // One option (more noisy than console.log): + // Bir secenek (console.log'dan daha dikkat çekici) console.error(error); - // Another option: + // Bir secenek daha notifyUserOfError(error); - // Another option: + // Bir secenek daha reportErrorToService(error); - // OR do all three! + // veya hepsini bir yapın } ``` -### Don't ignore rejected promises +### Reddedilmiş promisleri görmemezlikten gelmeyin -For the same reason you shouldn't ignore caught errors -from `try/catch`. +Aynı sebeplerden dolayı, `try/catch`'de oluşan hataları yok saymamalısınız -**Bad:** +**Kötü:** ```javascript getdata() @@ -2056,7 +1875,7 @@ getdata() }); ``` -**Good:** +**İyi:** ```javascript getdata() @@ -2064,36 +1883,29 @@ getdata() functionThatMightThrow(data); }) .catch(error => { - // One option (more noisy than console.log): + // Bir secenek (console.log'dan daha dikkat çekici) console.error(error); - // Another option: + // Bir secenek daha notifyUserOfError(error); - // Another option: + // Bir secenek daha reportErrorToService(error); - // OR do all three! + // veya hepsini bir yapın }); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Formatting** +## **Biçimlendirme** -Formatting is subjective. Like many rules herein, there is no hard and fast -rule that you must follow. The main point is DO NOT ARGUE over formatting. -There are [tons of tools](https://standardjs.com/rules.html) to automate this. -Use one! It's a waste of time and money for engineers to argue over formatting. +Biçimlendirme özneldir. Buradaki birçok kural gibi, uygulamanız gereken zor ve hızlı bir kural yoktur. Ana nokta biçimlendirme üzerinde tartışma DEĞİLDİR. Bunu otomatikleştirmek için [tonlarca araç](https://standardjs.com/rules.html) vardır. Birini kullan! Mühendislerin biçimlendirme konusunda tartışmaları zaman ve para kaybıdır. -For things that don't fall under the purview of automatic formatting -(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here -for some guidance. +Otomatik biçimlendirme (girintileme, sekmeler ve boşluklar, çift veya tek tırnak işaretleri vb.) Kapsamına girmeyen şeyler için bazı rehberlik için buraya bakın. -### Use consistent capitalization +### Tutarlı büyük harf kullanımı -JavaScript is untyped, so capitalization tells you a lot about your variables, -functions, etc. These rules are subjective, so your team can choose whatever -they want. The point is, no matter what you all choose, just be consistent. +JavaScript türsüzdür, bu nedenle büyük / küçük harf kullanımı değişkenleriniz, işlevleriniz vb. Hakkında çok şey anlatır. Bu kurallar özneldir, böylece ekibiniz istediklerini seçebilir. Mesele şu ki, ne seçerseniz seçin, tutarlı olun. -**Bad:** +**Kötü:** ```javascript const DAYS_IN_WEEK = 7; @@ -2109,7 +1921,7 @@ class animal {} class Alpaca {} ``` -**Good:** +**İyi:** ```javascript const DAYS_IN_WEEK = 7; @@ -2125,15 +1937,13 @@ class Animal {} class Alpaca {} ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Function callers and callees should be close +### Fonksion çağıranları ve çağrılanları yakın olmalı -If a function calls another, keep those functions vertically close in the source -file. Ideally, keep the caller right above the callee. We tend to read code from -top-to-bottom, like a newspaper. Because of this, make your code read that way. +Eğer bir fonksiyon diğer fonksiyonu çağırıyorsa, dikey olarak bu fonksiyonları kaynak dosyasında yakın tutun. İdeal olan, fonksiyonu kullanan kullandığı fonksiyonun hemen üstünde olmasıdır. We tend to read code from top-to-bottom, like a newspaper.Bu nedenle, kodunuzu bu şekilde okuyun. -**Bad:** +**Kötü:** ```javascript class PerformanceReview { @@ -2173,7 +1983,7 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**Good:** +**İyi:** ```javascript class PerformanceReview { @@ -2213,37 +2023,37 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## **Comments** +## **Yorumlar** -### Only comment things that have business logic complexity. +### Yalnızca iş mantığı karmaşıklığı olan şeyleri yorumlayın -Comments are an apology, not a requirement. Good code _mostly_ documents itself. +Yorumlar aslında bir özür, şart değil. İyi kod _çoğunlukla_ kendini belgelemektedir. -**Bad:** +**Kötü:** ```javascript function hashIt(data) { - // The hash + // Karma let hash = 0; - // Length of string + // String uzunluğu const length = data.length; - // Loop through every character in data + // Verilerdeki her karakteri gözden geçirin for (let i = 0; i < length; i++) { - // Get character code. + // Karakter kodunu al const char = data.charCodeAt(i); - // Make the hash + // Karıştır hash = (hash << 5) - hash + char; - // Convert to 32-bit integer + // 32-bit tam sayıya dönüştür hash &= hash; } } ``` -**Good:** +**İyi:** ```javascript function hashIt(data) { @@ -2254,19 +2064,19 @@ function hashIt(data) { const char = data.charCodeAt(i); hash = (hash << 5) - hash + char; - // Convert to 32-bit integer + // 32-bit tam sayıya dönüştür hash &= hash; } } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't leave commented out code in your codebase +### Kodlarınızı yorum olarak bırakmayın -Version control exists for a reason. Leave old code in your history. +Versiyon kontrol'un var olmasının bir sebebi var. Eski kodlarınızı tarihin tozlu sayfalarında bırakın. -**Bad:** +**Kötü:** ```javascript doStuff(); @@ -2275,20 +2085,19 @@ doStuff(); // doSoMuchStuff(); ``` -**Good:** +**İyi:** ```javascript doStuff(); ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Don't have journal comments +### Günlük yorumları yapmayın -Remember, use version control! There's no need for dead code, commented code, -and especially journal comments. Use `git log` to get history! +Hatırlayın, versiyon kontrol kullanın! Kullanılmayan koda, yoruma alınmış koda ve özellikle günlük kodlarına gerek yok. Geçmiş için `git log` kullanın. -**Bad:** +**Kötü:** ```javascript /** @@ -2302,7 +2111,7 @@ function combine(a, b) { } ``` -**Good:** +**İyi:** ```javascript function combine(a, b) { @@ -2310,14 +2119,13 @@ function combine(a, b) { } ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -### Avoid positional markers +### Yer belirleyicilerden kaçının -They usually just add noise. Let the functions and variable names along with the -proper indentation and formatting give the visual structure to your code. +Bunlar genellikle kirlilik yaratır. Fonksiyonların ve değişken adlarının yanı sıra uygun girinti ve biçimlendirme kodunuza görsel yapı kazandırsın. -**Bad:** +**Kötü:** ```javascript //////////////////////////////////////////////////////////////////////////////// @@ -2336,7 +2144,7 @@ const actions = function() { }; ``` -**Good:** +**İyi:** ```javascript $scope.model = { @@ -2349,11 +2157,11 @@ const actions = function() { }; ``` -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** -## Translation +## Çeviriler -This is also available in other languages: +Ayrıca diğer dillerde de: - ![am](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Armenia.png) **Armenian**: [hanumanum/clean-code-javascript/](https://github.com/hanumanum/clean-code-javascript) - ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) @@ -2377,4 +2185,4 @@ This is also available in other languages: - ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) -**[⬆ back to top](#table-of-contents)** +**[⬆ Başa dön](#İçindekiler)** From 46869c86bce8612bccf876b9b451f5e5890b8a4a Mon Sep 17 00:00:00 2001 From: Burak Sonmez Date: Sun, 29 Mar 2020 08:01:27 +0300 Subject: [PATCH 159/170] fix --- README.md | 902 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 547 insertions(+), 355 deletions(-) diff --git a/README.md b/README.md index d7747ff9..0bfe4c85 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,69 @@ # clean-code-javascript -## İçindekiler +## Table of Contents -1. [Giriş](#Giriş) -2. [Değişkenler](#Değişkenler) -3. [Fonksiyonlar](#Fonksiyonlar) -4. [Nesneler ve Veri Yapıları](#nesneler-ve-veri-yapıları) -5. [Sınıflar (Class)](#sınıflar) +1. [Introduction](#introduction) +2. [Variables](#variables) +3. [Functions](#functions) +4. [Objects and Data Structures](#objects-and-data-structures) +5. [Classes](#classes) 6. [SOLID](#solid) -7. [Testler](#testler) -8. [Tutarlılık](#Tutarlılık) -9. [Hata Yönetimi](#Hata-Yönetimi) -10. [Biçimlendirme](#Biçimlendirme) -11. [Yorumlar](#yorumlar) -12. [Çeviriler](#çeviriler) +7. [Testing](#testing) +8. [Concurrency](#concurrency) +9. [Error Handling](#error-handling) +10. [Formatting](#formatting) +11. [Comments](#comments) +12. [Translation](#translation) -## Giriş +## Introduction -![Kod okurken kaç defa bağıracağınızın bir sayısı olarak yazılım kalite tahmininin görüntüsü](https://www.osnews.com/images/comics/wtfm.jpg) +![Humorous image of software quality estimation as a count of how many expletives +you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg) -Robert C. Martin'nın kitabı olan ve yazılım mühendisliği ilkerini barındıran [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)'un JavaScript için uyarlanmış hali. Bu bir tarz rehberi değil. JavaScript'te [okunabilir, yeniden kullanılabilir ve yeniden düzenlenebilir](https://github.com/ryanmcdermott/3rs-of-software-architecture) yazılımlar üretmeye yönelik bir kılavuzdur. +Software engineering principles, from Robert C. Martin's book +[_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), +adapted for JavaScript. This is not a style guide. It's a guide to producing +[readable, reusable, and refactorable](https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript. -Buradaki her ilkeye kesinlikle uyulmak zorunda değildir ve daha azı evrensel olarak kabul edilecektir. Bunlar önerilerden başka bir şey değildir, fakat bunlar _Clean Code_ yazarları tarafından uzun yıllar süren tecrübeler ile kodlandı. +Not every principle herein has to be strictly followed, and even fewer will be +universally agreed upon. These are guidelines and nothing more, but they are +ones codified over many years of collective experience by the authors of +_Clean Code_. -Yazılım mühendisliği zanaatımız 50 yaşın biraz üzerinde ve hala çok şey öğreniyoruz. Yazılım mimarisi, mimarinin kendisi kadar eski olduğunda, belki de takip edilmesi daha zor kurallarımız olacak. Şimdi, bu önerilerin sizin ve ekibinizin ürettiği JavaScript kodunun kalitesini değerlendirmek için bir mihenk taşı işlevi görmesine izin verin. +Our craft of software engineering is just a bit over 50 years old, and we are +still learning a lot. When software architecture is as old as architecture +itself, maybe then we will have harder rules to follow. For now, let these +guidelines serve as a touchstone by which to assess the quality of the +JavaScript code that you and your team produce. -Bir şey daha: bunların bilinmesi sizi hemen daha iyi bir yazılım geliştiricisi yapmaz ve uzun yıllar onlarla çalışmak hata yapmayacağınız anlamına gelmez. +One more thing: knowing these won't immediately make you a better software +developer, and working with them for many years doesn't mean you won't make +mistakes. Every piece of code starts as a first draft, like wet clay getting +shaped into its final form. Finally, we chisel away the imperfections when +we review it with our peers. Don't beat yourself up for first drafts that need +improvement. Beat up the code instead! -Her kod parçası ilk taslak olarak başlar, örneğin ıslak kil son halini alır. Son olarak, akranlarımızla gözden geçirdiğimizde kusurları kesiyoruz. İyileştirilmesi gereken ilk taslaklar için kendinize yüklenmeyin. Onun yerine kodunuza yüklenin. +## **Variables** -## **Değişkenler** +### Use meaningful and pronounceable variable names -### Anlamlı ve belirgin değişken adları kullanın - -**Kötü:** +**Bad:** ```javascript const yyyymmdstr = moment().format("YYYY/MM/DD"); ``` -**İyi:** +**Good:** ```javascript const currentDate = moment().format("YYYY/MM/DD"); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Aynı değişken türü için aynı kelimeleri kullanın +### Use the same vocabulary for the same type of variable -**Kötü:** +**Bad:** ```javascript getUserInfo(); @@ -57,41 +71,45 @@ getClientData(); getCustomerRecord(); ``` -**İyi:** +**Good:** ```javascript getUser(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Aranabilir isimler kullanın +### Use searchable names -Yazacağımızdan daha fazla kod okuyacağız. Yazdığımız kodun okunabilir ve aranabilir olması önemlidir. Değişkenleri anlamlı ve anlaşılabilir _isimlendirmemekle_, okuyucuya zarar veriyoruz. -Adlarınızı aranabilir hale getirin. [buddy.js](https://github.com/danielstjules/buddy.js) ve -[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) gibi araçlar isimlendirilmemiş sabitlerinizi belirlemenize yardımcı olacaktır. +We will read more code than we will ever write. It's important that the code we +do write is readable and searchable. By _not_ naming variables that end up +being meaningful for understanding our program, we hurt our readers. +Make your names searchable. Tools like +[buddy.js](https://github.com/danielstjules/buddy.js) and +[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) +can help identify unnamed constants. -**Kötü:** +**Bad:** ```javascript -// 86400000 neydi ? +// What the heck is 86400000 for? setTimeout(blastOff, 86400000); ``` -**İyi:** +**Good:** ```javascript -// Bunları büyük harfle adlandırılmış sabitler olarak bildirin. +// Declare them as capitalized named constants. const MILLISECONDS_IN_A_DAY = 86_400_000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Açıklayıcı değişkenler kullan +### Use explanatory variables -**Kötü:** +**Bad:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -102,7 +120,7 @@ saveCityZipCode( ); ``` -**İyi:** +**Good:** ```javascript const address = "One Infinite Loop, Cupertino 95014"; @@ -111,13 +129,13 @@ const [_, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kafadan planmaktan kaçının +### Avoid Mental Mapping -Açık olmak, ima etmekten iyidir. +Explicit is better than implicit. -**Kötü:** +**Bad:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -127,12 +145,12 @@ locations.forEach(l => { // ... // ... // ... - // Bir dakika, 'l' neydi? + // Wait, what is `l` for again? dispatch(l); }); ``` -**İyi:** +**Good:** ```javascript const locations = ["Austin", "New York", "San Francisco"]; @@ -146,13 +164,14 @@ locations.forEach(location => { }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Gereksiz içerikler eklemeyin +### Don't add unneeded context -Sınıf / nesne adınız size bir şey söylüyorsa, bunu değişken adınızda tekrarlamayın. +If your class/object name tells you something, don't repeat that in your +variable name. -**Kötü:** +**Bad:** ```javascript const Car = { @@ -166,7 +185,7 @@ function paintCar(car) { } ``` -**İyi:** +**Good:** ```javascript const Car = { @@ -180,13 +199,16 @@ function paintCar(car) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kısa devre veya şartlar yerine önceden tanımlanmış argümanlar kullanın +### Use default arguments instead of short circuiting or conditionals -Önceden tanımlanan argümanlar genellikle kısa devrelere göre daha temizdir. Bunları kullanırsanız şunun farkında olun, fonksiyonunuz sadece `tanımsız` _(undefined)_ değerler için önceden tanımlanmış argümana kullanacaktır. `''`, `""`, `false`, `null`, `0`, ve `NaN` gibi "yanlış denilebilecek" değerler önceden tanımlanmış bir değerle değiştirilmez. +Default arguments are often cleaner than short circuiting. Be aware that if you +use them, your function will only provide default values for `undefined` +arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and +`NaN`, will not be replaced by a default value. -**Kötü:** +**Bad:** ```javascript function createMicrobrewery(name) { @@ -195,7 +217,7 @@ function createMicrobrewery(name) { } ``` -**İyi:** +**Good:** ```javascript function createMicrobrewery(name = "Hipster Brew Co.") { @@ -203,27 +225,41 @@ function createMicrobrewery(name = "Hipster Brew Co.") { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Fonksiyonlar** +## **Functions** -### Fonksiyonlar argümanları (Tercihen 2 veya daha az) +### Function arguments (2 or fewer ideally) -Fonksiyon parametrelerinin sayısını sınırlamak inanılmaz derecede önemlidir, çünkü fonksiyonunuzu test etmeyi kolaylaştırır. Üçten fazlasına sahip olmak, her bir argümanla tonlarca farklı vakayı test etmeniz gereken bir kombinasyonel patlamaya yol açar. +Limiting the amount of function parameters is incredibly important because it +makes testing your function easier. Having more than three leads to a +combinatorial explosion where you have to test tons of different cases with +each separate argument. -Bir veya iki argüman ideal durumdur ve mümkünse üç tanesinden kaçınılmalıdır. Bundan daha fazlası birleştirilmeldir. Genellikle, -ikiden fazla argüman sonra fonksiyonunuz çok işlem yapmaya çalışıyor. Olmadığı durumlarda, çoğu zaman daha üst düzey bir nesne argüman olarak yeterli olacaktır. +One or two arguments is the ideal case, and three should be avoided if possible. +Anything more than that should be consolidated. Usually, if you have +more than two arguments then your function is trying to do too much. In cases +where it's not, most of the time a higher-level object will suffice as an +argument. -JavaScript havada nesne yapmanıza olanak sağladığı için, bir çok sınıf yapısına gerek kalmadan, bir nesneyi birden fazla nesne kullanmadan kullanabilirsiniz. +Since JavaScript allows you to make objects on the fly, without a lot of class +boilerplate, you can use an object if you are finding yourself needing a +lot of arguments. -Fonksiyonun hangi özellikleri beklediğini netleştirmek için ES2015 / ES6 ayrıştırma sintaksını kullanabilirsiniz. Bunun birkaç avantajı vardır: +To make it obvious what properties the function expects, you can use the ES2015/ES6 +destructuring syntax. This has a few advantages: -1. Birisi fonksiyonun imzasına baktığında, hangi özelliklerin kullanıldığını hemen anlar.. -2. Adlandırılmış parametreleri simüle etmek için kullanılabilir. -3. Ayrıştırma işlemi ayrıca fonksiyona iletilen argüman nesnesinin belirtilen ilk değerlerini de klonlar. Bu, yan etkilerin önlenmesine yardımcı olabilir. Not: Argüman nesnelerinden ayrıştırılan nesneler ve diziler klonlanmaz! -4. Linters, sizi kullanılmayan değerler için uyarabilir bu da ayrıştırmadan imkansız olurdu. +1. When someone looks at the function signature, it's immediately clear what + properties are being used. +2. It can be used to simulate named parameters. +3. Destructuring also clones the specified primitive values of the argument + object passed into the function. This can help prevent side effects. Note: + objects and arrays that are destructured from the argument object are NOT + cloned. +4. Linters can warn you about unused properties, which would be impossible + without destructuring. -**Kötü:** +**Bad:** ```javascript function createMenu(title, body, buttonText, cancellable) { @@ -234,7 +270,7 @@ createMenu("Foo", "Bar", "Baz", true); ``` -**İyi:** +**Good:** ```javascript function createMenu({ title, body, buttonText, cancellable }) { @@ -249,13 +285,17 @@ createMenu({ }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyonlar bir şey yapmalı +### Functions should do one thing -Bu, yazılım mühendisliğinde açık ara en önemli kuraldır. Fonksiyonlar birden fazla şey yaptığında, birleştirmesi, test etmesi ve anlamdırılması daha zordur. Bir fonksiyonu yalnızca bir eyleme ayırabildiğinizde, kolayca yeniden düzenlenebilir ve kodunuz çok daha temiz okunur. Bu yazıdan başka bir şey almazsanız dahi sadece bununla birçok geliştiricinin önünde olacaksınız. +This is by far the most important rule in software engineering. When functions +do more than one thing, they are harder to compose, test, and reason about. +When you can isolate a function to just one action, it can be refactored +easily and your code will read much cleaner. If you take nothing else away from +this guide other than this, you'll be ahead of many developers. -**Kötü:** +**Bad:** ```javascript function emailClients(clients) { @@ -268,7 +308,7 @@ function emailClients(clients) { } ``` -**İyi:** +**Good:** ```javascript function emailActiveClients(clients) { @@ -281,11 +321,11 @@ function isActiveClient(client) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyon isimleri ne yaptıklarını anlatmalı +### Function names should say what they do -**Kötü:** +**Bad:** ```javascript function addToDate(date, month) { @@ -294,11 +334,11 @@ function addToDate(date, month) { const date = new Date(); -// Fonksiyonun adından ne eklendiğini söylemek zor +// It's hard to tell from the function name what is added addToDate(date, 1); ``` -**İyi:** +**Good:** ```javascript function addMonthToDate(month, date) { @@ -309,13 +349,15 @@ const date = new Date(); addMonthToDate(1, date); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyonlar soyutlaştırmadan sadece bir seviye uzak olmalıdır +### Functions should only be one level of abstraction -Birden fazla soyutlama seviyeniz olduğunda fonksiyonunuz genellikle çok fazla şey yapar. Fonksiyonların bölünmesi, yeniden kullanılabilirliği ve daha kolay test yapılmasını sağlayacak. +When you have more than one level of abstraction your function is usually +doing too much. Splitting up functions leads to reusability and easier +testing. -**Kötü:** +**Bad:** ```javascript function parseBetterJSAlternative(code) { @@ -342,7 +384,7 @@ function parseBetterJSAlternative(code) { } ``` -**İyi:** +**Good:** ```javascript function parseBetterJSAlternative(code) { @@ -379,19 +421,32 @@ function parse(tokens) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Tekrarlanan kodu sil +### Remove duplicate code -Tekrarlanan kodlardan kaçınmak için elinizden geleni yapın. Tekrarlanan kod kötü çünkü bazı mantığı değiştirmeniz gerekirse bir şeyi değiştirmek için birden fazla yer olduğu anlamına geliyor. +Do your absolute best to avoid duplicate code. Duplicate code is bad because it +means that there's more than one place to alter something if you need to change +some logic. -Bir restoran işlettiğinizi ve stoklarınızı takip ettiğinizi düşünün: tüm domates, soğan, sarımsak, baharat, vb. onlara. Yalnızca bir listeniz varsa, güncellenecek tek bir yer vardır! +Imagine if you run a restaurant and you keep track of your inventory: all your +tomatoes, onions, garlic, spices, etc. If you have multiple lists that +you keep this on, then all have to be updated when you serve a dish with +tomatoes in them. If you only have one list, there's only one place to update! -Çoğunlukla yinelenen kodunuz vardır, çünkü çoğu şeyi ortak paylaşsalar dahi çok küçük bir kaç farklılık vardır, ancak farklılıkları sizi aynı şeylerin çoğunu yapan iki veya daha fazla ayrı fonksiyona sahip olmaya zorlar. Tekrarlanan kodun kaldırılması, bu farklı şeyleri tek bir fonksiyon / modül / sınıfla işleyebilecek bir soyutlama oluşturmak anlamına gelir. +Oftentimes you have duplicate code because you have two or more slightly +different things, that share a lot in common, but their differences force you +to have two or more separate functions that do much of the same things. Removing +duplicate code means creating an abstraction that can handle this set of +different things with just one function/module/class. -Soyutlamayı doğru yapmak kritik öneme sahiptir, bu yüzden _Sınıflar_ bölümünde belirtilen SOLID ilkelerini izlemelisiniz. Kötü soyutlamalar yinelenen koddan daha kötü olabilir, bu yüzden dikkatli olun! Bunu söyledikten sonra, eğer iyi bir soyutlama yapabilirseniz, yapın! Kendinizi tekrarlamayın, aksi takdirde bir şeyi değiştirmek istediğinizde kendinizi birden fazla yeri güncellerken bulacaksınız. +Getting the abstraction right is critical, that's why you should follow the +SOLID principles laid out in the _Classes_ section. Bad abstractions can be +worse than duplicate code, so be careful! Having said this, if you can make +a good abstraction, do it! Don't repeat yourself, otherwise you'll find yourself +updating multiple places anytime you want to change one thing. -**Kötü:** +**Bad:** ```javascript function showDeveloperList(developers) { @@ -425,7 +480,7 @@ function showManagerList(managers) { } ``` -**İyi:** +**Good:** ```javascript function showEmployeeList(employees) { @@ -452,11 +507,11 @@ function showEmployeeList(employees) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Object.assign ile varsayılan nesneleri ayarlama +### Set default objects with Object.assign -**Kötü:** +**Bad:** ```javascript const menuConfig = { @@ -477,12 +532,12 @@ function createMenu(config) { createMenu(menuConfig); ``` -**İyi:** +**Good:** ```javascript const menuConfig = { title: "Order", - // Kullanıcı 'body' eklemedi + // User did not include 'body' key buttonText: "Send", cancellable: true }; @@ -498,20 +553,20 @@ function createMenu(config) { config ); - // config çıktısı şimdi : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} + // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksiyon parametrelerinde işaretleme kullanma +### Don't use flags as function parameters -İşaretlemeler fonksiyonunuzun birden fazla şey yaptığını gösterir. Fonkisyonlar sadece tek şey yapmalılar. Eğer aşağıdakine benzer değişiklere ve mantıksal operatorlere sahip fonksiyonunuz varsa fonksiyonlarınızı ayırın. +Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean. -**Kötü:** +**Bad:** ```javascript function createFile(name, temp) { @@ -523,7 +578,7 @@ function createFile(name, temp) { } ``` -**İyi:** +**Good:** ```javascript function createFile(name) { @@ -535,21 +590,30 @@ function createTempFile(name) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yan Etkilerden Kaçının (Bölüm 1) +### Avoid Side Effects (part 1) -Bir fonksiyon, bir değeri alıp başka bir değer veya değer döndürmekten başka bir şey yaparsa yan etki üretir. Bir yan etki, bir dosyaya yazma, bazı global değişkeni değiştirme veya yanlışlıkla tüm paranızı bir yabancıya gönderme olabilir. +A function produces a side effect if it does anything other than take a value in +and return another value or values. A side effect could be writing to a file, +modifying some global variable, or accidentally wiring all your money to a +stranger. -Şimdi, zaman zaman bir programda yan etkilere sahip olmanız gerekir. Önceki örnekte olduğu gibi, bir dosyaya yazmanız gerekebilir. Yapmak istediğiniz, bunu yapacağınız yeri merkezileştirmektir. Birden fazla yazma işlemi yapan fonksiyonlar veya sınıflar yapmayın. Bunu yapan bir servisiniz olsun. Bir ve sadece bir. +Now, you do need to have side effects in a program on occasion. Like the previous +example, you might need to write to a file. What you want to do is to +centralize where you are doing this. Don't have several functions and classes +that write to a particular file. Have one service that does it. One and only one. -Buradaki ana nokta, herhangi bir yapıya sahip olmayan nesneler arasında durumu(state) paylaşmak, herhangi bir şeyle yazılabilen değiştirilebilir(mutable) veri türlerini kullanmak ve yan etkilerinizin nerede ortaya çıktığını merkezileştirmemek gibi yaygın tuzaklardan kaçınmaktır. Bunu yapabilirseniz, diğer programcıların büyük çoğunluğundan daha mutlu olacaksınız. +The main point is to avoid common pitfalls like sharing state between objects +without any structure, using mutable data types that can be written to by anything, +and not centralizing where your side effects occur. If you can do this, you will +be happier than the vast majority of other programmers. -**Kötü:** +**Bad:** ```javascript -// Aşağıdaki fonksiyon Global değişkeni refere alıyor -// Bu adı kullanan başka bir fonksiyonumuz olsaydı, şimdi bir dizi olurdu ve onu bozacaktı. +// 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 name = "Ryan McDermott"; function splitIntoFirstAndLastName() { @@ -561,7 +625,7 @@ splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; ``` -**İyi:** +**Good:** ```javascript function splitIntoFirstAndLastName(name) { @@ -575,27 +639,44 @@ console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott']; ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yan Etkilerden Kaçının (Bölüm 2) +### Avoid Side Effects (part 2) -JavaScript'te, temel öğeler değerlerle aktarılır (passed by value) ve objeler/diziler referans ile aktarılır (passed by reference). +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: -Bu durumda, nesnelerde ve dizilerde fonksiyonunuz bir değişiklik yaparsa -örneğin, bir alışveriş sepeti dizisinde, satın almak için bir öğe ekleyerek, -bu `cart` dizisini kullanan diğer fonksiyonlar bu eklemeden etkilenecektir. -Bu harika olabilir, ancak kötü de olabilir. Kötü bir hayal edelim -durum: +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. -Kullanıcı, 'Satın Al' butonuna basar ve bu `satınal` fonksiyonu sunucuya bir istek atarak `sepet` dizisini sunucuya gönderir. Kötü bir ağ bağlantısı nedeniyle, `satınal` işlevi isteği yeniden denemeye devam etmelidir. Şimdi, bu arada kullanıcı yanlışlıkla ağ isteği başlamadan istemedikleri bir öğenin üzerine "Sepete Ekle" düğmesini tıklarsa ne olur? Bu olursa ve ağ isteği başlarsa, yanlışlıkla satın alınan öğeyi gönderir çünkü alışveriş sepeti dizisine, `ürünüSepeteEkle` işlevinin istenmeyen bir öğe ekleyerek değiştirdiği bir referansı vardır. `ürünüSepeteEkle` ın her zaman `sepeti` klonlaması, düzenlemesi ve klonu döndürmesi için harika bir çözüm olacaktır. Bu, alışveriş sepetinin referansını tutan başka hiçbir işlevin herhangi bir değişiklikten etkilenmemesini sağlar. +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. -Bu yaklaşıma değinecek iki uyarı: +Two caveats to mention to this approach: -1. Girilen nesnesini gerçekten değiştirmek istediğiniz durumlar olabilir, ancak bunu uyguladığınızda, bu vakaların oldukça nadir olduğunu göreceksiniz. Çoğu şeyin yan etkisi olmayacak şekilde yeniden düzenlenebilir! +1. 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! -2. Büyük nesneleri klonlamak performans açısından çok pahalı olabilir. Neyse ki, bu pratikte büyük bir sorun değildir, çünkü bu tür programlama yaklaşımlarını manuel yapmaktansa daha hızlı olmasını ve büyük nesneleri ve dizileri klonlanlarken daha az bellek harcayan [harika kütüphaneler](https://facebook.github.io/immutable-js/) vardır. +2. 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](https://facebook.github.io/immutable-js/) 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ü:** +**Bad:** ```javascript const addItemToCart = (cart, item) => { @@ -603,7 +684,7 @@ const addItemToCart = (cart, item) => { }; ``` -**İyi:** +**Good:** ```javascript const addItemToCart = (cart, item) => { @@ -611,17 +692,21 @@ const addItemToCart = (cart, item) => { }; ``` -**[⬆ Başa dön](#İçindekiler)** - -### Global fonksiyonlar yazma +**[⬆ back to top](#table-of-contents)** -Globalleri kirletmek JavaScript'te kötü bir uygulamadır, çünkü başka bir kütüphaneyle çakıştırabilirsiniz ve API'lerinizi kullananlar kişiler canlıya çıkana kadar bu aksi durumların farkında olmayabilir. +### Don't write to global functions -Bir örnek düşünelim: JavaScript'in yerel kütüphanesindeki dizi `diff` methodunu genişletmek ve iki dizinin farklılıklarını göstermek istediğinizi var sayalım. JavaScript'in yerel Array yöntemini iki dizi arasındaki farkı gösterebilecek bir "diff" yöntemine genişletmek mi istiyorsunuz? +Polluting globals is a bad practice in JavaScript because you could clash with another +library and the user of your API would be none-the-wiser until they get an +exception in production. Let's think about an example: what if you wanted to +extend JavaScript's native Array method to have a `diff` method that could +show the difference between two arrays? You could write your new function +to the `Array.prototype`, but it could clash with another library that tried +to do the same thing. What if that other library was just using `diff` to find +the difference between the first and last elements of an array? This is why it +would be much better to just use ES2015/ES6 classes and simply extend the `Array` global. -Yeni fonksiyonunuzu `Array.prototype`'e yazabilirsiniz, ancak aynı şeyi yapmaya çalışan başka bir kütüphane ile çakışabilir. Ya diğer kütüphane bir 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 `Array` globalini genişletmek çok daha iyi olurdu. - -**Kötü:** +**Bad:** ```javascript Array.prototype.diff = function diff(comparisonArray) { @@ -630,7 +715,7 @@ Array.prototype.diff = function diff(comparisonArray) { }; ``` -**İyi:** +**Good:** ```javascript class SuperArray extends Array { @@ -641,13 +726,15 @@ class SuperArray extends Array { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Zorunlu programlama yerine fonksiyonel programlamayı tercih edin +### Favor functional programming over imperative programming -JavaScript, Haskell'in olduğu gibi işlevsel bir dil değildir, ancak işlevsel bir tadı vardır. İşlevsel diller daha temiz ve test edilmesi daha kolay olabilir. Yapabildiğinizde bu tarz bir programlama yapın. +JavaScript isn't a functional language in the way that Haskell is, but it has +a functional flavor to it. Functional languages can be cleaner and easier to test. +Favor this style of programming when you can. -**Kötü:** +**Bad:** ```javascript const programmerOutput = [ @@ -676,7 +763,7 @@ for (let i = 0; i < programmerOutput.length; i++) { } ``` -**İyi:** +**Good:** ```javascript const programmerOutput = [ @@ -704,11 +791,11 @@ const totalOutput = programmerOutput.reduce( ); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Koşulları kapsamak +### Encapsulate conditionals -**Kötü:** +**Bad:** ```javascript if (fsm.state === "fetching" && isEmpty(listNode)) { @@ -716,7 +803,7 @@ if (fsm.state === "fetching" && isEmpty(listNode)) { } ``` -**İyi:** +**Good:** ```javascript function shouldShowSpinner(fsm, listNode) { @@ -728,11 +815,11 @@ if (shouldShowSpinner(fsmInstance, listNodeInstance)) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** ### Avoid negative conditionals -**Kötü:** +**Bad:** ```javascript function isDOMNodeNotPresent(node) { @@ -744,7 +831,7 @@ if (!isDOMNodeNotPresent(node)) { } ``` -**İyi:** +**Good:** ```javascript function isDOMNodePresent(node) { @@ -756,14 +843,20 @@ if (isDOMNodePresent(node)) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Koşullardan kaçının +### Avoid conditionals -Bu imkansız bir görev gibi görünüyor. Bunu ilk kez duyduktan sonra, çoğu kişi "`if` ifadesi olmadan nasıl bir şey yapmam gerekiyor?" Cevap şu ki -birçok durumda aynı görevi yerine getirmek için polimorfizm kullanabilirsiniz. İkinci soru genellikle, "peki bu harika ama neden bunu yapmak isteyeyim ki?" Cevap, daha önce öğrendiğimiz temiz bir kod kavramıydı: bir fonksiyon sadece bir şey yapmalı. `If` ifadeleri olan sınıflarınız ve fonksiyonlarınız olduğunda, kullanıcılara işlevinizin birden fazla şey yaptığını söylüyor. Unutmayın, sadece bir şey yapın. +This seems like an impossible task. Upon first hearing this, most people say, +"how am I supposed to do anything without an `if` statement?" The answer is that +you can use polymorphism to achieve the same task in many cases. The second +question is usually, "well that's great but why would I want to do that?" The +answer is a previous clean code concept we learned: a function should only do +one thing. When you have classes and functions that have `if` statements, you +are telling your user that your function does more than one thing. Remember, +just do one thing. -**Kötü:** +**Bad:** ```javascript class Airplane { @@ -781,7 +874,7 @@ class Airplane { } ``` -**İyi:** +**Good:** ```javascript class Airplane { @@ -810,13 +903,16 @@ class Cessna extends Airplane { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yazım denetiminden kaçının (Bölüm 1) +### Avoid type-checking (part 1) -JavaScript türsüzdür, yani işlevleriniz her türlü argümanı alabilir. Bazen bu özgürlük bizi oltaya getirebilir ve fonksiyonlarınızda tip kontrolü yapmak cazip hale gelir. Bunu yapmaktan kaçınmanın birçok yolu vardır. Dikkate alınması gereken ilk şey tutarlı API'lardır. +JavaScript is untyped, which means your functions can take any type of argument. +Sometimes you are bitten by this freedom and it becomes tempting to do +type-checking in your functions. There are many ways to avoid having to do this. +The first thing to consider is consistent APIs. -**Kötü:** +**Bad:** ```javascript function travelToTexas(vehicle) { @@ -828,7 +924,7 @@ function travelToTexas(vehicle) { } ``` -**İyi:** +**Good:** ```javascript function travelToTexas(vehicle) { @@ -836,14 +932,21 @@ function travelToTexas(vehicle) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yazım denetiminden kaçının (Bölüm 2) +### Avoid type-checking (part 2) -Dizeler ve tamsayılar gibi temel ilkel değerlerle çalışıyorsanız, -ve polimorfizm kullanamazsınız, ancak yine de yazım denetimi yapma gereğini hissediyorsanız, TypeScript kullanmayı düşünmelisiniz. Standart JavaScript sentaks üstünde statik yazım sağladığı için normal JavaScript'e mükemmel bir alternatiftir. Manuel olarak normal JavaScript'i kontrol etmeyle ilgili sorun, iyi yapmak o kadar fazla ayrıntılı gerektirir ki, elde ettiğiniz sahte "tip güvenliği" kaybettiğiniz okunabilirliği telafi etmez. JavaScript'inizi temiz tutun, iyi testler yazın ve iyi kod incelemelerine sahip olun. Aksi takdirde, hepsini TypeScript ile yapın (dediğim gibi harika bir alternatif!). +If you are working with basic primitive values like strings and integers, +and you can't use polymorphism but you still feel the need to type-check, +you should consider using TypeScript. It is an excellent alternative to normal +JavaScript, as it provides you with static typing on top of standard JavaScript +syntax. The problem with manually type-checking normal JavaScript is that +doing it well requires so much extra verbiage that the faux "type-safety" you get +doesn't make up for the lost readability. Keep your JavaScript clean, write +good tests, and have good code reviews. Otherwise, do all of that but with +TypeScript (which, like I said, is a great alternative!). -**Kötü:** +**Bad:** ```javascript function combine(val1, val2) { @@ -858,7 +961,7 @@ function combine(val1, val2) { } ``` -**İyi:** +**Good:** ```javascript function combine(val1, val2) { @@ -866,25 +969,27 @@ function combine(val1, val2) { } ``` -**[⬆ Başa dön](#İçindekiler)** - -### Aşırı optimizasyon yapma +**[⬆ back to top](#table-of-contents)** -Modern tarayıcılar çalışma zamanında çok sayıda optimizasyon yapar. Çoğu zaman, eğer optimize ediyorsanız, sadece zamanınızı boşa harcıyorsunuz demektir. +### Don't over-optimize -Optimizasyonun nerede olmadığını görmek için [iyi kaynaklar](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) vardır. Bunları optimize edene kadar işaretleyebilirsiniz. +Modern browsers do a lot of optimization under-the-hood at runtime. A lot of +times, if you are optimizing then you are just wasting your time. [There are good +resources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) +for seeing where optimization is lacking. Target those in the meantime, until +they are fixed if they can be. -**Kötü:** +**Bad:** ```javascript -// Eski tarayıcılarda, önbelleğe alınmamış "list.length" içeren her yineleme maliyetli olacaktır -// list.length yeniden hesaplanması nedeniyle. Modern tarayıcılarda bu optimize edilmiştir. +// On old browsers, each iteration with uncached `list.length` would be costly +// because of `list.length` recomputation. In modern browsers, this is optimized. for (let i = 0, len = list.length; i < len; i++) { // ... } ``` -**İyi:** +**Good:** ```javascript for (let i = 0; i < list.length; i++) { @@ -892,13 +997,15 @@ for (let i = 0; i < list.length; i++) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kullanılmayan kodları silin +### Remove dead code -Ölü kod, yinelenen kod kadar kötü. Kod tabanınızda tutmak için bir neden yoktur. Eğer çağrılmazsa, ondan kurtulun! Hala ihtiyacınız varsa sürüm geçmişinizde güvende olacaktır. +Dead code is just as bad as duplicate code. There's no reason to keep it in +your codebase. If it's not being called, get rid of it! It will still be safe +in your version history if you still need it. -**Kötü:** +**Bad:** ```javascript function oldRequestModule(url) { @@ -913,7 +1020,7 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**İyi:** +**Good:** ```javascript function newRequestModule(url) { @@ -924,21 +1031,25 @@ const req = newRequestModule; inventoryTracker("apples", req, "www.inventory-awesome.io"); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Nesneler ve Veri Yapıları** +## **Objects and Data Structures** -### Alıcıları ve ayarlayıcıları kullanma (Getters & Setters) +### Use getters and setters -Nesnelerdeki verilere erişmek için alıcıları ve ayarlayıcıları kullanmak, bir nesnedeki bir özelliği aramaktan daha iyi olabilir. "Neden?" sorabilirsiniz. Pekala, işte organize edilmeden nedenlerin bir listesi: +Using getters and setters to access data on objects could be better than simply +looking for a property on an object. "Why?" you might ask. Well, here's an +unorganized list of reasons why: -- Bir nesne özelliği almanın ötesinde daha fazlasını yapmak istediğinizde, kod tabanınızdaki her erişimciye bakmanız ve değiştirmeniz gerekmez. -- `set`yaparken doğrulama basitçe yapılabilir. -- Dahili gösterimi içine alır. -- Alma ve ayarlama sırasında kayıtlamayı(logging) ve hata yönetimi(error handling) eklemek kolaydır. -- Diyelimki sunucudan alıyorsunuz, nesnenizin özelliklerini tembel(lazy load) olarak yükleyebilirsiniz. +- When you want to do more beyond getting an object property, you don't have + to look up and change every accessor in your codebase. +- Makes adding validation simple when doing a `set`. +- Encapsulates the internal representation. +- Easy to add logging and error handling when getting and setting. +- You can lazy load your object's properties, let's say getting it from a + server. -**Kötü:** +**Bad:** ```javascript function makeBankAccount() { @@ -954,19 +1065,19 @@ const account = makeBankAccount(); account.balance = 100; ``` -**İyi:** +**Good:** ```javascript function makeBankAccount() { - // Bu fonksiyon özelinde (private) + // this one is private let balance = 0; - // "alıcı"yı, genel(public) objeyi döndürerek yap + // a "getter", made public via the returned object below function getBalance() { return balance; } - - // "ayarlayıcı"yı, genel(public) objeyi döndürerek yap + + // a "setter", made public via the returned object below function setBalance(amount) { // ... validate before updating the balance balance = amount; @@ -983,13 +1094,13 @@ const account = makeBankAccount(); account.setBalance(100); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Nesnelerin özel üyeleri olmasını sağlama +### Make objects have private members This can be accomplished through closures (for ES5 and below). -**Kötü:** +**Bad:** ```javascript const Employee = function(name) { @@ -1006,7 +1117,7 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined ``` -**İyi:** +**Good:** ```javascript function makeEmployee(name) { @@ -1023,15 +1134,18 @@ delete employee.name; console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Sınıflar** +## **Classes** -### ES5 düz fonksiyonlar yerine ES2015 / ES6 sınıflarını tercih et +### Prefer ES2015/ES6 classes over ES5 plain functions -Klasik ES5 sınıfları için okunabilir sınıf mirası, yapısı ve yöntem tanımları almak çok zordur. Miras almaya(inheritance) ihtiyacınız varsa (ve gerekmeyebileceğini unutmayın), ES2015 / ES6 sınıflarını tercih edin. Bununla birlikte, kendinizi daha büyük ve daha karmaşık nesnelere ihtiyaç duyana kadar küçük işlevlere tercih edin. +It's very difficult to get readable class inheritance, construction, and method +definitions for classical ES5 classes. If you need inheritance (and be aware +that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over +classes until you find yourself needing larger and more complex objects. -**Kötü:** +**Bad:** ```javascript const Animal = function(age) { @@ -1071,7 +1185,7 @@ Human.prototype.constructor = Human; Human.prototype.speak = function speak() {}; ``` -**İyi:** +**Good:** ```javascript class Animal { @@ -1107,13 +1221,17 @@ class Human extends Mammal { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yöntem zincirini kullan +### Use method chaining -Bu desen JavaScript'te çok kullanışlıdır ve jQuery ve Lodash gibi birçok kütüphanede görürsünüz. Kodunuzun etkileyici ve daha az detaylı olmasını sağlar. Bu nedenle, diyorum ki, yöntem zincirleme kullanın ve kodunuzun ne kadar temiz olacağını göreceksiniz. Sınıf fonksiyonlarınızda, her fonksiyonun sonunda `this`'i döndürmeniz yeterlidir ve daha fazla sınıf yöntemini buna zincirleyebilirsiniz. +This pattern is very useful in JavaScript and you see it in many libraries such +as jQuery and Lodash. It allows your code to be expressive, and less verbose. +For that reason, I say, use method chaining and take a look at how clean your code +will be. In your class functions, simply return `this` at the end of every function, +and you can chain further class methods onto it. -**Kötü:** +**Bad:** ```javascript class Car { @@ -1145,7 +1263,7 @@ car.setColor("pink"); car.save(); ``` -**İyi:** +**Good:** ```javascript class Car { @@ -1157,25 +1275,25 @@ class Car { setMake(make) { this.make = make; - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } setModel(model) { this.model = model; - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } setColor(color) { this.color = color; - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } save() { console.log(this.make, this.model, this.color); - // NOT: Zincirleme için `this` döndür + // NOTE: Returning this for chaining return this; } } @@ -1183,19 +1301,28 @@ class Car { const car = new Car("Ford", "F-150", "red").setColor("pink").save(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Miras yerine kompozisyon tercih et +### Prefer composition over inheritance -Dörtlü çete tarafından başlatılan ünlü [_Tasarım Desenleri_](https://en.wikipedia.org/wiki/Design_Patterns) gibi, siz de kompozisyonu, miras bırakmaya yerine göre tercih etmelisiniz. Miras kullanmak için birçok iyi neden ve kompozisyon kullanmak için birçok iyi neden vardır. Bu maksimum nokta için ana nokta, zihniniz içgüdüsel olarak miras kullanma için giderse, kompozisyon sorununuzu daha iyi modelleyip değiştiremeyeceğini düşünmeye çalışın. Bazı durumlarda olabilir. +As stated famously in [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four, +you should prefer composition over inheritance where you can. There are lots of +good reasons to use inheritance and lots of good reasons to use composition. +The main point for this maxim is that if your mind instinctively goes for +inheritance, try to think if composition could model your problem better. In some +cases it can. -O zaman "ne zaman miras kullanmalıyım?" diye merak ediyor olabilirsiniz. O eldeki probleminize bağlıdır, ancak bu mirasın kompozisyondan daha mantıklı olduğu iyi bir listedir: +You might be wondering then, "when should I use inheritance?" It +depends on your problem at hand, but this is a decent list of when inheritance +makes more sense than composition: -1. Mirasınız "has-a" değil, "is-a" ilişkisini temsil eder ilişki (İnsan-> Hayvan ve Kullanıcı-> KullanıcıAyrıntıları). -2. Temel sınıflardan kodu yeniden kullanabilirsiniz (İnsanlar tüm hayvanlar gibi hareket edebilir). -3. Temel sınıfı değiştirerek türetilmiş sınıflarda genel değişiklikler yapmak istiyorsunuz. (Hareket ettiklerinde tüm hayvanların kalori harcamalarını değiştirin). +1. Your inheritance represents an "is-a" relationship and not a "has-a" + relationship (Human->Animal vs. User->UserDetails). +2. You can reuse code from the base classes (Humans can move like all animals). +3. You want to make global changes to derived classes by changing a base class. + (Change the caloric expenditure of all animals when they move). -**Kötü:** +**Bad:** ```javascript class Employee { @@ -1207,7 +1334,7 @@ class Employee { // ... } -// Kötü çünkü Çalışanların(Employees) vergi bilgisi 'var'. ÇalışanVergiBilgisi(EmployeeTaxData) bir çeşit Çalışan(Employee) değil. +// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee class EmployeeTaxData extends Employee { constructor(ssn, salary) { super(); @@ -1219,7 +1346,7 @@ class EmployeeTaxData extends Employee { } ``` -**İyi:** +**Good:** ```javascript class EmployeeTaxData { @@ -1244,15 +1371,22 @@ class Employee { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** ## **SOLID** -### Tek Sorumluluk İlkesi (SRP) +### Single Responsibility Principle (SRP) -Temiz Kod'da belirtildiği gibi, "Bir sınıfın değişmesi için asla birden fazla sebep olmamalıdır". Bir sınıfı tıklım tıklım bir çok işlevsellikle doldurmak çekici gelebilir, tıpkı uçuşlarda yanına alabileceğiniz bir valiz gibi. Bununla ilgili sorun, sınıfınızın kavramsal olarak uyumlu olmayacağı ve değişmesi için birçok neden vereceği yönündedir. Bir sınıfı değiştirmek için ihtiyaç duyduğunuz sayıyı en aza indirmek önemlidir. Bir sınıfta çok fazla işlevsellik varsa 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. +As stated in Clean Code, "There should never be more than one reason for a class +to change". It's tempting to jam-pack a class with a lot of functionality, like +when you can only take one suitcase on your flight. The issue with this is +that your class won't be conceptually cohesive and it will give it many reasons +to change. Minimizing the amount of times you need to change a class is important. +It's important because if too much functionality is in one class and you modify +a piece of it, it can be difficult to understand how that will affect other +dependent modules in your codebase. -**Kötü:** +**Bad:** ```javascript class UserSettings { @@ -1272,7 +1406,7 @@ class UserSettings { } ``` -**İyi:** +**Good:** ```javascript class UserAuth { @@ -1299,13 +1433,16 @@ class UserSettings { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Açık / Kapalı Prensibi (OCP) +### Open/Closed Principle (OCP) -Bertrand Meyer tarafından belirtildiği gibi, "yazılım varlıkları (sınıflar, modüller, işlevler, vb.) Genişletme 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. +As stated by Bertrand Meyer, "software entities (classes, modules, functions, +etc.) should be open for extension, but closed for modification." What does that +mean though? This principle basically states that you should allow users to +add new functionalities without changing existing code. -**Kötü:** +**Bad:** ```javascript class AjaxAdapter extends Adapter { @@ -1349,7 +1486,7 @@ function makeHttpCall(url) { } ``` -**İyi:** +**Good:** ```javascript class AjaxAdapter extends Adapter { @@ -1387,15 +1524,24 @@ class HttpRequester { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Liskov’un Yerine Geçme Prensibi (LSP) +### Liskov Substitution Principle (LSP) -Bu çok basit bir kavram için korkutucu bir terimdir.Resmi olarak "S, T'nin bir alt tipiyse, o zaman T tipi nesnelerin yerine S tipi nesneler (yani, S tipi nesneler T programındaki nesnelerin yerine geçebilir), bu programın istenen özelliklerini değiştirmeden değiştirilebilir (doğruluk, yapılan görev vb.) " Bu daha da korkunç bir tanım. +This is a scary term for a very simple concept. It's formally defined as "If S +is a subtype of T, then objects of type T may be replaced with objects of type S +(i.e., objects of type S may substitute objects of type T) without altering any +of the desirable properties of that program (correctness, task performed, +etc.)." That's an even scarier definition. -Bunun için en iyi açıklama, bir üst 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 elde etmeden birbirinin yerine kullanılabilir.Bu hala kafa karıştırıcı olabilir, bu yüzden klasik Kare Dikdörtgen örneğine bakalım. Matematiksel olarak, bir kare bir dikdörtgendir, ancak miras yoluyla "is-a" ilişkisini kullanarak model verirseniz, hızlı bir şekilde sorun yaşarsınız. +The best explanation for this is if you have a parent class and a child class, +then the base class and child class can be used interchangeably without getting +incorrect results. This might still be confusing, so let's take a look at the +classic Square-Rectangle example. Mathematically, a square is a rectangle, but +if you model it using the "is-a" relationship via inheritance, you quickly +get into trouble. -**Kötü:** +**Bad:** ```javascript class Rectangle { @@ -1441,7 +1587,7 @@ function renderLargeRectangles(rectangles) { rectangles.forEach(rectangle => { rectangle.setWidth(4); rectangle.setHeight(5); - const area = rectangle.getArea(); // KÖTÜ: Kare için 25 değerini döndürür. 20 olmalı. + const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. rectangle.render(area); }); } @@ -1450,7 +1596,7 @@ const rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles(rectangles); ``` -**İyi:** +**Good:** ```javascript class Shape { @@ -1497,17 +1643,25 @@ const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Arayüzlerin Ayrımı Prensibi (ISP) +### Interface Segregation Principle (ISP) -JavaScript'in arayüzleri yoktur, bu nedenle bu ilke diğerleri kadar kesin olarak geçerli değildir. Bununla birlikte, JavaScript'in tür sistemi eksikliğinde bile önemli ve alâkalıdır. +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, "kullanıcılar kullanmadığı arabirimlere bağımlı olmaya zorlanmamalıdır." der. Arabirimler, `Duck Typing` yüzünden JavaScript'de üstü kapalı anlaşmalardır. +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. -Bu ilkeyi JavaScript'te gösteren iyi bir örnek, sınıflar büyük ayar nesneleri gerektirir. Kullanıcıların büyük miktarda seçenek ayarlamalarını istememek gerekli değildir, çünkü çoğu zaman tüm ayarlara ihtiyaç duymazlar. Bunları isteğe bağlı yapmak, bir "büyük arayüzü" olmasını önlemeye yardımcı olur. +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ü:** +**Bad:** ```javascript class DOMTraverser { @@ -1528,12 +1682,12 @@ class DOMTraverser { const $ = new DOMTraverser({ rootNode: document.getElementsByTagName("body"), - animationModule() {} // Coğunlukla, bunu canlandırmamız gerekmeyecek + animationModule() {} // Most of the time, we won't need to animate when traversing. // ... }); ``` -**İyi:** +**Good:** ```javascript class DOMTraverser { @@ -1567,24 +1721,32 @@ const $ = new DOMTraverser({ }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Bağlılığı Tersine Çevirme Prensibi (DIP) +### Dependency Inversion Principle (DIP) -Bu ilke iki temel şeyi ifade eder: +This principle states two essential things: -1. Yüksek seviyeli modüller, düşük seviyeli modüllere bağlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır. -2. Soyutlamalar detaylara bağlı olmamalıdır. Ayrıntılar soyutlamalara bağlı olmalıdır. +1. High-level modules should not depend on low-level modules. Both should + depend on abstractions. +2. Abstractions should not depend upon details. Details should depend on + abstractions. -İlk başta bunu anlamak zor olabilir, ancak AngularJS ile çalıştıysanız, bu prensibin Bağımlılık Enjeksiyonu (DI) şeklinde bir uygulamasını gördünüz. Aynı kavramlar olmasalar da, DIP yüksek seviyede modüllerin düşük seviyeli modüllerinin detaylarını bilmelerini ve ayarlamalarını sağlar. -Bunu DI ile başarabilir. Bunun büyük bir yararı, modüller arasındaki bağlantıyı azaltmasıdır. Eşleştirme(Coupling) çok kötü bir gelişme modelidir çünkü -kodunuzu yeniden düzenleme zorlaştırır. +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. -Daha önce belirtildiği gibi, JavaScript'in arayüzleri yoktur, bu nedenle soyutlamalar örtük sözleşmelere bağlıdır. -Yani, bir nesnenin / sınıfın başka bir nesneye / sınıfa maruz bıraktığı yöntemler ve özellikler. -Aşağıdaki örnekte, örtük sözleşme, bir `InventoryTracker` için herhangi bir Request modülünün `requestItems` yöntemine sahip olacağıdır. +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ü:** +**Bad:** ```javascript class InventoryRequester { @@ -1601,8 +1763,8 @@ class InventoryTracker { constructor(items) { this.items = items; - // KÖTÜ: Belirli bir istek uygulamasına bağımlılık yarattık. -    // requestItems sadece `request` 'e bağlımlı olmalıdır. + // 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(); } @@ -1617,7 +1779,7 @@ const inventoryTracker = new InventoryTracker(["apples", "bananas"]); inventoryTracker.requestItems(); ``` -**İyi:** +**Good:** ```javascript class InventoryTracker { @@ -1653,8 +1815,8 @@ class InventoryRequesterV2 { } } -// Bağımlılıklarımızı harici olarak yapılandırarak ve enjekte ederek -// istek modülümüzü WebSockets kullanan yeni ve havalı bir modülle kolayca değiştirebiliriz. +// 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() @@ -1662,19 +1824,28 @@ const inventoryTracker = new InventoryTracker( inventoryTracker.requestItems(); ``` -**[⬆ Başa dön](#İçindekiler)** - -## **Testler** +**[⬆ back to top](#table-of-contents)** -Test etmek canlıya çıkmaktan bile önemlidir. Eğer testiniz yoksa veya yetersiz bir miktardaysa, kodu her gönderdiğinizde hiçbir şeyin bozulmadığına emin olmazsınız. Neyin yeterli bir miktar oluşturduğuna karar vermek takımınıza bağlıdır, %100 kapsama sahip olmak (tüm ifadeler ve şubeler) size güven ve gönül rahatlığı sağlar. Bu, harika bir test framework'üne sahip olmanın yanı sıra, [iyi bir kapsama aracı](https://gotwarlost.github.io/istanbul/) kullanmanız gerektiği anlamına gelir +## **Testing** -Test yazmamanın mazereti yoktur. Çok sayıda [iyi JS test framework'ü](https://jstherightway.org/#testing-tools) vardır, bu yüzden ekibinize uyan birini bulun. +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](https://gotwarlost.github.io/istanbul/). -Ekibiniz için uygun olanı bulduğunuzda, kullanılan her yeni özellik / modül için daima testler yazmayı hedefleyin. Tercih ettiğiniz yöntem Test Odaklı Geliştirme (TDD) ise, bu harika, ancak asıl mesele, herhangi bir özelliği başlatmadan veya mevcut bir özelliği yeniden düzenlemeden önce kapsama hedeflerinize ulaştığınızdan emin olmaktır. +There's no excuse to not write tests. There are [plenty of good JS test frameworks](https://jstherightway.org/#testing-tools), 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. -### Her test için tek konsept +### Single concept per test -**Kötü:** +**Bad:** ```javascript import assert from "assert"; @@ -1698,7 +1869,7 @@ describe("MomentJS", () => { }); ``` -**İyi:** +**Good:** ```javascript import assert from "assert"; @@ -1724,15 +1895,16 @@ describe("MomentJS", () => { }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Tutarlılık** +## **Concurrency** -### Promises kullanın, callback değil +### Use Promises, not callbacks -'Callback'ler temiz değildir ve aşırı miktarda iç içe geçmeye neden olurlar. ES2015 / ES6 ile Promise'ler yerleşik bir global tiptir. Onları kullan! +Callbacks aren't clean, and they cause excessive amounts of nesting. With ES2015/ES6, +Promises are a built-in global type. Use them! -**Kötü:** +**Bad:** ```javascript import { get } from "request"; @@ -1756,7 +1928,7 @@ get( ); ``` -**İyi:** +**Good:** ```javascript import { get } from "request-promise"; @@ -1774,13 +1946,17 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Async/Await kullanmak Promis kullanmaktan bile daha temiz +### Async/Await are even cleaner than Promises -Promise'ler callback'lere göte çok daha temiz alternatiflerdir, ama ES2017/ES8 async ve await getirdi ve bu daha temiz bir çözüm sunuyor. Tek yapmanız gereken fonksiyonun başına `async` eklemek ve sonrasında mantıksal kullanımızı `then` zinciri kulanmadan yazabilirsiniz. Bugün ES2017 / ES8 özelliklerinden yararlanabiliyorsanız bunu kullanın! +Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await +which offer an even cleaner solution. All you need is a function that is prefixed +in an `async` keyword, and then you can write your logic imperatively without +a `then` chain of functions. Use this if you can take advantage of ES2017/ES8 features +today! -**Kötü:** +**Bad:** ```javascript import { get } from "request-promise"; @@ -1798,7 +1974,7 @@ get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") }); ``` -**İyi:** +**Good:** ```javascript import { get } from "request-promise"; @@ -1819,21 +1995,25 @@ async function getCleanCodeArticle() { getCleanCodeArticle() ``` -**[⬆ Başa dön](#İçindekiler)** - -## **Hata Yönetimi** - -Atılan hatalar iyi bir şey! Bunlar, programınızdaki bir şey yanlış gittiğinde çalışma zamanının başarıyla tanımlandığı anlamına gelir ve sizi geçerli yığında (stack) fonksiyonu çalıştırmayı, işlevi dururup size konsolda yığın izlemede haber vererek yapar. +**[⬆ back to top](#table-of-contents)** ---- HERE +## **Error Handling** -### Yakalanmış hataları görmemezlikten gelmeyin +Thrown errors are a good thing! They mean the runtime has successfully +identified when something in your program has gone wrong and it's letting +you know by stopping function execution on the current stack, killing the +process (in Node), and notifying you in the console with a stack trace. -Yakalanan bir hatayla hiçbir şey yapmamanız size söz konusu hatayı düzeltebilme veya tepki gösterme yeteneği vermez. Hatayı konsola (`console.log`) kaydetmek, konsola yazdırılan bir şey denizinde kaybolabileceği sıklıkta daha iyi değildir. +### Don't ignore caught errors -Herhangi bir kod parçasını `try/catch` içerisinde kullanıyorsanız, orada bir hata olabileceğini düşündüğünüz anlamına gelir ve bu nedenle gerçekleştiği zaman için bir planınız olması veya bir kod yolu oluşturmanız gerekir. +Doing nothing with a caught error doesn't give you the ability to ever fix +or react to said error. Logging the error to the console (`console.log`) +isn't much better as often times it can get lost in a sea of things printed +to the console. If you wrap any bit of code in a `try/catch` it means you +think an error may occur there and therefore you should have a plan, +or create a code path, for when it occurs. -**Kötü:** +**Bad:** ```javascript try { @@ -1843,27 +2023,28 @@ try { } ``` -**İyi:** +**Good:** ```javascript try { functionThatMightThrow(); } catch (error) { - // Bir secenek (console.log'dan daha dikkat çekici) + // One option (more noisy than console.log): console.error(error); - // Bir secenek daha + // Another option: notifyUserOfError(error); - // Bir secenek daha + // Another option: reportErrorToService(error); - // veya hepsini bir yapın + // OR do all three! } ``` -### Reddedilmiş promisleri görmemezlikten gelmeyin +### Don't ignore rejected promises -Aynı sebeplerden dolayı, `try/catch`'de oluşan hataları yok saymamalısınız +For the same reason you shouldn't ignore caught errors +from `try/catch`. -**Kötü:** +**Bad:** ```javascript getdata() @@ -1875,7 +2056,7 @@ getdata() }); ``` -**İyi:** +**Good:** ```javascript getdata() @@ -1883,29 +2064,36 @@ getdata() functionThatMightThrow(data); }) .catch(error => { - // Bir secenek (console.log'dan daha dikkat çekici) + // One option (more noisy than console.log): console.error(error); - // Bir secenek daha + // Another option: notifyUserOfError(error); - // Bir secenek daha + // Another option: reportErrorToService(error); - // veya hepsini bir yapın + // OR do all three! }); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Biçimlendirme** +## **Formatting** -Biçimlendirme özneldir. Buradaki birçok kural gibi, uygulamanız gereken zor ve hızlı bir kural yoktur. Ana nokta biçimlendirme üzerinde tartışma DEĞİLDİR. Bunu otomatikleştirmek için [tonlarca araç](https://standardjs.com/rules.html) vardır. Birini kullan! Mühendislerin biçimlendirme konusunda tartışmaları zaman ve para kaybıdır. +Formatting is subjective. Like many rules herein, there is no hard and fast +rule that you must follow. The main point is DO NOT ARGUE over formatting. +There are [tons of tools](https://standardjs.com/rules.html) to automate this. +Use one! It's a waste of time and money for engineers to argue over formatting. -Otomatik biçimlendirme (girintileme, sekmeler ve boşluklar, çift veya tek tırnak işaretleri vb.) Kapsamına girmeyen şeyler için bazı rehberlik için buraya bakın. +For things that don't fall under the purview of automatic formatting +(indentation, tabs vs. spaces, double vs. single quotes, etc.) look here +for some guidance. -### Tutarlı büyük harf kullanımı +### Use consistent capitalization -JavaScript türsüzdür, bu nedenle büyük / küçük harf kullanımı değişkenleriniz, işlevleriniz vb. Hakkında çok şey anlatır. Bu kurallar özneldir, böylece ekibiniz istediklerini seçebilir. Mesele şu ki, ne seçerseniz seçin, tutarlı olun. +JavaScript is untyped, so capitalization tells you a lot about your variables, +functions, etc. These rules are subjective, so your team can choose whatever +they want. The point is, no matter what you all choose, just be consistent. -**Kötü:** +**Bad:** ```javascript const DAYS_IN_WEEK = 7; @@ -1921,7 +2109,7 @@ class animal {} class Alpaca {} ``` -**İyi:** +**Good:** ```javascript const DAYS_IN_WEEK = 7; @@ -1937,13 +2125,15 @@ class Animal {} class Alpaca {} ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Fonksion çağıranları ve çağrılanları yakın olmalı +### Function callers and callees should be close -Eğer bir fonksiyon diğer fonksiyonu çağırıyorsa, dikey olarak bu fonksiyonları kaynak dosyasında yakın tutun. İdeal olan, fonksiyonu kullanan kullandığı fonksiyonun hemen üstünde olmasıdır. We tend to read code from top-to-bottom, like a newspaper.Bu nedenle, kodunuzu bu şekilde okuyun. +If a function calls another, keep those functions vertically close in the source +file. Ideally, keep the caller right above the callee. We tend to read code from +top-to-bottom, like a newspaper. Because of this, make your code read that way. -**Kötü:** +**Bad:** ```javascript class PerformanceReview { @@ -1983,7 +2173,7 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**İyi:** +**Good:** ```javascript class PerformanceReview { @@ -2023,37 +2213,37 @@ const review = new PerformanceReview(employee); review.perfReview(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## **Yorumlar** +## **Comments** -### Yalnızca iş mantığı karmaşıklığı olan şeyleri yorumlayın +### Only comment things that have business logic complexity. -Yorumlar aslında bir özür, şart değil. İyi kod _çoğunlukla_ kendini belgelemektedir. +Comments are an apology, not a requirement. Good code _mostly_ documents itself. -**Kötü:** +**Bad:** ```javascript function hashIt(data) { - // Karma + // The hash let hash = 0; - // String uzunluğu + // Length of string const length = data.length; - // Verilerdeki her karakteri gözden geçirin + // Loop through every character in data for (let i = 0; i < length; i++) { - // Karakter kodunu al + // Get character code. const char = data.charCodeAt(i); - // Karıştır + // Make the hash hash = (hash << 5) - hash + char; - // 32-bit tam sayıya dönüştür + // Convert to 32-bit integer hash &= hash; } } ``` -**İyi:** +**Good:** ```javascript function hashIt(data) { @@ -2064,19 +2254,19 @@ function hashIt(data) { const char = data.charCodeAt(i); hash = (hash << 5) - hash + char; - // 32-bit tam sayıya dönüştür + // Convert to 32-bit integer hash &= hash; } } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Kodlarınızı yorum olarak bırakmayın +### Don't leave commented out code in your codebase -Versiyon kontrol'un var olmasının bir sebebi var. Eski kodlarınızı tarihin tozlu sayfalarında bırakın. +Version control exists for a reason. Leave old code in your history. -**Kötü:** +**Bad:** ```javascript doStuff(); @@ -2085,19 +2275,20 @@ doStuff(); // doSoMuchStuff(); ``` -**İyi:** +**Good:** ```javascript doStuff(); ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Günlük yorumları yapmayın +### Don't have journal comments -Hatırlayın, versiyon kontrol kullanın! Kullanılmayan koda, yoruma alınmış koda ve özellikle günlük kodlarına gerek yok. Geçmiş için `git log` kullanın. +Remember, use version control! There's no need for dead code, commented code, +and especially journal comments. Use `git log` to get history! -**Kötü:** +**Bad:** ```javascript /** @@ -2111,7 +2302,7 @@ function combine(a, b) { } ``` -**İyi:** +**Good:** ```javascript function combine(a, b) { @@ -2119,13 +2310,14 @@ function combine(a, b) { } ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -### Yer belirleyicilerden kaçının +### Avoid positional markers -Bunlar genellikle kirlilik yaratır. Fonksiyonların ve değişken adlarının yanı sıra uygun girinti ve biçimlendirme kodunuza görsel yapı kazandırsın. +They usually just add noise. Let the functions and variable names along with the +proper indentation and formatting give the visual structure to your code. -**Kötü:** +**Bad:** ```javascript //////////////////////////////////////////////////////////////////////////////// @@ -2144,7 +2336,7 @@ const actions = function() { }; ``` -**İyi:** +**Good:** ```javascript $scope.model = { @@ -2157,11 +2349,11 @@ const actions = function() { }; ``` -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** -## Çeviriler +## Translation -Ayrıca diğer dillerde de: +This is also available in other languages: - ![am](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Armenia.png) **Armenian**: [hanumanum/clean-code-javascript/](https://github.com/hanumanum/clean-code-javascript) - ![bd](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bangladesh.png) **Bangla(বাংলা)**: [InsomniacSabbir/clean-code-javascript/](https://github.com/InsomniacSabbir/clean-code-javascript/) @@ -2182,7 +2374,7 @@ Ayrıca diğer dillerde de: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) -- ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript) +- ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript/tree/turkish-translation) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) -**[⬆ Başa dön](#İçindekiler)** +**[⬆ back to top](#table-of-contents)** From ec7baf25c13147d62d175d9518d24aa2fff56cc1 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 1 Apr 2020 18:47:12 +0300 Subject: [PATCH 160/170] Fix calling animationModule in ISP bad example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bfe4c85..61890f35 100644 --- a/README.md +++ b/README.md @@ -1672,7 +1672,7 @@ class DOMTraverser { setup() { this.rootNode = this.settings.rootNode; - this.animationModule.setup(); + this.settings.animationModule.setup(); } traverse() { From 93888080dd76f609ff36a233cc40d53593208805 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaychenko Date: Thu, 16 Apr 2020 16:57:44 +0300 Subject: [PATCH 161/170] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0bfe4c85..2bbc443e 100644 --- a/README.md +++ b/README.md @@ -2375,6 +2375,7 @@ This is also available in other languages: - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) - ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript/tree/turkish-translation) +- ![ua](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Ukraine.png) **Ukrainian**: [mindfr1k/clean-code-javascript-ua](https://github.com/mindfr1k/clean-code-javascript-ua) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) **[⬆ back to top](#table-of-contents)** From 365717f8bb54f0046e226606b0bbd3c9123e3594 Mon Sep 17 00:00:00 2001 From: Yaniv Chekroun Date: Sun, 6 Sep 2020 15:23:12 +0300 Subject: [PATCH 162/170] Add calculation for milliseconds in a day --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23ffbd33..fbe4de71 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ setTimeout(blastOff, 86400000); ```javascript // Declare them as capitalized named constants. -const MILLISECONDS_IN_A_DAY = 86_400_000; +const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000; //86400000; setTimeout(blastOff, MILLISECONDS_IN_A_DAY); ``` From 3b9504ef2ac585fadeec0523bb22ca39ca626e6b Mon Sep 17 00:00:00 2001 From: "J.D. Sandifer" Date: Sun, 4 Oct 2020 20:50:25 -0700 Subject: [PATCH 163/170] Replace pass-by-reference with mutability, clean up This edit only affects the Avoid Side Effects (part 2) section. It replaces the discussion of pass by reference with an explanation in terms of mutability. By avoiding the confusing terminology and possible links to the implementation details of different JS engines the explanation can be kept at an abstraction level that's great for beginners and experts alike. I also cleaned up some grammar, typos, etc. --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 23ffbd33..a67a044b 100644 --- a/README.md +++ b/README.md @@ -643,26 +643,29 @@ console.log(newName); // ['Ryan', 'McDermott']; ### Avoid Side Effects (part 2) -In JavaScript, primitives are passed by value and objects/arrays are passed by -reference. In the case of objects and arrays, if your function makes a change -in a shopping cart array, for example, by adding an item to purchase, -then any other function that uses that `cart` array will be affected by this -addition. That may be great, however it can be bad too. Let's imagine a bad -situation: +In JavaScript, some values are unchangeable (immutable) and some are changeable +(mutable). Objects and arrays are two kinds of mutable values so it's important +to handle them carefully when they're passed as parameters to a function. A +JavaScript function can change an object's properties or alter the contents of +an array which could easily cause bugs elsewhere. + +Suppose there's a function that accepts an array parameter representing a +shopping cart. If the function makes a change in that shopping cart array +- by adding an item to purchase, for example - then any other function that +uses that same `cart` array will be affected by this addition. That may be +great, however it could also be bad. 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" +request. Now, what if in the meantime the user accidentally clicks an "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. +will send the accidentally added item because the `cart` array was modified. -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. +A great solution would be for the `addItemToCart` function to always clone the +`cart`, edit it, and return the clone. This would ensure that functions that are still +using the old shopping cart wouldn't be affected by the changes. Two caveats to mention to this approach: From 30282195952b7eb01d2c8e0f022f83d60cde9284 Mon Sep 17 00:00:00 2001 From: "J.D. Sandifer" Date: Wed, 7 Oct 2020 20:15:53 -0700 Subject: [PATCH 164/170] Fix stray hyphen turning into a random bullet --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a67a044b..e5b63a81 100644 --- a/README.md +++ b/README.md @@ -650,8 +650,8 @@ JavaScript function can change an object's properties or alter the contents of an array which could easily cause bugs elsewhere. Suppose there's a function that accepts an array parameter representing a -shopping cart. If the function makes a change in that shopping cart array -- by adding an item to purchase, for example - then any other function that +shopping cart. If the function makes a change in that shopping cart array - +by adding an item to purchase, for example - then any other function that uses that same `cart` array will be affected by this addition. That may be great, however it could also be bad. Let's imagine a bad situation: From 029c4c2f8aef211caa99ab4cd39dcdbd496ba669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Doskovi=C4=87?= <40147841+doskovicmilos@users.noreply.github.com> Date: Sat, 10 Oct 2020 18:04:42 +0200 Subject: [PATCH 165/170] Add Serbian translation. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6205f631..ad21e9aa 100644 --- a/README.md +++ b/README.md @@ -2377,6 +2377,7 @@ This is also available in other languages: - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Spanish**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) +- ![rs](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Serbia.png) **Serbian**: [doskovicmilos/clean-code-javascript/](https://github.com/doskovicmilos/clean-code-javascript) - ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript/tree/turkish-translation) - ![ua](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Ukraine.png) **Ukrainian**: [mindfr1k/clean-code-javascript-ua](https://github.com/mindfr1k/clean-code-javascript-ua) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) From 753d6d8b33dece69e08b2ec0ea0807592881fbf2 Mon Sep 17 00:00:00 2001 From: James Tharpe Date: Mon, 29 Mar 2021 08:38:27 -0400 Subject: [PATCH 166/170] Make example more succinct "PER" has the same meaning as "IN_A" but is less verbose --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ad21e9aa..71896ed4 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,9 @@ setTimeout(blastOff, 86400000); ```javascript // Declare them as capitalized named constants. -const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000; //86400000; +const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000; -setTimeout(blastOff, MILLISECONDS_IN_A_DAY); +setTimeout(blastOff, MILLISECONDS_PER_DAY); ``` **[⬆ back to top](#table-of-contents)** From 3ff9eba6d460f31db8146762bade4fcc32626762 Mon Sep 17 00:00:00 2001 From: Ryan McDermott Date: Sun, 23 May 2021 11:52:44 -0700 Subject: [PATCH 167/170] Fix `paintCar` example to include `color` as param --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 71896ed4..631db6e8 100644 --- a/README.md +++ b/README.md @@ -180,8 +180,8 @@ const Car = { carColor: "Blue" }; -function paintCar(car) { - car.carColor = "Red"; +function paintCar(car, color) { + car.carColor = color; } ``` @@ -194,8 +194,8 @@ const Car = { color: "Blue" }; -function paintCar(car) { - car.color = "Red"; +function paintCar(car, color) { + car.color = color; } ``` From 384665f8da2de749ad96c694a9e18227cfd08b45 Mon Sep 17 00:00:00 2001 From: Hamed Abdollahi <42994875+hamettio@users.noreply.github.com> Date: Fri, 23 Jul 2021 19:04:42 +0200 Subject: [PATCH 168/170] Persian translation added (Fluent + RTL styled) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 631db6e8..52e2e786 100644 --- a/README.md +++ b/README.md @@ -2381,5 +2381,6 @@ This is also available in other languages: - ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [bsonmez/clean-code-javascript](https://github.com/bsonmez/clean-code-javascript/tree/turkish-translation) - ![ua](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Ukraine.png) **Ukrainian**: [mindfr1k/clean-code-javascript-ua](https://github.com/mindfr1k/clean-code-javascript-ua) - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) +- ![ir](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Iran.png) **Persian**: [hamettio/clean-code-javascript](https://github.com/hamettio/clean-code-javascript) **[⬆ back to top](#table-of-contents)** From 98f1d4a5a3b00c7b201400db6a443817d759859b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A8ne=20d=27Augier?= <101138826+eugene-augier@users.noreply.github.com> Date: Wed, 13 Apr 2022 14:08:10 +0200 Subject: [PATCH 169/170] French translation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 631db6e8..3c06041e 100644 --- a/README.md +++ b/README.md @@ -2365,7 +2365,7 @@ This is also available in other languages: - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Traditional Chinese**: [AllJointTW/clean-code-javascript](https://github.com/AllJointTW/clean-code-javascript) -- ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr) +- ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [eugene-augier/clean-code-javascript-fr](https://github.com/eugene-augier/clean-code-javascript-fr) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonesia**: [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) From 2cab77b8e58dce3f6c418595b7ef70adca1a4258 Mon Sep 17 00:00:00 2001 From: Agil Atakishiyev Date: Sun, 10 Jul 2022 21:05:45 +0400 Subject: [PATCH 170/170] fixed naming --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 631db6e8..e2880d99 100644 --- a/README.md +++ b/README.md @@ -201,9 +201,9 @@ function paintCar(car, color) { **[⬆ back to top](#table-of-contents)** -### Use default arguments instead of short circuiting or conditionals +### Use default parameters instead of short circuiting or conditionals -Default arguments are often cleaner than short circuiting. Be aware that if you +Default parameters are often cleaner than short circuiting. Be aware that if you use them, your function will only provide default values for `undefined` arguments. Other "falsy" values such as `''`, `""`, `false`, `null`, `0`, and `NaN`, will not be replaced by a default value.