├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ryan McDermott 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | > Ceci est une traduction française de ce lien: https://github.com/ryanmcdermott/clean-code-javascript 3 | 4 | ## Table Des Matières 5 | 6 | 1. [Introduction](#introduction) 7 | 2. [Variables](#variables) 8 | 3. [Fonctions](#fonctions) 9 | 4. [Objets Et Structures De Données](#objets-et-structures-de-données) 10 | 5. [Classes](#classes) 11 | 6. [SOLID](#solid) 12 | 7. [Testage](#testage) 13 | 8. [Simultanéité](#simultanéité) 14 | 9. [La Gestion Des Erreurs](#la-gestion-des-erreurs) 15 | 10. [Mise En Forme](#mise-en-forme) 16 | 11. [Commentaires](#commentaires) 17 | 12. [Traduction](#traduction) 18 | 19 | ## Introduction 20 | 21 | ![Humorous image of software quality estimation as a count of how many expletives 22 | you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg) 23 | 24 | Principes d'ingénierie logicielle, de l'ouvrage de Robert C. Martin 25 | [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), 26 | adapté pour JavaScript. Ce n'est pas un guide de style. C'est un guide pour la production de logiciels 27 | [lisibles, réutilisables et refactorables](https://github.com/ryanmcdermott/3rs-of-software-architecture) en JavaScript. 28 | 29 | Tous les principes énoncés ici ne doivent pas être strictement suivis, et encore moins seront universellement acceptés. Ce sont des lignes directrices et rien de plus, mais ce sont des lignes codifiées au fil de nombreuses années d'expérience collective par les auteurs de _Clean Code_. 30 | 31 | Notre métier de génie logiciel a un peu plus de 50 ans et nous apprenons encore beaucoup. Lorsque l'architecture logicielle est aussi ancienne que l'architecture elle-même, nous aurons peut-être des règles plus strictes à suivre. Pour l’instant, laissez ces instructions servir de point de repère pour évaluer la qualité du code JavaScript que vous et votre équipe produisez. 32 | 33 | Encore une chose: savoir que cela ne fera pas de vous un meilleur développeur de logiciels et travailler avec eux pendant de nombreuses années ne signifie pas que vous ne ferez pas d'erreurs. Chaque morceau de code commence par un premier brouillon, comme l'argile humide qui prend sa forme finale. Enfin, nous corrigeons les imperfections lorsque nous les examinons avec nos pairs. Ne vous battez pas pour les premières ébauches à améliorer. Battez le code à la place! 34 | 35 | ## **Variables** 36 | 37 | ### Utilisez des noms de variables significatifs et prononçables 38 | 39 | **Mal:** 40 | 41 | ```javascript 42 | const yyyymmdstr = moment().format("YYYY/MM/DD"); 43 | ``` 44 | 45 | **Bien:** 46 | 47 | ```javascript 48 | const dateActuelle = moment().format("YYYY/MM/DD"); 49 | ``` 50 | 51 | **[⬆ retour au sommet](#table-des-matières)** 52 | 53 | ### Utilisez le même vocabulaire pour le même type de variable 54 | 55 | **Mal:** 56 | 57 | ```javascript 58 | obtenirInformationDeUtilisateur(); 59 | obtenirDonéesDuClient(); 60 | obtenirFicheDuClient(); 61 | ``` 62 | 63 | **Bien:** 64 | 65 | ```javascript 66 | obtenirUtiilisateur(); 67 | ``` 68 | 69 | **[⬆ retour au sommet](#table-des-matières)** 70 | 71 | ### Utilisez des noms interrogeables 72 | 73 | Nous lirons plus de code que nous n'en écrirons jamais. Il est important que le code nous 74 | écrire est lisible et consultable. Par _pas_ nommer les variables qui finissent par 75 | avoir un sens pour comprendre notre programme, nous avons blessé nos lecteurs. 76 | Rendez vos noms consultables. Des outils comme 77 | [buddy.js](https://github.com/danielstjules/buddy.js) et 78 | [ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) 79 | peuvent aider à identifier des constantes non nommées. 80 | 81 | **Mal:** 82 | 83 | ```javascript 84 | // Qu'est-ce que c'est que 86400000? 85 | setTimeout(misÀFeu, 86400000); 86 | ``` 87 | 88 | **Bien:** 89 | 90 | ```javascript 91 | // Déclarez-les comme des constantes nommées capitalisées. 92 | const MILLISECONDES_PAR_JOUR = 86400000; 93 | 94 | setTimeout(misÀFeu, MILLISECONDES_PAR_JOUR); 95 | ``` 96 | 97 | **[⬆ retour au sommet](#table-des-matières)** 98 | 99 | ### Utiliser des variables explicatives 100 | 101 | **Mal:** 102 | 103 | ```javascript 104 | const adresse = "One Infinite Loop, Cupertino 95014"; 105 | const codePostalVilleRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; 106 | enregistrerCodePostalVille( 107 | adresse.match(codePostalVilleRegex)[1], 108 | adresse.match(codePostalVilleRegex)[2] 109 | ); 110 | ``` 111 | 112 | **Bien:** 113 | 114 | ```javascript 115 | const adresse = "One Infinite Loop, Cupertino 95014"; 116 | const codePostalVilleRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; 117 | const [, ville, codePostal] = adresse.match(codePostalVilleRegex) || []; 118 | enregistrerCodePostalVille(ville, codePostal); 119 | ``` 120 | 121 | **[⬆ retour au sommet](#table-des-matières)** 122 | 123 | ### Éviter la cartographie mentale 124 | 125 | Explicite est meilleur qu'implicite. 126 | 127 | **Mal:** 128 | 129 | ```javascript 130 | const endroits = ["Austin", "New York", "San Francisco"]; 131 | endroits.forEach(l => { 132 | faireDesTrucs(); 133 | faireAutreChoses(); 134 | // ... 135 | // ... 136 | // ... 137 | // Attendez, à quoi sert "l`"? 138 | dispatch(l); 139 | }); 140 | ``` 141 | 142 | **Bien:** 143 | 144 | ```javascript 145 | const endroits = ["Austin", "New York", "San Francisco"]; 146 | endroits.forEach(endroit => { 147 | faireDesTrucs(); 148 | faireAutreChoses(); 149 | // ... 150 | // ... 151 | // ... 152 | dispatch(endroit); 153 | }); 154 | ``` 155 | 156 | **[⬆ retour au sommet](#table-des-matières)** 157 | 158 | ### Ne pas ajouter de contexte inutile 159 | 160 | Si votre nom de classe / d’objet vous dit quelque chose, ne répétez pas cela dans votre 161 | Nom de variable. 162 | 163 | **Mal:** 164 | 165 | ```javascript 166 | const Voiture = { 167 | marqueDeVoiture: "Honda", 168 | modèleDeVoiture: "Accord", 169 | couleurDeVoiture: "Bleue" 170 | }; 171 | 172 | function peindreLaVoiture(voiture) { 173 | voiture.couleurDeVoiture = "Rouge"; 174 | } 175 | ``` 176 | 177 | **Bien:** 178 | 179 | ```javascript 180 | const Voiture = { 181 | marque: "Honda", 182 | modèle: "Accord", 183 | couleur: "Bleue" 184 | }; 185 | 186 | function peindreLaVoiture(voiture) { 187 | voiture.couleur = "Rouge"; 188 | } 189 | ``` 190 | 191 | **[⬆ retour au sommet](#table-des-matières)** 192 | 193 | ### Utiliser des arguments par défaut au lieu de court-circuiter ou conditionnels 194 | 195 | Les arguments par défaut sont souvent plus propres que les courts-circuits. Sachez que si vous 196 | utilisez-les, votre fonction ne fournira que les valeurs par défaut pour `undefined` 197 | arguments. Autres valeurs "fausses" telles que `''`, `" ",` `false`, `null`, `0` et 198 | `NaN`, ne sera pas remplacé par une valeur par défaut. 199 | 200 | **Mal:** 201 | 202 | ```javascript 203 | function créerUneMicroBrasserie(nom) { 204 | const nomDeBrasserie = nom || "Hipster Brew Co."; 205 | // ... 206 | } 207 | ``` 208 | 209 | **Good:** 210 | 211 | ```javascript 212 | function créerUneMicroBrasserie(nom = "Hipster Brew Co.") { 213 | // ... 214 | } 215 | ``` 216 | 217 | **[⬆ retour au sommet](#table-des-matières)** 218 | 219 | ## **Fonctions** 220 | 221 | ### Arguments de fonction (idéalement 2 ou moins) 222 | 223 | Limiter le nombre de paramètres de fonction est extrêmement important car 224 | facilite le test de votre fonction. Avoir plus de trois prospects à un 225 | explosion combinatoire où vous devez tester des tonnes de cas différents avec 226 | chaque argument séparé. 227 | 228 | Un ou deux arguments est le cas idéal, et trois devraient être évités si possible. 229 | Quelque chose de plus que cela devrait être consolidé. Habituellement, si vous avez 230 | plus de deux arguments, votre fonction essaie de faire trop. Dans les cas 231 | où il n'est pas, la plupart du temps un objet de niveau supérieur suffira en tant que 232 | argument. 233 | 234 | Puisque JavaScript vous permet de créer des objets à la volée, sans trop de classe 235 | passe-partout, vous pouvez utiliser un objet si vous vous trouvez nécessitant un 236 | beaucoup d'arguments. 237 | 238 | Pour rendre évidentes les propriétés attendues par la fonction, vous pouvez utiliser le logiciel ES2015 / ES6. 239 | syntaxe de déstructuration. Cela présente quelques avantages: 240 | 241 | 1. Quand quelqu'un regarde la signature de la fonction, on voit immédiatement ce qui 242 |      les propriétés sont utilisées. 243 | 2. La destruction clone également les valeurs primitives spécifiées de l'argument 244 |      objet passé dans la fonction. Cela peut aider à prévenir les effets secondaires. Remarque: 245 |      les objets et les tableaux qui sont déstructurés à partir de l'objet argument ne sont PAS 246 |      cloné. 247 | 3. Les linters peuvent vous avertir des propriétés inutilisées, ce qui serait impossible 248 |      sans déstructuration. 249 | 250 | **Mal:** 251 | 252 | ```javascript 253 | function créerUnMenu(titre, corps, boutonTexte, annulable) { 254 | // ... 255 | } 256 | ``` 257 | 258 | **Bien:** 259 | 260 | ```javascript 261 | function créerUnMenu({ titre, corps, boutonTexte, annulable }) { 262 | // ... 263 | } 264 | 265 | créerUnMenu({ 266 | titre: "Foo", 267 | corps: "Bar", 268 | boutonTexte: "Baz", 269 | annulable: true 270 | }); 271 | ``` 272 | 273 | **[⬆ retour au sommet](#table-des-matières)** 274 | 275 | ### Les fonctions doivent faire une chose 276 | 277 | C’est de loin la règle la plus importante en génie logiciel. Quand fonctionne 278 | faire plus d’une chose, il est plus difficile de composer, de tester et de raisonner. 279 | Lorsque vous pouvez isoler une fonction à une seule action, elles peuvent être refactorisées. 280 | facilement et votre code lira beaucoup plus propre. Si vous ne prenez rien d'autre de 281 | ce guide autre que celui-ci, vous serez en avance sur de nombreux développeurs. 282 | 283 | **Mal:** 284 | 285 | ```javascript 286 | function emailClients(clients) { 287 | clients.forEach(client => { 288 | const enregistrementDuClient = database.lookup(client); 289 | if (enregistrementDuClient.estActif()) { 290 | email(client); 291 | } 292 | }); 293 | } 294 | ``` 295 | 296 | **Good:** 297 | 298 | ```javascript 299 | function emailClientsActifs(clients) { 300 | clients.filter(estClientActif).forEach(email); 301 | } 302 | 303 | function estClientActif(client) { 304 | const enregistrementDuClient = database.lookup(client); 305 | return enregistrementDuClient.estActif(); 306 | } 307 | ``` 308 | 309 | **[⬆ retour au sommet](#table-des-matières)** 310 | 311 | ### Les noms de fonction doivent dire ce qu'ils font 312 | 313 | **Mal:** 314 | 315 | ```javascript 316 | function ajouterÀLaDate(date, mois) { 317 | // ... 318 | } 319 | 320 | const date = new Date(); 321 | 322 | // Il est difficile de dire à partir du nom de la fonction ce qui est ajouté 323 | ajouterÀLaDate(date, 1); 324 | ``` 325 | 326 | **Bien:** 327 | 328 | ```javascript 329 | function ajouterMoisÀDate(mois, date) { 330 | // ... 331 | } 332 | 333 | const date = new Date(); 334 | ajouterMoisÀDate(1, date); 335 | ``` 336 | 337 | **[⬆ retour au sommet](#table-des-matières)** 338 | 339 | ### Les fonctions ne devraient être qu'un seul niveau d'abstraction 340 | 341 | Lorsque vous avez plus d'un niveau d'abstraction, votre fonction est généralement 342 | faire trop. La scission des fonctions conduit à la réutilisation et à la facilité 343 | essai. 344 | 345 | **Mal:** 346 | 347 | ```javascript 348 | function mieuxAnalyserAlternatifJS(code) { 349 | const REGEXES = [ 350 | // ... 351 | ]; 352 | 353 | const déclarations = code.split(" "); 354 | const jetons = []; 355 | REGEXES.forEach(REGEX => { 356 | déclarations.forEach(déclaration => { 357 | // ... 358 | }); 359 | }); 360 | 361 | const ast = []; 362 | jetons.forEach(jeton => { 363 | // lex... 364 | }); 365 | 366 | ast.forEach(node => { 367 | // parse... 368 | }); 369 | } 370 | ``` 371 | 372 | **Bien:** 373 | 374 | ```javascript 375 | function mieuxAnalyserAlternatifJS(code) { 376 | const jetons = tokenize(code); 377 | const arbreDeSyntaxe = analyser(jetons); 378 | arbreDeSyntaxe.forEach(node => { 379 | // parse... 380 | }); 381 | } 382 | 383 | function tokenize(code) { 384 | const REGEXES = [ 385 | // ... 386 | ]; 387 | 388 | const déclarations = code.split(" "); 389 | const jetons = []; 390 | REGEXES.forEach(REGEX => { 391 | déclarations.forEach(déclaration => { 392 | jetons.push(/* ... */); 393 | }); 394 | }); 395 | 396 | return jetons; 397 | } 398 | 399 | function analyser(jetons) { 400 | const arbreDeSyntaxe = []; 401 | jetons.forEach(jeton => { 402 | arbreDeSyntaxe.push(/* ... */); 403 | }); 404 | 405 | return arbreDeSyntaxe; 406 | } 407 | ``` 408 | 409 | **[⬆ retour au sommet](#table-des-matières)** 410 | 411 | ### Supprimez le code en double 412 | 413 | Faites de votre mieux pour éviter le code en double. Dupliquer le code est mauvais parce que 414 | signifie qu'il y a plus d'un endroit pour changer quelque chose si vous avez besoin de changer 415 | un peu de logique. 416 | 417 | Imaginez que vous dirigiez un restaurant et que vous gardiez une trace de votre inventaire: tous vos 418 | tomates, oignons, ail, épices, etc. Si vous avez plusieurs listes qui 419 | vous gardez ce sur, alors tous doivent être mis à jour lorsque vous servez un plat avec 420 | tomates en eux. Si vous n'avez qu'une seule liste, il n'y a qu'un seul endroit pour mettre à jour! 421 | 422 | Vous avez souvent un code en double parce que vous en avez deux ou plus légèrement 423 | choses différentes, qui partagent beaucoup en commun, mais leurs différences vous forcent 424 | d'avoir deux ou plusieurs fonctions distinctes qui font beaucoup de choses identiques. Enlever 425 | dupliquer le code signifie créer une abstraction capable de gérer cet ensemble de 426 | choses différentes avec une seule fonction / module / classe. 427 | 428 | Il est essentiel de bien comprendre l’abstraction, c’est pourquoi vous devriez suivre 429 | Principes SOLID énoncés dans la section _Classes_. Les mauvaises abstractions peuvent être 430 | pire que le code en double, alors soyez prudent! Cela dit, si vous pouvez faire 431 | une bonne abstraction, faites-le! Ne te répète pas, sinon tu te retrouveras 432 | mettre à jour plusieurs endroits chaque fois que vous voulez changer une chose. 433 | 434 | **Mal:** 435 | 436 | ```javascript 437 | function afficherLaListeDesDéveloppeurs(développeurs) { 438 | développeurs.forEach(développeur => { 439 | const salairePrévu = développeur.calculerSalairePrévu(); 440 | const expérience = développeur.obtenirExpérience(); 441 | const lienGithub = développeur.obtenirLienGithub(); 442 | const données = { 443 | salairePrévu, 444 | expérience, 445 | lienGithub 446 | }; 447 | 448 | rendre(données); 449 | }); 450 | } 451 | 452 | function afficherLaListeDesGestionnaires(gestionnaires) { 453 | gestionnaires.forEach(gestionnaire => { 454 | const salairePrévu = gestionnaire.calculerSalairePrévu(); 455 | const expérience = gestionnaire.obtenirExpérience(); 456 | const portfolio = gestionnaire.obtenireProjetsMBA(); 457 | const données = { 458 | salairePrévu, 459 | expérience, 460 | portfolio 461 | }; 462 | 463 | rendre(données); 464 | }); 465 | } 466 | ``` 467 | 468 | **Bien:** 469 | 470 | ```javascript 471 | function afficherLaListeDesEmployés(employés) { 472 | employés.forEach(employé => { 473 | const salairePrévu = employés.calculerSalairePrévu(); 474 | const expérience = employés.obtenirExpérience(); 475 | 476 | const données = { 477 | salairePrévu, 478 | expérience 479 | }; 480 | 481 | switch (employés.type) { 482 | case "gestionnaire": 483 | data.portfolio = employés.obtenireProjetsMBA(); 484 | break; 485 | case "développeur": 486 | data.lienGithub = employés.obtenirLienGithub(); 487 | break; 488 | } 489 | 490 | rendre(données); 491 | }); 492 | } 493 | ``` 494 | 495 | **[⬆ retour au sommet](#table-des-matières)** 496 | 497 | ### Définir des objets par défaut avec Object.assign 498 | 499 | **Mal:** 500 | 501 | ```javascript 502 | const configMenu = { 503 | titre: null, 504 | corps: "Bar", 505 | boutonTexte: null, 506 | annulable: true 507 | }; 508 | 509 | function créerUnMenu(config) { 510 | config.titre = config.titre || "Foo"; 511 | config.corps = config.corps || "Bar"; 512 | config.boutonTexte = config.boutonTexte || "Baz"; 513 | config.annulable = config.annulable !== undefined ? config.annulable : true; 514 | } 515 | 516 | créerUnMenu(configMenu); 517 | ``` 518 | 519 | **Good:** 520 | 521 | ```javascript 522 | const configMenu = { 523 | titre: "Commande", 524 | // L'utilisateur n'a pas inclus la clé 'corps' 525 | boutonTexte: "Envoyer", 526 | annulable: true 527 | }; 528 | 529 | function créerUnMenu(config) { 530 | config = Object.assign( 531 | { 532 | titre: "Foo", 533 | corps: "Bar", 534 | boutonTexte: "Baz", 535 | annulable: true 536 | }, 537 | config 538 | ); 539 | 540 | // config est maintenant égal à: {titre: "Commande", corps: "Bar", boutonTexte - "Envoyer", annulable: true} 541 | // ... 542 | } 543 | 544 | créerUnMenu(configMenu); 545 | ``` 546 | 547 | **[⬆ retour au sommet](#table-des-matières)** 548 | 549 | ### Ne pas utiliser les drapeaux comme paramètres de fonction 550 | 551 | Les drapeaux indiquent à l'utilisateur que cette fonction fait plus d'une chose. Les fonctions devraient faire une chose. Répartissez vos fonctions si elles suivent différents chemins de code basés sur un booléen. 552 | 553 | **Mal:** 554 | 555 | ```javascript 556 | function créerUnFichier(nom, temp) { 557 | if (temp) { 558 | fs.create(`./temp/${nom}`); 559 | } else { 560 | fs.create(nom); 561 | } 562 | } 563 | ``` 564 | 565 | **Bien:** 566 | 567 | ```javascript 568 | function créerUnFichier(nom) { 569 | fs.create(nom); 570 | } 571 | 572 | function créerUnFichierTemp(nom) { 573 | créerUnFichier(`./temp/${nom}`); 574 | } 575 | ``` 576 | 577 | **[⬆ retour au sommet](#table-des-matières)** 578 | 579 | ### Éviter les effets secondaires (partie 1) 580 | 581 | Une fonction produit un effet secondaire si elle fait autre chose que prendre une valeur dans 582 | et renvoyer une ou plusieurs valeurs. Un effet secondaire pourrait être l'écriture dans un fichier, 583 | modifier une variable globale ou transférer accidentellement tout votre argent à un 584 | étranger. 585 | 586 | Maintenant, vous devez avoir des effets secondaires dans un programme à l'occasion. Comme le précédent 587 | Par exemple, vous devrez peut-être écrire dans un fichier. Ce que tu veux faire c'est 588 | centraliser où vous faites cela. Ne pas avoir plusieurs fonctions et classes 589 | qui écrivent dans un fichier particulier. Avoir un service qui le fait. Seul et l'unique. 590 | 591 | Le principal est d'éviter les pièges courants tels que le partage d'état entre objets 592 | sans aucune structure, en utilisant des types de données mutables qui peuvent être écrits par n'importe quoi, 593 | et ne pas centraliser où vos effets secondaires se produisent. Si vous pouvez faire cela, vous voudrez 594 | être plus heureux que la grande majorité des autres programmeurs. 595 | 596 | **Mal:** 597 | 598 | ```javascript 599 | // Variable globale référencée par la fonction suivante. 600 | // Si nous avions une autre fonction qui utilisait ce nom, maintenant ce serait un tableau et cela pourrait le casser. 601 | let nom = "Gavish Barosee"; 602 | 603 | function diviséEnPrénomEtNom() { 604 | nom = nom.split(" "); 605 | } 606 | 607 | diviséEnPrénomEtNom(); 608 | 609 | console.log(nom); // ['Gavish', 'Barosee']; 610 | ``` 611 | 612 | **Bien:** 613 | 614 | ```javascript 615 | function diviséEnPrénomEtNom(nom) { 616 | return nom.split(" "); 617 | } 618 | 619 | const nom = "Gavish Barosee"; 620 | const nouveauNom = splitIntoFirstAndLastName(nom); 621 | 622 | console.log(nom); // 'Gavish Barosee'; 623 | console.log(nouveauNom); // ['Gavish', 'Barosee']; 624 | ``` 625 | 626 | **[⬆ retour au sommet](#table-des-matières)** 627 | 628 | ### Éviter les effets secondaires (partie 2) 629 | 630 | En JavaScript, les primitives sont passées par valeur et les objets / tableaux par 631 | référence. Dans le cas d'objets et de tableaux, si votre fonction effectue un changement 632 | dans un tableau de panier, par exemple, en ajoutant un article à acheter, 633 | alors toute autre fonction utilisant ce tableau `cart` sera affectée par cette 634 | une addition. Cela peut être formidable, mais cela peut aussi être mauvais. Imaginons un mauvais 635 | situation: 636 | 637 | L'utilisateur clique sur le bouton "Acheter", qui appelle une fonction "achat" qui 638 | génère une requête réseau et envoie le tableau `cart` au serveur. Parce que 639 | d’une mauvaise connexion réseau, la fonction `purchase` doit continuer à réessayer la 640 | demande. Maintenant, qu'en est-il si, dans l'intervalle, l'utilisateur clique accidentellement sur "Ajouter au panier" 641 | bouton sur un élément dont ils ne veulent pas avant le début de la demande réseau? 642 | Si cela se produit et que la demande de réseau commence, alors cette fonction d'achat 643 | enverra l'élément ajouté accidentellement car il a une référence à un achat 644 | cart array que la fonction `addItemToCart` a modifiée en ajoutant un élément indésirable 645 | article. 646 | 647 | Une bonne solution serait pour le `addItemToCart` de toujours cloner le`cart`, 648 | éditez-le et retournez le clone. Cela garantit qu'aucune autre fonction ne soit 649 | conserver une référence du panier d'achat sera affecté par tout changement. 650 | 651 | Deux mises en garde à mentionner à cette approche: 652 | 653 | 1. Il peut arriver que vous souhaitiez réellement modifier l’objet d’entrée, 654 |      mais lorsque vous adoptez cette pratique de programmation, vous constaterez que ces cas 655 |      sont assez rares. La plupart des choses peuvent être refactorisées sans effets secondaires! 656 | 657 | 2. Le clonage de gros objets peut être très coûteux en termes de performances. Heureusement, 658 |      ce n'est pas un gros problème dans la pratique car il y a 659 | [grandes libraries](https://facebook.github.io/immutable-js/) qui permettent 660 |      ce type d’approche de programmation doit être rapide et ne nécessite pas autant de mémoire que 661 |      ce serait à vous de cloner manuellement des objets et des tableaux. 662 | 663 | **Mal:** 664 | 665 | ```javascript 666 | const AjouterUnArticleAuPanier = (panier, article) => { 667 | cart.push({ article, date: Date.now() }); 668 | }; 669 | ``` 670 | 671 | **Bien:** 672 | 673 | ```javascript 674 | const AjouterUnArticleAuPanier = (panier, article) => { 675 | return [...panier, { article, date: Date.now() }]; 676 | }; 677 | ``` 678 | 679 | **[⬆ retour au sommet](#table-des-matières)** 680 | 681 | ### Ne pas écrire dans les fonctions globales 682 | 683 | Les globaux polluants sont une mauvaise pratique en JavaScript car vous pourriez vous heurter à un autre 684 | bibliothèque et l'utilisateur de votre API serait absolument inutile jusqu'à ce qu'ils obtiennent un 685 | exception en production. Pensons à un exemple: et si vous vouliez 686 | étendre la méthode Array native de JavaScript pour avoir une méthode `diff` qui pourrait 687 | montrer la différence entre deux tableaux? Vous pouvez écrire votre nouvelle fonction 688 | `Array.prototype`, mais il pourrait entrer en conflit avec une autre bibliothèque qui a essayé 689 | faire la même chose. Et si cette autre bibliothèque utilisait simplement `diff` pour trouver 690 | la différence entre le premier et le dernier élément d'un tableau? C'est pourquoi ça 691 | Il serait bien mieux d’utiliser simplement les classes ES2015 / ES6 et d’étendre simplement le `Array` global. 692 | 693 | **Mal:** 694 | 695 | ```javascript 696 | Array.prototype.diff = function diff(tableauDeComparaison) { 697 | const hash = new Set(tableauDeComparaison); 698 | return this.filter(elem => !hash.has(elem)); 699 | }; 700 | ``` 701 | 702 | **Bien:** 703 | 704 | ```javascript 705 | class SuperArray extends Array { 706 | diff(tableauDeComparaison) { 707 | const hash = new Set(tableauDeComparaison); 708 | return this.filter(elem => !hash.has(elem)); 709 | } 710 | } 711 | ``` 712 | 713 | **[⬆ retour au sommet](#table-des-matières)** 714 | 715 | ### Privilégier la programmation fonctionnelle à la programmation impérative 716 | 717 | JavaScript n'est pas un langage fonctionnel comme Haskell, mais il a 718 | une saveur fonctionnelle à elle. Les langages fonctionnels peuvent être plus propres et plus faciles à tester. 719 | Privilégiez ce style de programmation quand vous le pouvez. 720 | 721 | **Mal:** 722 | 723 | ```javascript 724 | const sortieDuProgrammeur = [ 725 | { 726 | nom: "Oncle Bobby", 727 | linesDeCode: 500 728 | }, 729 | { 730 | nom: "Suzie Q", 731 | linesDeCode: 1500 732 | }, 733 | { 734 | nom: "Jimmy Gosling", 735 | linesDeCode: 150 736 | }, 737 | { 738 | nom: "Gracie Hopper", 739 | linesDeCode: 1000 740 | } 741 | ]; 742 | 743 | let sortieTotale = 0; 744 | 745 | for (let i = 0; i < sortieDuProgrammeur.length; i++) { 746 | sortieTotale += sortieDuProgrammeur[i].linesDeCode; 747 | } 748 | ``` 749 | 750 | **Bien:** 751 | 752 | ```javascript 753 | const sortieDuProgrammeur = [ 754 | { 755 | nom: "Oncle Bobby", 756 | linesDeCode: 500 757 | }, 758 | { 759 | nom: "Suzie Q", 760 | linesDeCode: 1500 761 | }, 762 | { 763 | nom: "Jimmy Gosling", 764 | linesDeCode: 150 765 | }, 766 | { 767 | nom: "Gracie Hopper", 768 | linesDeCode: 1000 769 | } 770 | ]; 771 | 772 | const sortieTotale = sortieDuProgrammeur.reduce( 773 | (linesTotal, sortie) => linesTotal + sortie.linesDeCode, 774 | 0 775 | ); 776 | ``` 777 | 778 | **[⬆ retour au sommet](#table-des-matières)** 779 | 780 | ### Encapsuler des conditions 781 | 782 | **Mal:** 783 | 784 | ```javascript 785 | if (fsm.state === "chercher" && estVide(listNode)) { 786 | // ... 787 | } 788 | ``` 789 | 790 | **Bien:** 791 | 792 | ```javascript 793 | function devraitMontrerSpinner(fsm, listNode) { 794 | return fsm.state === "chercher" && estVide(listNode); 795 | } 796 | 797 | if (devraitMontrerSpinner(fsmInstance, listNodeInstance)) { 798 | // ... 799 | } 800 | ``` 801 | 802 | **[⬆ retour au sommet](#table-des-matières)** 803 | 804 | ### Éviter les conditionnels négatifs 805 | 806 | **Mal:** 807 | 808 | ```javascript 809 | function DOMNodeNestPasPrésent(node) { 810 | // ... 811 | } 812 | 813 | if (!DOMNodeNestPasPrésent(node)) { 814 | // ... 815 | } 816 | ``` 817 | 818 | **Bien:** 819 | 820 | ```javascript 821 | function estDOMNodePresent(node) { 822 | // ... 823 | } 824 | 825 | if (estDOMNodePresent(node)) { 826 | // ... 827 | } 828 | ``` 829 | 830 | **[⬆ retour au sommet](#table-des-matières)** 831 | 832 | ### Éviter les conditionnels 833 | 834 | Cela semble être une tâche impossible. En entendant cela, la plupart des gens disent: 835 | "comment suis-je censé faire quoi que ce soit sans une déclaration`if`? " La réponse est que 836 | vous pouvez utiliser le polymorphisme pour accomplir la même tâche dans de nombreux cas. La deuxième 837 | la question est généralement, "bien c'est génial, mais pourquoi voudrais-je faire cela?" le 838 | answer est un précédent concept de code propre que nous avons appris: une fonction ne devrait faire que 839 | une chose. Quand vous avez des classes et des fonctions qui ont des instructions `if`, vous 840 | dites à votre utilisateur que votre fonction fait plus d’une chose. Rappelles toi, 841 | faites juste une chose. 842 | 843 | **Mal:** 844 | 845 | ```javascript 846 | class Avion { 847 | // ... 848 | obtenirAltitudeDeCroisière() { 849 | switch (this.type) { 850 | case "777": 851 | return this.obtenirAltitudeMax() - this.obtenirLeNombreDePassagers(); 852 | case "Air Force One": 853 | return this.obtenirAltitudeMax(); 854 | case "Cessna": 855 | return this.obtenirAltitudeMax() - this.obtenirDépensesDeCarburant(); 856 | } 857 | } 858 | } 859 | ``` 860 | 861 | **Good:** 862 | 863 | ```javascript 864 | class Avion { 865 | // ... 866 | } 867 | 868 | class Boeing777 extends Avion { 869 | // ... 870 | obtenirAltitudeDeCroisière() { 871 | return this.obtenirAltitudeMax() - this.obtenirLeNombreDePassagers(); 872 | } 873 | } 874 | 875 | class AirForceOne extends Avion { 876 | // ... 877 | obtenirAltitudeDeCroisière() { 878 | return this.obtenirAltitudeMax(); 879 | } 880 | } 881 | 882 | class Cessna extends Avion { 883 | // ... 884 | obtenirAltitudeDeCroisière() { 885 | return this.obtenirAltitudeMax() - this.obtenirDépensesDeCarburant(); 886 | } 887 | } 888 | ``` 889 | 890 | **[⬆ retour au sommet](#table-des-matières)** 891 | 892 | ### Éviter la vérification de type (partie 1) 893 | 894 | JavaScript n'est pas typé, ce qui signifie que vos fonctions peuvent accepter n'importe quel type d'argument. 895 | Parfois, vous êtes mordu par cette liberté et cela devient tentant de le faire 896 | vérification de type dans vos fonctions. Il y a plusieurs façons d'éviter d'avoir à le faire. 897 | La première chose à considérer est des API cohérentes. 898 | 899 | **Mal:** 900 | 901 | ```javascript 902 | function voyagerAuTexas(véhicule) { 903 | if (véhicule instanceof vélo) { 904 | véhicule.pédale(this.localisationActuelle, new Location("texas")); 905 | } else if (véhicule instanceof voiture) { 906 | véhicule.conduire(this.localisationActuelle, new Location("texas")); 907 | } 908 | } 909 | ``` 910 | 911 | **Bien:** 912 | 913 | ```javascript 914 | function voyagerAuTexas(véhicule) { 915 | véhicule.déplacer(this.localisationActuelle, new Location("texas")); 916 | } 917 | ``` 918 | 919 | **[⬆ retour au sommet](#table-des-matières)** 920 | 921 | ### Éviter la vérification de type (partie 2) 922 | 923 | Si vous travaillez avec des valeurs primitives de base telles que des chaînes et des entiers, 924 | et vous ne pouvez pas utiliser le polymorphisme mais vous ressentez toujours le besoin de vérifier le type, 925 | vous devriez envisager d'utiliser TypeScript. C'est une excellente alternative à la normale 926 | JavaScript, car il fournit du typage statique en plus du JavaScript standard 927 | syntaxe. Le problème avec la vérification manuelle du code JavaScript normal est que 928 | bien le faire nécessite tellement de verbiage supplémentaire que le faux "type-safety" que vous obtenez 929 | ne compense pas la lisibilité perdue. Gardez votre JavaScript propre, écrivez 930 | de bons tests et de bonnes critiques de code. Sinon, faites tout cela mais avec 931 | TypeScript (qui, comme je l'ai dit, est une excellente alternative!). 932 | 933 | **Mal:** 934 | 935 | ```javascript 936 | function combiner(val1, val2) { 937 | if ( 938 | (typeof val1 === "number" && typeof val2 === "number") || 939 | (typeof val1 === "string" && typeof val2 === "string") 940 | ) { 941 | return val1 + val2; 942 | } 943 | 944 | throw new Error("Doit être de type String ou Number"); 945 | } 946 | ``` 947 | 948 | **Bien:** 949 | 950 | ```javascript 951 | function combiner(val1, val2) { 952 | return val1 + val2; 953 | } 954 | ``` 955 | 956 | **[⬆ retour au sommet](#table-des-matières)** 957 | 958 | ### Ne pas trop optimiser 959 | 960 | Les navigateurs modernes effectuent beaucoup d’optimisation au moment de l’exécution. Beaucoup de 961 | Parfois, si vous optimisez, vous perdez simplement votre temps. [Il y a de bons 962 | Ressources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) 963 | pour voir où l'optimisation fait défaut. Ciblez-les entre-temps, jusqu'à ce que 964 | ils sont fixes s'ils peuvent l'être. 965 | 966 | **Mal:** 967 | 968 | ```javascript 969 | // Sur les anciens navigateurs, chaque itération avec `list.length` non mis en cache serait coûteuse 970 | // à cause du recalcul de `list.length`. Dans les navigateurs modernes, cela est optimisé. 971 | for (let i = 0, len = list.length; i < len; i++) { 972 | // ... 973 | } 974 | ``` 975 | 976 | **Bien:** 977 | 978 | ```javascript 979 | for (let i = 0; i < list.length; i++) { 980 | // ... 981 | } 982 | ``` 983 | 984 | **[⬆ retour au sommet](#table-des-matières)** 985 | 986 | ### Supprimer le code mort 987 | 988 | Le code mort est aussi grave qu'un code en double. Il n'y a aucune raison de le garder dans 989 | votre base de code. Si ce n'est pas appelé, éliminez-le! Il sera toujours en sécurité 990 | dans votre historique de version si vous en avez toujours besoin. 991 | 992 | **Mal:** 993 | 994 | ```javascript 995 | function ancienModuleDeDemande(url) { 996 | // ... 997 | } 998 | 999 | function nouveauModuleDeDemande(url) { 1000 | // ... 1001 | } 1002 | 1003 | const req = nouveauModuleDeDemande; 1004 | traqueurInventaire("pommes", req, "www.inventory-awesome.io"); 1005 | ``` 1006 | 1007 | **Bien:** 1008 | 1009 | ```javascript 1010 | function nouveauModuleDeDemande(url) { 1011 | // ... 1012 | } 1013 | 1014 | const req = nouveauModuleDeDemande; 1015 | traqueurInventaire("pommed", req, "www.inventory-awesome.io"); 1016 | ``` 1017 | 1018 | **[⬆ retour au sommet](#table-des-matières)** 1019 | 1020 | ## **Objets et Structures De Données** 1021 | 1022 | ### Utilisez des getters et des setters 1023 | 1024 | Utiliser des getters et des setters pour accéder aux données sur des objets pourrait être mieux que simplement 1025 | chercher une propriété sur un objet. "Pourquoi?" vous pourriez demander. Eh bien, voici un 1026 | liste non organisée des raisons pour lesquelles: 1027 | 1028 | - Lorsque vous voulez faire plus que d'obtenir une propriété d'objet, vous n'avez pas 1029 |    pour rechercher et changer tous les accesseurs de votre base de code. 1030 | - Facilite l'ajout d'une validation lors d'un `set`. 1031 | - Encapsule la représentation interne. 1032 | - Facile à ajouter la journalisation et la gestion des erreurs lors de l'obtention et la configuration. 1033 | - Vous pouvez charger paresseux les propriétés de votre objet, disons l'obtenir d'un 1034 |    serveur. 1035 | 1036 | **Mal:** 1037 | 1038 | ```javascript 1039 | function faireUnCompteBancaire() { 1040 | // ... 1041 | 1042 | return { 1043 | bilan: 0 1044 | // ... 1045 | }; 1046 | } 1047 | 1048 | const compte = faireUnCompteBancaire(); 1049 | compte.bilan = 100; 1050 | ``` 1051 | 1052 | **Bien:** 1053 | 1054 | ```javascript 1055 | function faireUnCompteBancaire() { 1056 | // celui-ci est privé 1057 | let bilan = 0; 1058 | 1059 | // un "getter", rendu public via l'objet renvoyé ci-dessous 1060 | function obtenirLeBilan() { 1061 | return bilan; 1062 | } 1063 | 1064 | // un "setter", rendu public via l'objet retourné ci-dessous 1065 | function fixerLeBilan(montante) { 1066 | // ... valider avant de mettre à jour le solde 1067 | bilan = montante; 1068 | } 1069 | 1070 | return { 1071 | // ... 1072 | obtenirLeBilan, 1073 | fixerLeBilan 1074 | }; 1075 | } 1076 | 1077 | const compte = faireUnCompteBancaire(); 1078 | compte.fixerLeBilan(100); 1079 | ``` 1080 | 1081 | **[⬆ retour au sommet](#table-des-matières)** 1082 | 1083 | ### Faire en sorte que les objets aient des membres privés 1084 | 1085 | Ceci peut être accompli par des fermetures (pour ES5 et moins). 1086 | 1087 | **Mal:** 1088 | 1089 | ```javascript 1090 | const Employé = function(nom) { 1091 | this.nom = nom; 1092 | }; 1093 | 1094 | Employé.prototype.obtenirNom = function obtenirNom() { 1095 | return this.nom; 1096 | }; 1097 | 1098 | const employé = new Employé("John Doe"); 1099 | console.log(`Nom de l'employé: ${employé.obtenirNom()}`); // Nom de l'employé: John Doe 1100 | delete employé.nom; 1101 | console.log(`Nom de l'employé: ${employé.obtenirNom()}`); // Nom de l'employé: undefined 1102 | ``` 1103 | 1104 | **Bien:** 1105 | 1106 | ```javascript 1107 | function faireEmployé(nom) { 1108 | return { 1109 | obtenirNom() { 1110 | return nom; 1111 | } 1112 | }; 1113 | } 1114 | 1115 | const employé = faireEmployé("John Doe"); 1116 | console.log(`Nom de l'employé: ${employé.obtenirNom()}`); // Nom de l'employé: John Doe 1117 | delete employé.nom; 1118 | console.log(`Nom de l'employé: ${employé.obtenirNom()}`); // Nom de l'employé: undefined 1119 | ``` 1120 | 1121 | **[⬆ retour au sommet](#table-des-matières)** 1122 | 1123 | ## **Classes** 1124 | 1125 | ### Préférez les classes ES2015 / ES6 aux fonctions simples de ES5 1126 | 1127 | Il est très difficile d'obtenir un héritage de classe, une construction et une méthode lisibles. 1128 | définitions pour les classes ES5 classiques. Si vous avez besoin d'héritage (et soyez conscient 1129 | que vous ne pourriez pas), alors préférez les classes ES2015 / ES6. Cependant, préférez les petites fonctions aux 1130 | cours jusqu’à ce que vous ayez besoin d’objets plus grands et plus complexes. 1131 | 1132 | **Mal:** 1133 | 1134 | ```javascript 1135 | const Animale = function(âge) { 1136 | if (!(this instanceof Animale)) { 1137 | throw new Error("Instancier Animal avec `new`"); 1138 | } 1139 | 1140 | this.âge = âge; 1141 | }; 1142 | 1143 | Animale.prototype.bouger = function bouger() {}; 1144 | 1145 | const Mammifère = function(age, couleurDefourrure) { 1146 | if (!(this instanceof Mammifère)) { 1147 | throw new Error("Instancier Mammifère avec `new`"); 1148 | } 1149 | 1150 | Animale.appel(this, âge); 1151 | this.couleurDefourrure = couleurDefourrure; 1152 | }; 1153 | 1154 | Mammifère.prototype = Object.create(Animale.prototype); 1155 | Mammifère.prototype.constructor = Mammifère; 1156 | Mammifère.prototype.naissanceVivante = function naissanceVivante() {}; 1157 | 1158 | const Humain = function(âge, couleurDefourrure, langueParlée) { 1159 | if (!(this instanceof Humain)) { 1160 | throw new Error("Instancier Humain avec `new`"); 1161 | } 1162 | 1163 | Mammifère.appel(this, âge, couleurDefourrure); 1164 | this.langueParlée = langueParlée; 1165 | }; 1166 | 1167 | Humain.prototype = Object.create(Mammifère.prototype); 1168 | Humain.prototype.constructor = Humain; 1169 | Humain.prototype.parler = function parler() {}; 1170 | ``` 1171 | 1172 | **Bien:** 1173 | 1174 | ```javascript 1175 | class Animale { 1176 | constructor(âge) { 1177 | this.âge = âge; 1178 | } 1179 | 1180 | bouger() { 1181 | /* ... */ 1182 | } 1183 | } 1184 | 1185 | class Mammifère extends Animale { 1186 | constructor(âge, couleurDefourrure) { 1187 | super(âge); 1188 | this.couleurDefourrure = couleurDefourrure; 1189 | } 1190 | 1191 | naissanceVivante() { 1192 | /* ... */ 1193 | } 1194 | } 1195 | 1196 | class Humain extends Mammal { 1197 | constructor(âge, couleurDefourrure, languageSpoken) { 1198 | super(âge, couleurDefourrure); 1199 | this.languageSpoken = languageSpoken; 1200 | } 1201 | 1202 | parler() { 1203 | /* ... */ 1204 | } 1205 | } 1206 | ``` 1207 | 1208 | **[⬆ retour au sommet](#table-des-matières)** 1209 | 1210 | ### Utiliser la méthode de chaînage 1211 | 1212 | Ce modèle est très utile en JavaScript et vous le voyez dans de nombreuses bibliothèques telles que 1213 | comme jQuery et Lodash. Cela permet à votre code d’être expressif et moins bavard. 1214 | Pour cette raison, dis-je, utilisez la méthode de chaînage et jetez un coup d'œil à la propreté de votre code 1215 | sera. Dans vos fonctions de classe, retournez simplement `this` à la fin de chaque fonction, 1216 | et vous pouvez y chaîner d’autres méthodes de classe. 1217 | 1218 | **Mal:** 1219 | 1220 | ```javascript 1221 | class Voiture { 1222 | constructor(marque, modèle, couleur) { 1223 | this.marque = marque; 1224 | this.modèle = modèle; 1225 | this.couleur = couleur; 1226 | } 1227 | 1228 | fixerLaMarque(marque) { 1229 | this.marque = marque; 1230 | } 1231 | 1232 | fixerLaModèle(modèle) { 1233 | this.modèle = modèle; 1234 | } 1235 | 1236 | fixerLaCouleur(couleur) { 1237 | this.couleur = couleur; 1238 | } 1239 | 1240 | enregistrer() { 1241 | console.log(this.marque, this.modèle, this.couleur); 1242 | } 1243 | } 1244 | 1245 | const voiture = new Voiture("Ford", "F-150", "rouge"); 1246 | voiture.fixerLaCouleur("rose"); 1247 | voiture.enregistrer(); 1248 | ``` 1249 | 1250 | **Bien:** 1251 | 1252 | ```javascript 1253 | class Voiture { 1254 | constructor(marque, modèle, couleur) { 1255 | this.marque = marque; 1256 | this.modèle = modèle; 1257 | this.couleur = couleur; 1258 | } 1259 | 1260 | fixerLaMarque(marque) { 1261 | this.marque = marque; 1262 | // NOTE: Renvoyer ceci pour chaîner 1263 | return this; 1264 | } 1265 | 1266 | fixerLaModèle(modèle) { 1267 | this.modèle = modèle; 1268 | // NOTE: Renvoyer ceci pour chaîner 1269 | return this; 1270 | } 1271 | 1272 | fixerLaCouleur(couleur) { 1273 | this.couleur = couleur; 1274 | // NOTE: Renvoyer ceci pour chaîner 1275 | return this; 1276 | } 1277 | 1278 | enregistrer() { 1279 | console.log(this.marque, this.modèle, this.couleur); 1280 | // NOTE: Returning this for chaining 1281 | return this; 1282 | } 1283 | } 1284 | 1285 | const voiture = new Voiture("Ford", "F-150", "rouge") 1286 | .fixerLaCouleur("rose") 1287 | .enregistrer(); 1288 | ``` 1289 | 1290 | **[⬆ retour au sommet](#table-des-matières)** 1291 | 1292 | ### Préfère la composition à l'héritage 1293 | 1294 | Comme l'a déclaré célèbre dans [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) par la Gang of Four, vous devriez préférer la composition à l'héritage lorsque vous le pouvez. Il y a beaucoup de 1295 | bonnes raisons d'utiliser l'héritage et beaucoup de bonnes raisons d'utiliser la composition. 1296 | Le point principal de cette maxime est que si votre esprit opte instinctivement pour 1297 | héritage, essayez de penser si la composition pourrait mieux modéliser votre problème. Dans certaines 1298 | cas il peut. 1299 | 1300 | Vous vous demandez peut-être alors "quand devrais-je utiliser l'héritage?" Il 1301 | dépend de votre problème actuel, mais ceci est une liste décente des cas où l'héritage 1302 | est plus logique que la composition: 1303 | 1304 | 1. Votre héritage représente une relation "est-une" et non une relation "a-une" 1305 |      relation (Humain-> Animal contre Utilisateur-> UserDetails). 1306 | 2. Vous pouvez réutiliser le code des classes de base (les humains peuvent se déplacer comme tous les animaux). 1307 | 3. Vous souhaitez apporter des modifications globales aux classes dérivées en modifiant une classe de base. 1308 |      (Changer la dépense calorique de tous les animaux quand ils bougent). 1309 | 1310 | **Mal:** 1311 | 1312 | ```javascript 1313 | class Employé { 1314 | constructor(nom, email) { 1315 | this.nom = nom; 1316 | this.email = email; 1317 | } 1318 | 1319 | // ... 1320 | } 1321 | 1322 | // Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee 1323 | class DonnéesFiscalesDuEmployé extends Employé { 1324 | constructor(ssn, salaire) { 1325 | super(); 1326 | this.ssn = ssn; 1327 | this.salaire = salaire; 1328 | } 1329 | 1330 | // ... 1331 | } 1332 | ``` 1333 | 1334 | **Bien:** 1335 | 1336 | ```javascript 1337 | class DonnéesFiscalesDuEmployé { 1338 | constructor(ssn, salaire) { 1339 | this.ssn = ssn; 1340 | this.salaire = salaire; 1341 | } 1342 | 1343 | // ... 1344 | } 1345 | 1346 | class Employé { 1347 | constructor(nom, email) { 1348 | this.nom = nom; 1349 | this.email = email; 1350 | } 1351 | 1352 | définirDonnéesFiscales(ssn, salaire) { 1353 | this.donnéesFiscales = new DonnéesFiscalesDuEmployé(ssn, salaire); 1354 | } 1355 | // ... 1356 | } 1357 | ``` 1358 | 1359 | **[⬆ retour au sommet](#table-des-matières)** 1360 | 1361 | ## **SOLID** 1362 | 1363 | ### Principe de responsabilité unique (PRU) 1364 | 1365 | Comme indiqué dans Clean Code, "Il ne devrait jamais y avoir plus d'une raison pour une classe 1366 | pour changer ". Il est tentant de confier une classe avec beaucoup de fonctionnalités, comme 1367 | lorsque vous ne pouvez emporter qu'une valise par vol. Le problème avec ceci est 1368 | que votre classe ne sera pas conceptuellement cohérente et cela lui donnera de nombreuses raisons 1369 | changer. Il est important de minimiser le nombre de fois que vous avez besoin de changer de classe. 1370 | C'est important car si trop de fonctionnalités sont dans une classe et que vous modifiez 1371 | un morceau de celui-ci, il peut être difficile de comprendre comment cela affectera les autres 1372 | modules dépendants dans votre base de code. 1373 | 1374 | **Mal:** 1375 | 1376 | ```javascript 1377 | class ParamètresUtilisateur { 1378 | constructor(utilisateur) { 1379 | this.utilisateur = utilisateur; 1380 | } 1381 | 1382 | changerParamètres(paramètres) { 1383 | if (this.vérifierIdentifications()) { 1384 | // ... 1385 | } 1386 | } 1387 | 1388 | vérifierIdentifications() { 1389 | // ... 1390 | } 1391 | } 1392 | ``` 1393 | 1394 | **Bien:** 1395 | 1396 | ```javascript 1397 | class AuthentificationUtilisateur { 1398 | constructor(utilisateur) { 1399 | this.utilisateur = utilisateur; 1400 | } 1401 | 1402 | vérifierIdentifications() { 1403 | // ... 1404 | } 1405 | } 1406 | 1407 | class ParamètresUtilisateur { 1408 | constructor(utilisateur) { 1409 | this.utilisateur = utilisateur; 1410 | this.auth = new AuthentificationUtilisateur(utilisateur); 1411 | } 1412 | 1413 | changerParamètres(paramètres) { 1414 | if (this.auth.vérifierIdentifications()) { 1415 | // ... 1416 | } 1417 | } 1418 | } 1419 | ``` 1420 | 1421 | **[⬆ retour au sommet](#table-des-matières)** 1422 | 1423 | ### Principe Ouvert / Fermé (POF) 1424 | 1425 | Selon Bertrand Meyer, "les entités logicielles (classes, modules, fonctions, 1426 | etc.) devrait être ouvert pour extension, mais fermé pour modification. "Qu'est-ce que cela 1427 | signifie cependant? Ce principe stipule essentiellement que vous devez permettre aux utilisateurs de 1428 | ajouter de nouvelles fonctionnalités sans changer le code existant. 1429 | 1430 | **Mal:** 1431 | 1432 | ```javascript 1433 | class AdaptateurAjax extends Adaptateur { 1434 | constructor() { 1435 | super(); 1436 | this.nom = "adaptateurAjax"; 1437 | } 1438 | } 1439 | 1440 | class AdaptateurNode extends Adaptateur { 1441 | constructor() { 1442 | super(); 1443 | this.nom = "adaptateurNode"; 1444 | } 1445 | } 1446 | 1447 | class RequêtementHttp { 1448 | constructor(adaptateur) { 1449 | this.adaptateur = adaptateur; 1450 | } 1451 | 1452 | fetch(url) { 1453 | if (this.adaptateur.nom === "adaptateurAjax") { 1454 | return faireAppelAjax(url).then(réponse => { 1455 | // transformer la réponse et le retour 1456 | }); 1457 | } else if (this.adaptateur.nom === "adaptateurNode") { 1458 | return faireAppelHttp(url).then(réponse => { 1459 | //transformer la réponse et le retour 1460 | }); 1461 | } 1462 | } 1463 | } 1464 | 1465 | function faireAppelAjax(url) { 1466 | // demande et retour promesse 1467 | } 1468 | 1469 | function faireAppelHttp(url) { 1470 | // demande et retour promesse 1471 | } 1472 | ``` 1473 | 1474 | **Bien:** 1475 | 1476 | ```javascript 1477 | class AdaptateurAjax extends Adaptateur { 1478 | constructor() { 1479 | super(); 1480 | this.nom = "adaptateurAjax"; 1481 | } 1482 | 1483 | demande(url) { 1484 | // demande et retour promesse 1485 | } 1486 | } 1487 | 1488 | class AdaptateurNode extends Adaptateur { 1489 | constructor() { 1490 | super(); 1491 | this.nom = "adaptateurNode"; 1492 | } 1493 | 1494 | demande(url) { 1495 | // demande et retour promesse 1496 | } 1497 | } 1498 | 1499 | class RequêtementHttp { 1500 | constructor(adaptateur) { 1501 | this.adaptateur = adaptateur; 1502 | } 1503 | 1504 | rapporter(url) { 1505 | return this.adaptateur.demande(url).then(réponse => { 1506 | //transformer la réponse et le retour 1507 | }); 1508 | } 1509 | } 1510 | ``` 1511 | 1512 | **[⬆ retour au sommet](#table-des-matières)** 1513 | 1514 | ### Principe de substitution de Liskov (PSL) 1515 | 1516 | C'est un terme effrayant pour un concept très simple. Il est formellement défini comme "Si S 1517 | est un sous-type de T, les objets de type T peuvent être remplacés par des objets de type S 1518 | (c’est-à-dire que les objets de type S peuvent se substituer aux objets de type T) sans modifier 1519 | des propriétés souhaitables de ce programme (exactitude, tâche effectuée, 1520 | etc.). "C’est une définition encore plus effrayante. 1521 | 1522 | La meilleure explication à cela est si vous avez une classe parent et une classe enfant, 1523 | alors la classe de base et la classe enfant peuvent être utilisées de manière interchangeable sans obtenir 1524 | résultats incorrects. Cela peut encore être déroutant, alors jetons un coup d'oeil à la 1525 | exemple classique de Square-Rectangle. Mathématiquement, un carré est un rectangle, mais 1526 | si vous modélisez en utilisant la relation "est-une" via l'héritage, vous rapidement 1527 | avoir des problèmes. 1528 | 1529 | **Mal:** 1530 | 1531 | ```javascript 1532 | class Rectangle { 1533 | constructor() { 1534 | this.largeur = 0; 1535 | this.taille = 0; 1536 | } 1537 | 1538 | définirLaCouleur(couleur) { 1539 | // ... 1540 | } 1541 | 1542 | rendre(surface) { 1543 | // ... 1544 | } 1545 | 1546 | définirLaLargeur(largeur) { 1547 | this.largeur = largeur; 1548 | } 1549 | 1550 | définirLaTaille(taille) { 1551 | this.taille = taille; 1552 | } 1553 | 1554 | avoireLaSurface() { 1555 | return this.largeur * this.taille; 1556 | } 1557 | } 1558 | 1559 | class Carré extends Rectangle { 1560 | définirLaLargeur(largeur) { 1561 | this.largeur = largeur; 1562 | this.taille = largeur; 1563 | } 1564 | 1565 | définirLaTaille(taille) { 1566 | this.largeur = taille; 1567 | this.taille = taille; 1568 | } 1569 | } 1570 | 1571 | function rendreLesGrandsRectangles(rectangles) { 1572 | rectangles.forEach(rectangle => { 1573 | rectangle.définirLaLargeur(4); 1574 | rectangle.définirLaTaille(5); 1575 | const surface = rectangle.avoireLaSurface(); // BAD: renvoie 25 pour Square. Devrait être 20. 1576 | rectangle.rendre(surface); 1577 | }); 1578 | } 1579 | 1580 | const rectangles = [new Rectangle(), new Rectangle(), new Carré()]; 1581 | rendreLesGrandsRectangles(rectangles); 1582 | ``` 1583 | 1584 | **Bien:** 1585 | 1586 | ```javascript 1587 | class Forme { 1588 | définirLaCouleur(couleur) { 1589 | // ... 1590 | } 1591 | 1592 | rendre(surface) { 1593 | // ... 1594 | } 1595 | } 1596 | 1597 | class Rectangle extends Forme { 1598 | constructor(largeur, taille) { 1599 | super(); 1600 | this.largeur = largeur; 1601 | this.taille = taille; 1602 | } 1603 | 1604 | avoireLaSurface() { 1605 | return this.largeur * this.taille; 1606 | } 1607 | } 1608 | 1609 | class Carré extends Forme { 1610 | constructor(longeur) { 1611 | super(); 1612 | this.longeur = longeur; 1613 | } 1614 | 1615 | getArea() { 1616 | return this.longeur * this.longeur; 1617 | } 1618 | } 1619 | 1620 | function rendreLesGrandesFormes(formes) { 1621 | formes.forEach(forme => { 1622 | const surface = forme.avoireLaSurface(); 1623 | forme.rendre(surface); 1624 | }); 1625 | } 1626 | 1627 | const formes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; 1628 | rendreLesGrandesFormes(formes); 1629 | ``` 1630 | 1631 | **[⬆ retour au sommet](#table-des-matières)** 1632 | 1633 | ### Principe de séparation des interfaces (ISP) 1634 | 1635 | JavaScript n'a pas d'interface donc ce principe ne s'applique pas aussi strictement 1636 | comme d'autres. Cependant, il est important et pertinent même avec le manque de JavaScript de JavaScript. 1637 | système de type. 1638 | 1639 | ISP déclare que "les clients ne doivent pas être obligés de dépendre d’interfaces qui 1640 | ils n'utilisent pas. "Les interfaces sont des contrats implicites en JavaScript en raison de 1641 | canard en tapant. 1642 | 1643 | Un bon exemple à regarder qui démontre que ce principe en JavaScript est pour 1644 | les classes qui nécessitent des objets de paramètres volumineux. Ne nécessite pas de clients à configurer 1645 | d'énormes quantités d'options est bénéfique, car la plupart du temps, ils n'auront pas besoin 1646 | tous les paramètres. Les rendre facultatifs aide à éviter d'avoir un 1647 | "interface grasse". 1648 | 1649 | **Mal:** 1650 | 1651 | ```javascript 1652 | class DOMTraverser { 1653 | constructor(réglages) { 1654 | this.réglages = réglages; 1655 | this.installer(); 1656 | } 1657 | 1658 | installer() { 1659 | this.rootNode = this.réglages.rootNode; 1660 | this.moduleDAnimation.installer(); 1661 | } 1662 | 1663 | traverse() { 1664 | // ... 1665 | } 1666 | } 1667 | 1668 | const $ = new DOMTraverser({ 1669 | rootNode: document.getElementsByTagName("body"), 1670 | moduleDAnimation() {} // La plupart du temps, nous n'avons pas besoin d'animer lorsque vous traversez. 1671 | // ... 1672 | }); 1673 | ``` 1674 | 1675 | **Bien:** 1676 | 1677 | ```javascript 1678 | class DOMTraverser { 1679 | constructor(réglages) { 1680 | this.réglages = réglages; 1681 | this.options = réglages.options; 1682 | this.installer(); 1683 | } 1684 | 1685 | installer() { 1686 | this.rootNode = this.réglages.rootNode; 1687 | this.optionsInstallation(); 1688 | } 1689 | 1690 | setupOptions() { 1691 | if (this.options.moduleDAnimation) { 1692 | // ... 1693 | } 1694 | } 1695 | 1696 | traverse() { 1697 | // ... 1698 | } 1699 | } 1700 | 1701 | const $ = new DOMTraverser({ 1702 | rootNode: document.getElementsByTagName("body"), 1703 | options: { 1704 | moduleDAnimation() {} 1705 | } 1706 | }); 1707 | ``` 1708 | 1709 | **[⬆ retour au sommet](#table-des-matières)** 1710 | 1711 | ### Principe d'inversion de dépendance (DIP) 1712 | 1713 | Ce principe énonce deux choses essentielles: 1714 | 1715 | 1. Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Les deux devraient 1716 |     dépend des abstractions. 1717 | 2. Les abstractions ne doivent pas dépendre des détails. Les détails devraient dépendre de 1718 |     abstractions. 1719 | 1720 | Cela peut être difficile à comprendre au début, mais si vous avez travaillé avec AngularJS, 1721 | vous avez vu une mise en œuvre de ce principe sous la forme de dépendance 1722 | Injection (DI). Bien qu’ils ne soient pas des concepts identiques, DIP conserve un haut niveau de qualité. 1723 | modules de connaître les détails de ses modules de bas niveau et de les configurer. 1724 | Cela peut être accompli grâce à DI. Un énorme avantage de cela est qu'il réduit 1725 | le couplage entre les modules. Le couplage est un très mauvais schéma de développement car 1726 | cela rend votre code difficile à refactoriser. 1727 | 1728 | Comme indiqué précédemment, JavaScript n'a pas d'interface, donc les abstractions 1729 | qui sont dépendantes sont des contrats implicites. C'est-à-dire les méthodes 1730 | et les propriétés qu'un objet / classe expose à un autre objet / classe. dans le 1731 | exemple ci-dessous, le contrat implicite est que tout module de demande pour un 1732 | `InventoryTracker` aura une méthode`requestItems`. 1733 | 1734 | **Mal:** 1735 | 1736 | ```javascript 1737 | class DemandeurInventaire { 1738 | constructor() { 1739 | this.REQ_METHODS = ["HTTP"]; 1740 | } 1741 | 1742 | demanderUnArticle(article) { 1743 | // ... 1744 | } 1745 | } 1746 | 1747 | class SuiviInventaire { 1748 | constructor(articles) { 1749 | this.articles = articles; 1750 | 1751 | // BAD: Nous avons créé une dépendance sur une implémentation de requête spécifique. 1752 | // On devrait juste avoir demanderUnArticles dépendent d'une méthode de requête: `request` 1753 | this.demandeur = new DemandeurInventaire(); 1754 | } 1755 | 1756 | demanderDesArticles() { 1757 | this.articles.forEach(article => { 1758 | this.demandeur.demanderUnArticle(article); 1759 | }); 1760 | } 1761 | } 1762 | 1763 | const traqueurInventaire = new SuiviInventaire(["pommes", "bananes"]); 1764 | traqueurInventaire.demanderDesArticles(); 1765 | ``` 1766 | 1767 | **Bien:** 1768 | 1769 | ```javascript 1770 | class SuiviInventaire { 1771 | constructor(articles, demandeur) { 1772 | this.articles = articles; 1773 | this.demandeur = demandeur; 1774 | } 1775 | 1776 | demanderDesArticles() { 1777 | this.articles.forEach(article => { 1778 | this.demandeur.demanderUnArticle(article); 1779 | }); 1780 | } 1781 | } 1782 | 1783 | class DemandeurInventaireV1 { 1784 | constructor() { 1785 | this.REQ_METHODS = ["HTTP"]; 1786 | } 1787 | 1788 | demanderUnArticle(article) { 1789 | // ... 1790 | } 1791 | } 1792 | 1793 | class DemandeurInventaireV2 { 1794 | constructor() { 1795 | this.REQ_METHODS = ["WS"]; 1796 | } 1797 | 1798 | demanderUnArticle(article) { 1799 | // ... 1800 | } 1801 | } 1802 | 1803 | // En construisant nos dépendances à l'extérieur et en les injectant, on peut facilement 1804 | // substitue notre module de requête à un nouveau module utilisant WebSockets. 1805 | const traqueurInventaire = new SuiviInventaire( 1806 | ["pommes", "bananes"], 1807 | new DemandeurInventaireV2() 1808 | ); 1809 | traqueurInventaire.demanderDesArticles(); 1810 | ``` 1811 | 1812 | **[⬆ retour au sommet](#table-des-matières)** 1813 | 1814 | ## **Testage** 1815 | 1816 | Le test est plus important que l'expédition. Si vous n'avez pas de test ou un 1817 | quantité insuffisante, alors chaque fois que vous expédiez du code, vous ne serez pas sûr de 1818 | n'a rien casser. Décider de ce qui constitue un montant adéquat est en place 1819 | à votre équipe, mais avoir une couverture de 100% (tous les états et branches) est la 1820 | vous obtenez une très grande confiance et une tranquillité d'esprit développeur. Cela signifie que 1821 | en plus d'avoir un excellent framework de test, vous devez également utiliser un 1822 | [bon outil de couverture](http://gotwarlost.github.io/istanbul/). 1823 | 1824 | Il n'y a aucune excuse pour ne pas écrire de tests. Il y a [beaucoup de bons frameworks de test JS](http://jstherightway.org/#testing-tools), Alors, trouvez-en un que votre équipe préfère. 1825 | Lorsque vous en trouvez un qui convient à votre équipe, essayez de toujours écrire des tests 1826 | pour chaque nouvelle fonctionnalité / module que vous introduisez. Si votre méthode préférée est 1827 | Test Driven Development (TDD), c’est génial, mais l’essentiel est de simplement 1828 | assurez-vous d'atteindre vos objectifs de couverture avant de lancer une fonctionnalité, 1829 | ou refactoriser un existant. 1830 | 1831 | ### Concept unique par test 1832 | 1833 | **Mal:** 1834 | 1835 | ```javascript 1836 | import assert from "assert"; 1837 | 1838 | describe("Rendre MomentJS encore une fois génial", () => { 1839 | it("gère les limites de la date", () => { 1840 | let date; 1841 | 1842 | date = new RendreMomentJSEncoreUneFoisGénial("1/1/2015"); 1843 | date.addDays(30); 1844 | assert.equal("1/31/2015", date); 1845 | 1846 | date = new RendreMomentJSEncoreUneFoisGénial("2/1/2016"); 1847 | date.addDays(28); 1848 | assert.equal("02/29/2016", date); 1849 | 1850 | date = new RendreMomentJSEncoreUneFoisGénial("2/1/2015"); 1851 | date.addDays(28); 1852 | assert.equal("03/01/2015", date); 1853 | }); 1854 | }); 1855 | ``` 1856 | 1857 | **Bien:** 1858 | 1859 | ```javascript 1860 | import assert from "assert"; 1861 | 1862 | describe("Rendre MomentJS encore une fois génial", () => { 1863 | it("gère les mois de 30 jours", () => { 1864 | const date = new RendreMomentJSEncoreUneFoisGénial("1/1/2015"); 1865 | date.ajouterLesJours(30); 1866 | assert.equal("1/31/2015", date); 1867 | }); 1868 | 1869 | it("gère l'année bissextile", () => { 1870 | const date = new RendreMomentJSEncoreUneFoisGénial("2/1/2016"); 1871 | date.ajouterLesJours(28); 1872 | assert.equal("02/29/2016", date); 1873 | }); 1874 | 1875 | it("gère l'année non bissextile", () => { 1876 | const date = new RendreMomentJSEncoreUneFoisGénial("2/1/2015"); 1877 | date.ajouterLesJours(28); 1878 | assert.equal("03/01/2015", date); 1879 | }); 1880 | }); 1881 | ``` 1882 | 1883 | **[⬆ retour au sommet](#table-des-matières)** 1884 | 1885 | ## **Simultanéité** 1886 | 1887 | ### Utilisez des promesses, pas des rappels 1888 | 1889 | Les rappels ne sont pas propres et provoquent des quantités excessives d'imbrication. Avec ES2015 / ES6, 1890 | Les promesses sont un type global intégré. Utilise les! 1891 | 1892 | **Mal:** 1893 | 1894 | ```javascript 1895 | import { get } from "request"; 1896 | import { writeFile } from "fs"; 1897 | 1898 | get( 1899 | "https://en.wikipedia.org/wiki/Robert_Cecil_Martin", 1900 | (errDemande, réponse) => { 1901 | if (errDemande) { 1902 | console.error(errDemande); 1903 | } else { 1904 | writeFile("article.html", réponse.body, writeErr => { 1905 | if (writeErr) { 1906 | console.error(writeErr); 1907 | } else { 1908 | console.log("Fichier écrit"); 1909 | } 1910 | }); 1911 | } 1912 | } 1913 | ); 1914 | ``` 1915 | 1916 | **Bien:** 1917 | 1918 | ```javascript 1919 | import { get } from "request"; 1920 | import { writeFile } from "fs"; 1921 | 1922 | get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") 1923 | .then(réponse => { 1924 | return writeFile("article.html", réponse); 1925 | }) 1926 | .then(() => { 1927 | console.log("Fichier écrit"); 1928 | }) 1929 | .catch(err => { 1930 | console.error(err); 1931 | }); 1932 | ``` 1933 | 1934 | **[⬆ retour au sommet](#table-des-matières)** 1935 | 1936 | ### Async / Await sont encore plus propres que Promises 1937 | 1938 | Les promesses sont une alternative très propre aux callbacks, mais ES2017 / ES8 apporte async et attend 1939 | qui offrent une solution encore plus propre. Tout ce dont vous avez besoin est une fonction préfixée 1940 | dans un mot clé `async`, et vous pourrez alors écrire votre logique impérativement sans 1941 | une chaîne de fonctions "alors". Utilisez-le si vous pouvez tirer parti des fonctionnalités de ES2017 / ES8 1942 | aujourd'hui! 1943 | 1944 | **Mal:** 1945 | 1946 | ```javascript 1947 | import { get } from "request-promise"; 1948 | import { writeFile } from "fs-promise"; 1949 | 1950 | get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") 1951 | .then(réponse => { 1952 | return writeFile("article.html", réponse); 1953 | }) 1954 | .then(() => { 1955 | console.log("Fichier écrit"); 1956 | }) 1957 | .catch(err => { 1958 | console.error(err); 1959 | }); 1960 | ``` 1961 | 1962 | **Bien:** 1963 | 1964 | ```javascript 1965 | import { get } from "request-promise"; 1966 | import { writeFile } from "fs-promise"; 1967 | 1968 | async function obtenirUnArticleDeCodePropre() { 1969 | try { 1970 | const response = await get( 1971 | "https://en.wikipedia.org/wiki/Robert_Cecil_Martin" 1972 | ); 1973 | await writeFile("article.html", réponse); 1974 | console.log("Fichier écrit"); 1975 | } catch (err) { 1976 | console.error(err); 1977 | } 1978 | } 1979 | ``` 1980 | 1981 | **[⬆ retour au sommet](#table-des-matières)** 1982 | 1983 | ## **La Gestion des Erreurs** 1984 | 1985 | Les erreurs jetées sont une bonne chose! Ils veulent dire que le runtime a réussi 1986 | identifié quand quelque chose dans votre programme a mal tourné et il laisse 1987 | vous savez en arrêtant l'exécution de la fonction sur la pile en cours, tuant le 1988 | processus (en nœud), et vous notifiant dans la console avec une trace de pile. 1989 | 1990 | ### Ne pas ignorer les erreurs interceptées 1991 | 1992 | Ne rien faire avec une erreur interceptée ne vous donne pas la possibilité de réparer 1993 | ou réagir à cette erreur. Consigner l'erreur dans la console (`console.log`) 1994 | n'est pas beaucoup mieux que souvent, il peut se perdre dans une mer de choses imprimées 1995 | à la console. Si vous intégrez un morceau de code dans un `try / catch`, cela signifie que vous 1996 | pense qu'une erreur peut se produire là-bas et que vous devriez donc avoir un plan, 1997 | ou créer un chemin de code, pour quand il se produit. 1998 | 1999 | **Mal:** 2000 | 2001 | ```javascript 2002 | try { 2003 | fonctionQuiPourraitJeter(); 2004 | } catch (error) { 2005 | console.log(error); 2006 | } 2007 | ``` 2008 | 2009 | **Bien:** 2010 | 2011 | ```javascript 2012 | try { 2013 | fonctionQuiPourraitJeter(); 2014 | } catch (error) { 2015 | // Une option (plus bruyante que console.log): 2016 | console.error(error); 2017 | // Une autre option: 2018 | notifierUtilisateurDuErreur(error); 2019 | // Une autre option: 2020 | signalerAuServiceDuErreur(error); 2021 | // OU faire tous les trois! 2022 | } 2023 | ``` 2024 | 2025 | ### Ne pas ignorer les promesses rejetées 2026 | 2027 | Pour la même raison, vous ne devez pas ignorer les erreurs interceptées 2028 | de `try / catch`. 2029 | 2030 | **Mal:** 2031 | 2032 | ```javascript 2033 | getdata() 2034 | .then(données => { 2035 | fonctionQuiPourraitJeter(données); 2036 | }) 2037 | .catch(error => { 2038 | console.log(error); 2039 | }); 2040 | ``` 2041 | 2042 | **Good:** 2043 | 2044 | ```javascript 2045 | getdata() 2046 | .then(données => { 2047 | fonctionQuiPourraitJeter(données); 2048 | }) 2049 | .catch(error => { 2050 | // Une option (plus bruyante que console.log): 2051 | console.error(error); 2052 | // Une autre option: 2053 | notifierUtilisateurDuErreur(error); 2054 | // Une autre option: 2055 | signalerAuServiceDuErreur(error); 2056 | // OU faire tous les trois! 2057 | }); 2058 | ``` 2059 | 2060 | **[⬆ retour au sommet](#table-des-matières)** 2061 | 2062 | ## **Mise En Forme** 2063 | 2064 | Le formatage est subjectif. Comme beaucoup de règles, il n’existe pas de solution simple et rapide. 2065 | règle que vous devez suivre. L'essentiel est de NE PAS ARGUER sur le formatage. 2066 | Il y a [des tonnes d'outils](http://standardjs.com/rules.html) pour automatiser cela. 2067 | Utilisez-en un! Discuter de la mise en forme est une perte de temps et d'argent pour les ingénieurs. 2068 | 2069 | Pour les choses qui ne relèvent pas du formatage automatique 2070 | (indentation, tabulations vs espaces, guillemets doubles vs simples, etc.) regardez ici 2071 | pour des conseils. 2072 | 2073 | ### Utilisez une capitalisation cohérente 2074 | 2075 | JavaScript n'est pas typé, la capitalisation en dit beaucoup sur vos variables, 2076 | fonctions, etc. Ces règles sont subjectives, votre équipe peut donc choisir ce que 2077 | Ils veulent. Le fait est que, peu importe ce que vous choisissez tous, soyez juste cohérent. 2078 | 2079 | **Mal:** 2080 | 2081 | ```javascript 2082 | const JOURS_PAR_SEMAINE = 7; 2083 | const joursParMois = 30; 2084 | 2085 | const chansons = ["Back In Black", "Stairway to Heaven", "Hey Jude"]; 2086 | const Artists = ["ACDC", "Led Zeppelin", "The Beatles"]; 2087 | 2088 | function effacerLeDatabase() {} 2089 | function restaurer_Ledatabase() {} 2090 | 2091 | class animale {} 2092 | class Alpaga {} 2093 | ``` 2094 | 2095 | **Bien:** 2096 | 2097 | ```javascript 2098 | const JOURS_PAR_SEMAINE = 7; 2099 | const JOURS_PAR_MOIS = 30; 2100 | 2101 | const CHANSONS = ["Back In Black", "Stairway to Heaven", "Hey Jude"]; 2102 | const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"]; 2103 | 2104 | function effacerLeDatabase() {} 2105 | function restaurerLeDatabase() {} 2106 | 2107 | class Animale {} 2108 | class Alpaga {} 2109 | ``` 2110 | 2111 | **[⬆ retour au sommet](#table-des-matières)** 2112 | 2113 | ### Les appelants et les correspondants doivent être rapprochés 2114 | 2115 | Si une fonction en appelle une autre, gardez ces fonctions verticalement proches dans la source 2116 | fichier. Idéalement, laissez l'appelant juste au-dessus de l'appelé. Nous avons tendance à lire le code de 2117 | de haut en bas, comme un journal. Pour cette raison, faites votre code lu de cette façon. 2118 | 2119 | **Mal:** 2120 | 2121 | ```javascript 2122 | class ExamenDuRendement { 2123 | constructor(employé) { 2124 | this.employé = employé; 2125 | } 2126 | 2127 | lookupCollègues() { 2128 | return db.lookup(this.employé, "collègues"); 2129 | } 2130 | 2131 | lookupDirecteur() { 2132 | return db.lookup(this.employé, "directeur"); 2133 | } 2134 | 2135 | obtenirEvaluationsDesCollègues() { 2136 | const collègues = this.lookupCollègues(); 2137 | // ... 2138 | } 2139 | 2140 | examenDuRendement() { 2141 | this.obtenirEvaluationsDesCollègues(); 2142 | this.obtenirEvaluationsDuDirecteur(); 2143 | this.obtenirEvaluationDeSoi(); 2144 | } 2145 | 2146 | obtenirEvaluationsDuDirecteur() { 2147 | const manager = this.lookupDirecteur(); 2148 | } 2149 | 2150 | obtenirEvaluationDeSoi() { 2151 | // ... 2152 | } 2153 | } 2154 | 2155 | const evaluation = new ExamenDuRendement(employé); 2156 | evaluation.examenDuRendement(); 2157 | ``` 2158 | 2159 | **Bien:** 2160 | 2161 | ```javascript 2162 | class ExamenDuRendement { 2163 | constructor(employé) { 2164 | this.employé = employé; 2165 | } 2166 | 2167 | examenDuRendement() { 2168 | this.obtenirEvaluationsDesCollègues(); 2169 | this.obtenirEvaluationsDuDirecteur(); 2170 | this.obtenirEvaluationDeSoi(); 2171 | } 2172 | 2173 | obtenirEvaluationsDesCollègues() { 2174 | const collègues = this.lookupCollègues(); 2175 | // ... 2176 | } 2177 | 2178 | lookupCollègues() { 2179 | return db.lookup(this.employé, "collègues"); 2180 | } 2181 | 2182 | obtenirEvaluationsDuDirecteur() { 2183 | const manager = this.lookupDirecteur(); 2184 | } 2185 | 2186 | lookupDirecteur() { 2187 | return db.lookup(this.employé, "directeur"); 2188 | } 2189 | 2190 | obtenirEvaluationDeSoi() { 2191 | // ... 2192 | } 2193 | } 2194 | 2195 | const evaluation = new ExamenDuRendement(employé); 2196 | evaluation.examenDuRendement(); 2197 | ``` 2198 | 2199 | **[⬆ retour au sommet](#table-des-matières)** 2200 | 2201 | ## **Commentaires** 2202 | 2203 | ### Ne commentez que les choses qui ont une complexité de logique métier. 2204 | 2205 | Les commentaires sont des excuses, pas une exigence. Bon code _mostly_ se documente. 2206 | 2207 | **Mal:** 2208 | 2209 | ```javascript 2210 | function hasher(données) { 2211 | // La hash 2212 | let hash = 0; 2213 | 2214 | // Longeur du string 2215 | const longeur = données.longeur; 2216 | 2217 | // Boucle à travers chaque caractère dans les données 2218 | for (let i = 0; i < length; i++) { 2219 | // Obtenez le code de caractère. 2220 | const char = données.charCodeAt(i); 2221 | // Faire le hash 2222 | hash = (hash << 5) - hash + char; 2223 | // Convertir en entier 32 bits 2224 | hash &= hash; 2225 | } 2226 | } 2227 | ``` 2228 | 2229 | **Bien:** 2230 | 2231 | ```javascript 2232 | function hasher(données) { 2233 | let hash = 0; 2234 | const longeur = données.longeur; 2235 | 2236 | for (let i = 0; i < length; i++) { 2237 | const char = données.charCodeAt(i); 2238 | hash = (hash << 5) - hash + char; 2239 | 2240 | // Convertir en entier 32 bits 2241 | hash &= hash; 2242 | } 2243 | } 2244 | ``` 2245 | 2246 | **[⬆ retour au sommet](#table-des-matières)** 2247 | 2248 | ### Ne laissez pas de code commenté dans votre base de code 2249 | 2250 | Le contrôle de version existe pour une raison. Laissez l'ancien code dans votre historique. 2251 | 2252 | **Mal:** 2253 | 2254 | ```javascript 2255 | faireUneChose(); 2256 | // faireAutreChoses(); 2257 | // faireEncoreAutreChoses(); 2258 | // faireTropDeChoses(); 2259 | ``` 2260 | 2261 | **Bien:** 2262 | 2263 | ```javascript 2264 | faireUneChose(); 2265 | ``` 2266 | 2267 | **[⬆ retour au sommet](#table-des-matières)** 2268 | 2269 | ### Je n'ai pas de commentaires dans le journal 2270 | 2271 | Rappelez-vous, utilisez le contrôle de version! Il n'y a pas besoin de code mort, de code commenté, 2272 | et surtout les commentaires de journaux. Utilisez `git log` pour obtenir l'historique! 2273 | 2274 | **Mal:** 2275 | 2276 | ```javascript 2277 | /** 2278 | * 2016-12-20: Monades supprimées, je ne les ai pas comprises (RM) 2279 | * 2016-10-01: Amélioration de l'utilisation de monades spéciales (JP) 2280 | * 2016-02-03: Suppression de la vérification de type (LI) 2281 | * 2015-03-14: Ajout d'une combinaison avec la vérification de type (JR) 2282 | */ 2283 | function combiner(a, b) { 2284 | return a + b; 2285 | } 2286 | ``` 2287 | 2288 | **Bien:** 2289 | 2290 | ```javascript 2291 | function combiner(a, b) { 2292 | return a + b; 2293 | } 2294 | ``` 2295 | 2296 | **[⬆ retour au sommet](#table-des-matières)** 2297 | 2298 | ### Évitez les marqueurs de position 2299 | 2300 | Ils ne font généralement qu'ajouter du bruit. Laissez les fonctions et les noms de variables avec le 2301 | L’indentation et le formatage appropriés donnent la structure visuelle à votre code. 2302 | 2303 | **Mal:** 2304 | 2305 | ```javascript 2306 | //////////////////////////////////////////////////////////////////////////////// 2307 | // Instanciation du modèle de portée 2308 | //////////////////////////////////////////////////////////////////////////////// 2309 | $scope.model = { 2310 | menu: "foo", 2311 | nav: "bar" 2312 | }; 2313 | 2314 | //////////////////////////////////////////////////////////////////////////////// 2315 | // Configuration de l'action 2316 | //////////////////////////////////////////////////////////////////////////////// 2317 | const actions = function() { 2318 | // ... 2319 | }; 2320 | ``` 2321 | 2322 | **Bien:** 2323 | 2324 | ```javascript 2325 | $scope.model = { 2326 | menu: "foo", 2327 | nav: "bar" 2328 | }; 2329 | 2330 | const actions = function() { 2331 | // ... 2332 | }; 2333 | ``` 2334 | 2335 | **[⬆ retour au sommet](#table-des-matières)** 2336 | 2337 | ## Traduction 2338 | 2339 | Ceci est également disponible dans d'autres langues: 2340 | 2341 | - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **Français**: 2342 | [GavBaros/clean-code-javascript-fr](https://github.com/GavBaros/clean-code-javascript-fr) 2343 | - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Portugais Brésilien**: [fesnt/clean-code-javascript](https://github.com/fesnt/clean-code-javascript) 2344 | - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Uruguay.png) **Espanol**: [andersontr15/clean-code-javascript](https://github.com/andersontr15/clean-code-javascript-es) 2345 | - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Espanol**: [tureey/clean-code-javascript](https://github.com/tureey/clean-code-javascript) 2346 | - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinois**: 2347 | - [alivebao/clean-code-js](https://github.com/alivebao/clean-code-js) 2348 | - [beginor/clean-code-javascript](https://github.com/beginor/clean-code-javascript) 2349 | - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **Allemand**: [marcbruederlin/clean-code-javascript](https://github.com/marcbruederlin/clean-code-javascript) 2350 | - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Coréen**: [qkraudghgh/clean-code-javascript-ko](https://github.com/qkraudghgh/clean-code-javascript-ko) 2351 | - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polonais**: [greg-dev/clean-code-javascript-pl](https://github.com/greg-dev/clean-code-javascript-pl) 2352 | - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russe**: 2353 | - [BoryaMogila/clean-code-javascript-ru/](https://github.com/BoryaMogila/clean-code-javascript-ru/) 2354 | - [maksugr/clean-code-javascript](https://github.com/maksugr/clean-code-javascript) 2355 | - ![vi](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamien**: [hienvd/clean-code-javascript/](https://github.com/hienvd/clean-code-javascript/) 2356 | - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japonais**: [mitsuruog/clean-code-javascript/](https://github.com/mitsuruog/clean-code-javascript/) 2357 | - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Indonésie**: 2358 | [andirkh/clean-code-javascript/](https://github.com/andirkh/clean-code-javascript/) 2359 | - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italien**: 2360 | [frappacchio/clean-code-javascript/](https://github.com/frappacchio/clean-code-javascript/) 2361 | 2362 | **[⬆ retour au sommet](#table-des-matières)** 2363 | --------------------------------------------------------------------------------