└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # clean-code-javascript 2 | Original repository: https://github.com/ryanmcdermott/clean-code-javascript 3 | 4 | ## Sommaire 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. [Tests](#Tests) 13 | 8. [Concurrence](#Concurrence) 14 | 9. [Gestion des erreurs](#Gestion-des-erreurs) 15 | 10. [Formatage](#Formatage) 16 | 11. [Commentaires](#Commentaires) 17 | 18 | ## Introduction 19 | 20 | Principes d'ingénierie logicielle, du livre [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) 21 | de Robert C. Martin's adapté au JavaScript. 22 | Il ne s'agit pas d'un guide de style. C'est un guide pour produire des logiciels 23 | [lisibles, réutilisables, et refactorisables](https://github.com/ryanmcdermott/3rs-of-software-architecture) en JavaScript. 24 | 25 | Il n'est pas nécessaire de suivre à la lettre tous les principes énoncés dans le présent document, et rares sont ceux qui feront l'objet d'un consensus universellement accepté. Il s'agit de lignes directrices et rien de plus, mais elles ont été codifiées par les auteurs de _Clean Code_ au fil de nombreuses années d'expérience collective. 26 | 27 | Notre métier d'ingénieur logiciel a un peu plus de 50 ans, et nous apprenons encore beaucoup. Lorsque l'architecture logicielle sera aussi vieille que l'architecture elle-même, nous aurons peut-être alors des règles plus difficiles à suivre. Pour l'instant, laissez ces directives servir de pierre de touche pour évaluer la qualité du code JavaScript que vous et votre équipe produisez. 28 | 29 | Une dernière chose : connaître ces règles ne fera pas immédiatement de vous un meilleur développeur de logiciels, et travailler avec elles pendant de nombreuses années ne signifie pas que vous ne ferez pas d'erreurs. Chaque morceau de code commence par une première ébauche, comme de l'argile humide que l'on façonne pour lui donner sa forme finale. Enfin, nous ciselons les imperfections lorsque nous le révisons avec nos pairs. Ne vous blâmez pas pour les premières ébauches qui doivent être améliorer. Battez-vous plutôt contre le code ! 30 | 31 | ## **Variables** 32 | 33 | ### Utiliser des noms de variables explicites et prononçables 34 | 35 | **Mauvais :** 36 | 37 | ```javascript 38 | const yyyymmdstr = moment().format("YYYY/MM/DD"); 39 | ``` 40 | 41 | **Bien :** 42 | 43 | ```javascript 44 | const currentDate = moment().format("YYYY/MM/DD"); 45 | ``` 46 | 47 | **[⬆ retour en haut](#sommaire)** 48 | 49 | ### Utiliser le même vocabulaire pour le même type de variable 50 | 51 | **Mauvais :** 52 | 53 | ```javascript 54 | getUserInfo(); 55 | getClientData(); 56 | getCustomerRecord(); 57 | ``` 58 | 59 | **Bien :** 60 | 61 | ```javascript 62 | getUser(); 63 | ``` 64 | 65 | **[⬆ retour en haut](#sommaire)** 66 | 67 | ### Utiliser des noms recherchables 68 | 69 | Nous lirons plus de code que nous n'en écrirons jamais. Il est important que le code que nous écrivons soit lisible et consultable. En _ne nommant pas_ les variables qui finissent par être significatives pour la compréhension de notre programme, nous nuisons à nos lecteurs. 70 | Faites en sorte que vos noms soient consultables. Des outils comme [buddy.js](https://github.com/danielstjules/buddy.js) et [ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) peuvent vous aider à identifier les constantes sans nom. 71 | 72 | **Mauvais :** 73 | 74 | ```javascript 75 | // À quoi sert 86400000 ? 76 | setTimeout(blastOff, 86400000); 77 | ``` 78 | 79 | **Bien :** 80 | 81 | ```javascript 82 | // Déclarez-les en tant que constantes nommées en majuscules. 83 | const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000; 84 | setTimeout(blastOff, MILLISECONDS_PER_DAY); 85 | ``` 86 | 87 | **[⬆ retour en haut](#sommaire)** 88 | 89 | ### Utiliser des variables explicatives 90 | 91 | **Mauvais :** 92 | 93 | ```Javascript 94 | const address = "One Infinite Loop, Cupertino 95014" ; 95 | const cityZipCodeRegex = /^[^,\\]+[,\\\\\s]+(.+ ?)\s*(\d{5})?$/ ; 96 | saveCityZipCode( 97 | adresse.match(cityZipCodeRegex)[1], 98 | adresse.match(cityZipCodeRegex)[2] 99 | ) ; 100 | ``` 101 | 102 | **Bien :** 103 | 104 | ```Javascript 105 | const address = "One Infinite Loop, Cupertino 95014" ; 106 | const cityZipCodeRegex = /^[^,\\]+[,\\\\\s]+(.+ ?)\s*(\d{5})?$/ ; 107 | const [_, city, zipCode] = address.match(cityZipCodeRegex) || [] ; 108 | saveCityZipCode(city, zipCode) ; 109 | ``` 110 | 111 | **[⬆ retour en haut](#sommaire)** 112 | 113 | ### Éviter la cartographie mentale 114 | 115 | L'explicite est préférable à l'implicite. 116 | 117 | **Mauvais :** 118 | 119 | ```Javascript 120 | const locations = ["Austin", "New York", "San Francisco"] ; 121 | locations.forEach(l => { 122 | doStuff() ; 123 | doSomeOtherStuff() ; 124 | // ... 125 | // ... 126 | // ... 127 | // Attendez, à quoi sert `l` déjà ? 128 | dispatch(l) ; 129 | }) ; 130 | ``` 131 | 132 | **Bien :** 133 | 134 | ```Javascript 135 | const locations = ["Austin", "New York", "San Francisco"] ; 136 | locations.forEach(location => { 137 | doStuff() ; 138 | doSomeOtherStuff() ; 139 | // ... 140 | // ... 141 | // ... 142 | dispatch(location) ; 143 | }) ; 144 | ``` 145 | 146 | **[⬆ retour en haut](#sommaire)** 147 | 148 | ### Ne pas ajouter de contexte inutile 149 | 150 | Si le nom de votre classe/objet vous indique quelque chose, ne le répétez pas dans votre nom de variable. 151 | 152 | **Mauvais :** 153 | 154 | ```Javascript 155 | const Car = { 156 | carMake: "Honda", 157 | carModel: "Accord", 158 | carColor: "Blue" 159 | } ; 160 | function paintCar(car, color) { 161 | car.carColor = color ; 162 | } 163 | ``` 164 | 165 | **Bien :** 166 | 167 | ```Javascript 168 | const Car = { 169 | make: "Honda", 170 | model: "Accord", 171 | color: "Blue" 172 | } ; 173 | function paintCar(car, color) { 174 | car.color = color ; 175 | } 176 | ``` 177 | 178 | **[⬆ retour en haut](#sommaire)** 179 | 180 | ### Utiliser des arguments par défaut à la place des court-circuits ou des conditions 181 | 182 | Les arguments par défaut sont souvent plus propres que le court-circuitage. 183 | Soyez conscient que si vous les utilisez, votre fonction ne fournira des valeurs par défaut que pour les arguments `non définis`. 184 | Les autres valeurs "fausses" telles que `''`, `""`, `false`, `null`, `0`, et `NaN`, ne seront pas remplacées par une valeur par défaut. 185 | 186 | **Mauvais :** 187 | 188 | ```Javascript 189 | function createMicrobrewery(name) { 190 | const breweryName = name || "Hipster Brew Co." ; 191 | // ... 192 | } 193 | ``` 194 | 195 | **Bien :** 196 | 197 | ```Javascript 198 | function createMicrobrewery(name = "Hipster Brew Co.") { 199 | // ... 200 | } 201 | ``` 202 | 203 | **[⬆ retour en haut](#sommaire)** 204 | 205 | ## **Fonctions** 206 | 207 | ### Arguments de fonction (2 ou moins idéalement) 208 | 209 | Limiter le nombre de paramètres de fonction est extrêmement important car cela facilite le test de votre fonction. 210 | En avoir plus de trois conduit à une explosion combinatoire où vous devez tester des tonnes de cas différents avec chaque argument séparé. 211 | 212 | Un ou deux arguments est le cas idéal, et trois devraient être évités si possible. 213 | Tout ce qui est supérieur à cela doit être consolidé. En général, si vous avez plus de deux arguments, c'est que votre fonction essaie d'en faire trop. 214 | Dans les cas où ce n'est pas le cas, la plupart du temps, un objet de niveau supérieur suffira comme argument. 215 | 216 | Étant donné que JavaScript vous permet de créer des objets à la volée, sans avoir besoin d'un grand nombre de classes passe-partout, vous pouvez utiliser un objet si vous avez besoin de beaucoup d'arguments. 217 | 218 | Pour rendre évidentes les propriétés attendues par la fonction, vous pouvez utiliser la syntaxe de déstructuration ES2015/ES6. 219 | Cela présente quelques avantages : 220 | 1. Lorsqu'on regarde la signature de la fonction, on voit immédiatement quelles propriétés sont utilisées. 221 | 2. Elle peut être utilisée pour simuler des paramètres nommés. 222 | 3. La déstructuration clone également les valeurs primitives spécifiées de l'objet argument passé dans la fonction. Cela peut aider à prévenir les effets secondaires. 223 | Remarque : les objets et les tableaux qui sont déstructurés à partir de l'objet argument ne sont PAS clonés. 224 | 4. Les linters peuvent vous avertir des propriétés inutilisées, ce qui serait impossible sans la déstructuration. 225 | 226 | **Mauvais :** 227 | 228 | ```javascript 229 | function createMenu(title, body, buttonText, cancellable) { 230 | // ... 231 | } 232 | createMenu("Foo", "Bar", "Baz", true); 233 | ``` 234 | 235 | **Bien :** 236 | 237 | ```javascript 238 | function createMenu({ title, body, buttonText, cancellable }) { 239 | // ... 240 | } 241 | createMenu({ 242 | title: "Foo", 243 | body: "Bar", 244 | buttonText: "Baz", 245 | cancellable: true 246 | }); 247 | ``` 248 | 249 | **[⬆ retour en haut](#sommaire)** 250 | 251 | ### Les fonctions doivent faire une seule chose 252 | 253 | C'est de loin la règle la plus importante en ingénierie logicielle. 254 | Lorsque les fonctions font plus d'une chose, elles sont plus difficiles à composer, à tester et à raisonner. 255 | Lorsque vous pouvez isoler une fonction pour une seule action, elle peut être facilement remaniée et votre code sera beaucoup plus propre. 256 | Si vous ne retenez rien d'autre de ce guide, vous aurez une longueur d'avance sur de nombreux développeurs. 257 | 258 | **Mauvais :** 259 | 260 | ```javascript 261 | function emailClients(clients) { 262 | clients.forEach(client => { 263 | const clientRecord = database.lookup(client); 264 | if (clientRecord.isActive()) { 265 | email(client); 266 | } 267 | }); 268 | } 269 | ``` 270 | 271 | **Bien :** 272 | 273 | ```javascript 274 | function emailActiveClients(clients) { 275 | clients.filter(isActiveClient).forEach(email); 276 | } 277 | function isActiveClient(client) { 278 | const clientRecord = database.lookup(client); 279 | return clientRecord.isActive(); 280 | } 281 | ``` 282 | 283 | **[⬆ retour en haut](#sommaire)** 284 | 285 | ### Les noms des fonctions devraient dire ce qu'elles font 286 | 287 | **Mauvais :** 288 | 289 | ```Javascript 290 | function addToDate(date, mois) { 291 | // ... 292 | } 293 | const date = new Date() ; 294 | // Il est difficile de dire, à partir du nom de la fonction, ce qui est ajouté 295 | addToDate(date, 1) ; 296 | ``` 297 | 298 | **Bien :** 299 | 300 | ```Javascript 301 | function addMonthToDate(month, date) { 302 | // ... 303 | } 304 | const date = new Date() ; 305 | addMonthToDate(1, date) ; 306 | ``` 307 | 308 | **[⬆ retour en haut](#sommaire)** 309 | 310 | ### Les fonctions ne doivent avoir qu'un seul niveau d'abstraction. 311 | 312 | Lorsque vous avez plus d'un niveau d'abstraction, votre fonction fait généralement trop de choses. 313 | Le découpage des fonctions permet de les réutiliser et facilite les tests. 314 | 315 | **Mauvais :** 316 | 317 | ```Javascript 318 | function parseBetterJSAlternative(code) { 319 | const REGEXES = [ 320 | // ... 321 | ] ; 322 | const statements = code.split(" ") ; 323 | const tokens = [] ; 324 | REGEXES.forEach(REGEX => { 325 | statements.forEach(statement => { 326 | // ... 327 | }) ; 328 | }) ; 329 | const ast = [] ; 330 | tokens.forEach(token => { 331 | // lex... 332 | }) ; 333 | ast.forEach(node => { 334 | // analyse syntaxique... 335 | }) ; 336 | } 337 | ``` 338 | 339 | **Bien :** 340 | 341 | ```javascript 342 | function parseBetterJSAlternative(code) { 343 | const tokens = tokenize(code); 344 | const syntaxTree = parse(tokens); 345 | syntaxTree.forEach(node => { 346 | // parse... 347 | }); 348 | } 349 | function tokenize(code) { 350 | const REGEXES = [ 351 | // ... 352 | ]; 353 | const statements = code.split(" "); 354 | const tokens = []; 355 | REGEXES.forEach(REGEX => { 356 | statements.forEach(statement => { 357 | tokens.push(/* ... */); 358 | }); 359 | }); 360 | return tokens; 361 | } 362 | function parse(tokens) { 363 | const syntaxTree = []; 364 | tokens.forEach(token => { 365 | syntaxTree.push(/* ... */); 366 | }); 367 | return syntaxTree; 368 | } 369 | ``` 370 | 371 | **[⬆ retour en haut](#sommaire)** 372 | 373 | ### Supprimer le code dupliqué 374 | 375 | Faites de votre mieux pour éviter les doublons. 376 | Le code dupliqué est mauvais car cela signifie qu'il y a plus d'un endroit où modifier quelque chose si vous devez changer une logique. 377 | 378 | Imaginez que vous dirigez un restaurant et que vous gardez la trace de votre inventaire : toutes vos tomates, oignons, ail, épices, etc. 379 | Si vous disposez de plusieurs listes, elles doivent toutes être mises à jour lorsque vous servez un plat contenant des tomates. 380 | Si vous n'avez qu'une seule liste, il n'y a qu'un seul endroit à mettre à jour ! 381 | 382 | Souvent, vous avez du code dupliqué parce que vous avez deux ou plusieurs choses légèrement différentes, qui ont beaucoup en commun, mais leurs différences vous obligent à avoir deux ou plusieurs fonctions distinctes qui font à peu près la même chose. 383 | Supprimer le code dupliqué signifie créer une abstraction qui peut gérer cet ensemble de différences avec une seule fonction/module/classe. 384 | 385 | Il est essentiel d'obtenir une abstraction correcte, c'est pourquoi vous devez suivre les principes SOLID énoncés dans la section _Classes_. 386 | De mauvaises abstractions peuvent être pires que du code dupliqué, alors faites attention ! 387 | Ceci étant dit, si vous pouvez faire une bonne abstraction, faites-le ! 388 | 389 | __Ne vous répétez pas__, sinon vous vous retrouverez à mettre à jour plusieurs endroits chaque fois que vous voudrez changer une chose. 390 | 391 | **Mauvais :** 392 | 393 | ```javascript 394 | function showDeveloperList(developers) { 395 | developers.forEach(developer => { 396 | const expectedSalary = developer.calculateExpectedSalary(); 397 | const experience = developer.getExperience(); 398 | const githubLink = developer.getGithubLink(); 399 | const data = { 400 | expectedSalary, 401 | experience, 402 | githubLink 403 | }; 404 | render(data); 405 | }); 406 | } 407 | function showManagerList(managers) { 408 | managers.forEach(manager => { 409 | const expectedSalary = manager.calculateExpectedSalary(); 410 | const experience = manager.getExperience(); 411 | const portfolio = manager.getMBAProjects(); 412 | const data = { 413 | expectedSalary, 414 | experience, 415 | portfolio 416 | }; 417 | render(data); 418 | }); 419 | } 420 | ``` 421 | 422 | **Bien :** 423 | 424 | ```javascript 425 | function showEmployeeList(employees) { 426 | employees.forEach(employee => { 427 | const expectedSalary = employee.calculateExpectedSalary(); 428 | const experience = employee.getExperience(); 429 | const data = { 430 | expectedSalary, 431 | experience 432 | }; 433 | switch (employee.type) { 434 | case "manager": 435 | data.portfolio = employee.getMBAProjects(); 436 | break; 437 | case "developer": 438 | data.githubLink = employee.getGithubLink(); 439 | break; 440 | } 441 | render(data); 442 | }); 443 | } 444 | ``` 445 | 446 | **[⬆ retour en haut](#sommaire)** 447 | 448 | ### Définir des objets par défaut avec Object.assign 449 | 450 | **Mauvais :** 451 | 452 | ```javascript 453 | const menuConfig = { 454 | title: null, 455 | body: "Bar", 456 | buttonText: null, 457 | cancellable: true 458 | }; 459 | function createMenu(config) { 460 | config.title = config.title || "Foo"; 461 | config.body = config.body || "Bar"; 462 | config.buttonText = config.buttonText || "Baz"; 463 | config.cancellable = 464 | config.cancellable !== undefined ? config.cancellable : true; 465 | } 466 | createMenu(menuConfig); 467 | ``` 468 | 469 | **Bien :** 470 | 471 | ```javascript 472 | const menuConfig = { 473 | title: "Order", 474 | // User n'inclut pas la clé 'body' 475 | buttonText: "Send", 476 | cancellable: true 477 | }; 478 | function createMenu(config) { 479 | let finalConfig = Object.assign( 480 | { 481 | title: "Foo", 482 | body: "Bar", 483 | buttonText: "Baz", 484 | cancellable: true 485 | }, 486 | config 487 | ); 488 | return finalConfig 489 | // config est maintenant égal à : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} 490 | // ... 491 | } 492 | createMenu(menuConfig); 493 | ``` 494 | 495 | **[⬆ retour en haut](#sommaire)** 496 | 497 | ### Ne pas utiliser de drapeaux comme paramètres de fonction 498 | 499 | Les drapeaux indiquent à votre utilisateur que cette fonction fait plus d'une chose. 500 | Les fonctions doivent faire une seule chose. 501 | Séparez vos fonctions si elles suivent des chemins de code différents basés sur un booléen. 502 | 503 | **Mauvais :** 504 | 505 | ```javascript 506 | function createFile(name, temp) { 507 | if (temp) { 508 | fs.create(`./temp/${name}`); 509 | } else { 510 | fs.create(name); 511 | } 512 | } 513 | ``` 514 | 515 | **Bien :** 516 | 517 | ```javascript 518 | function createFile(name) { 519 | fs.create(name); 520 | } 521 | function createTempFile(name) { 522 | createFile(`./temp/${name}`); 523 | } 524 | ``` 525 | 526 | **[⬆ retour en haut](#sommaire)** 527 | 528 | ### Évitez les effets de bord (partie 1) 529 | 530 | Une fonction produit un effet secondaire si elle fait autre chose que prendre une valeur en entrée et renvoyer une ou plusieurs autres valeurs. 531 | Un effet secondaire peut être l'écriture dans un fichier, la modification d'une variable globale ou le transfert accidentel de tout votre argent à un étranger. 532 | 533 | Il est parfois nécessaire d'avoir des effets secondaires dans un programme. 534 | Comme dans l'exemple précédent, vous pouvez avoir besoin d'écrire dans un fichier. Ce que vous voulez faire, c'est centraliser l'endroit où vous le faites. 535 | N'ayez pas plusieurs fonctions et classes qui écrivent dans un fichier particulier. 536 | N'ayez qu'un seul service qui s'en charge. Un et un seul. 537 | 538 | Le point principal est d'éviter les pièges communs comme le partage d'état entre objets sans aucune structure, l'utilisation de types de données mutables qui peuvent être écrits par n'importe qui, 539 | et ne pas centraliser l'endroit où se produisent les effets secondaires. 540 | Si vous y parvenez, vous serez plus heureux que la grande majorité des autres programmeurs. 541 | 542 | **Mauvais :** 543 | 544 | ```javascript 545 | // Variable globale référencée par la fonction suivante. 546 | // Si on avait une autre fonction qui utilisait ce nom, maintenant ce serait un tableau et ça pourrait le casser. 547 | let name = "Ryan McDermott"; 548 | function splitIntoFirstAndLastName() { 549 | name = name.split(" "); 550 | } 551 | splitIntoFirstAndLastName(); 552 | console.log(name); // ['Ryan', 'McDermott']; 553 | ``` 554 | 555 | **Bien :** 556 | 557 | ```javascript 558 | function splitIntoFirstAndLastName(name) { 559 | return name.split(" "); 560 | } 561 | const name = "Ryan McDermott"; 562 | const newName = splitIntoFirstAndLastName(name); 563 | console.log(name); // 'Ryan McDermott'; 564 | console.log(newName); // ['Ryan', 'McDermott']; 565 | ``` 566 | 567 | **[⬆ retour en haut](#sommaire)** 568 | 569 | ### Éviter les effets de bord (partie 2) 570 | 571 | En JavaScript, certaines valeurs sont immuables (immutable) et d'autres sont modifiables (mutable). 572 | Les objets et les tableaux sont deux types de valeurs mutables. Il est donc important de les manipuler avec précaution lorsqu'ils sont transmis comme paramètres à une fonction. 573 | Une fonction JavaScript peut modifier les propriétés d'un objet ou altérer le contenu d'un tableau, ce qui pourrait facilement provoquer des bogues ailleurs. 574 | 575 | Supposons qu'une fonction accepte un paramètre de tableau représentant un panier d'achat. 576 | Si la fonction modifie ce tableau - en ajoutant un article, par exemple -, toute autre fonction utilisant le même tableau `cart` sera affectée par cet ajout. Cela peut être une bonne chose, mais cela peut aussi être mauvais. 577 | 578 | Imaginons une mauvaise situation : 579 | 580 | L'utilisateur clique sur le bouton "Acheter", ce qui appelle une fonction `purchase` qui génère une requête réseau et envoie le tableau `cart` au serveur. 581 | En raison d'une mauvaise connexion réseau, la fonction `purchase` doit sans cesse réessayer la requête. 582 | Maintenant, que se passe-t-il si entre-temps l'utilisateur clique accidentellement sur le bouton "Ajouter au panier" d'un article qu'il ne veut pas vraiment avant que la requête réseau ne commence ? 583 | Si cela se produit et que la requête réseau commence, la fonction d'achat enverra l'article ajouté accidentellement parce que le tableau `cart` a été modifié. 584 | 585 | Une bonne solution serait que la fonction `addItemToCart` clone toujours le `cart`, le modifie et retourne le clone. 586 | Ainsi, les fonctions qui utilisent encore l'ancien panier ne seraient pas affectées par les modifications. 587 | 588 | Deux mises en garde à propos de cette approche : 589 | 590 | 1. Il peut y avoir des cas où vous souhaitez réellement modifier l'objet d'entrée, mais si vous adoptez cette pratique de programmation, vous constaterez que ces cas sont plutôt rares. La plupart des choses peuvent être remaniées pour n'avoir aucun effet secondaire ! 591 | 592 | 2. Le clonage de gros objets peut être très coûteux en termes de performances. Heureusement, ce n'est pas un gros problème dans la pratique car il existe d'excellentes [bibliothèques](https://facebook.github.io/immutable-js/) qui permettent à ce type d'approche de programmation d'être rapide et moins gourmande en mémoire que si vous cloniez manuellement des objets et des tableaux. 593 | 594 | **Mauvais :** 595 | 596 | ```javascript 597 | const addItemToCart = (cart, item) => { 598 | cart.push({ item, date: Date.now() }); 599 | }; 600 | ``` 601 | 602 | **Bien :** 603 | 604 | ```javascript 605 | const addItemToCart = (cart, item) => { 606 | return [...cart, { item, date: Date.now() }]; 607 | }; 608 | ``` 609 | 610 | **[⬆ retour en haut](#sommaire)** 611 | 612 | ### Ne pas écrire dans les fonctions globales 613 | 614 | Polluer les fonctions globales est une mauvaise pratique en JavaScript, car vous pourriez entrer en conflit avec une autre bibliothèque et l'utilisateur de votre API n'en saurait rien jusqu'à ce qu'il obtienne une exception en production. 615 | Prenons un exemple : que se passerait-il si vous vouliez étendre la méthode native Array de JavaScript pour avoir une méthode `diff` qui pourrait montrer la différence entre deux tableaux ? 616 | Vous pourriez écrire votre nouvelle fonction dans `Array.prototype`, mais elle pourrait entrer en conflit avec une autre bibliothèque qui essaye de faire la même chose. 617 | 618 | Et si cette autre bibliothèque utilisait simplement `diff` pour trouver la différence entre le premier et le dernier élément d'un tableau ? C'est pourquoi il serait bien mieux d'utiliser les classes ES2015/ES6 et d'étendre simplement le global `Array`. 619 | 620 | **Mauvais :** 621 | 622 | ```javascript 623 | Array.prototype.diff = function diff(comparisonArray) { 624 | const hash = new Set(comparisonArray); 625 | return this.filter(elem => !hash.has(elem)); 626 | }; 627 | ``` 628 | 629 | **Bien :** 630 | 631 | ```javascript 632 | class SuperArray extends Array { 633 | diff(comparisonArray) { 634 | const hash = new Set(comparisonArray); 635 | return this.filter(elem => !hash.has(elem)); 636 | } 637 | } 638 | ``` 639 | 640 | **[⬆ retour en haut](#sommaire)** 641 | 642 | ### Favoriser la programmation fonctionnelle plutôt que la programmation impérative 643 | 644 | JavaScript n'est pas un langage fonctionnel comme Haskell, mais il a une saveur fonctionnelle. Les langages fonctionnels peuvent être plus propres et plus faciles à tester. 645 | Favorisez ce style de programmation lorsque vous le pouvez. 646 | 647 | **Mauvais :** 648 | 649 | ```javascript 650 | const programmerOutput = [ 651 | { 652 | name: "Uncle Bobby", 653 | linesOfCode: 500 654 | }, 655 | { 656 | name: "Suzie Q", 657 | linesOfCode: 1500 658 | }, 659 | { 660 | name: "Jimmy Gosling", 661 | linesOfCode: 150 662 | }, 663 | { 664 | name: "Gracie Hopper", 665 | linesOfCode: 1000 666 | } 667 | ]; 668 | let totalOutput = 0; 669 | for (let i = 0; i < programmerOutput.length; i++) { 670 | totalOutput += programmerOutput[i].linesOfCode; 671 | } 672 | ``` 673 | 674 | **Bien :** 675 | 676 | ```javascript 677 | const programmerOutput = [ 678 | { 679 | name: "Uncle Bobby", 680 | linesOfCode: 500 681 | }, 682 | { 683 | name: "Suzie Q", 684 | linesOfCode: 1500 685 | }, 686 | { 687 | name: "Jimmy Gosling", 688 | linesOfCode: 150 689 | }, 690 | { 691 | name: "Gracie Hopper", 692 | linesOfCode: 1000 693 | } 694 | ]; 695 | const totalOutput = programmerOutput.reduce( 696 | (totalLines, output) => totalLines + output.linesOfCode, 697 | 0 698 | ); 699 | ``` 700 | 701 | **[⬆ retour en haut](#sommaire)** 702 | 703 | ### Encapsuler les conditions 704 | 705 | **Mauvais :** 706 | 707 | ```javascript 708 | if (fsm.state === "fetching" && isEmpty(listNode)) { 709 | // ... 710 | } 711 | ``` 712 | 713 | **Bien :** 714 | 715 | ```javascript 716 | function shouldShowSpinner(fsm, listNode) { 717 | return fsm.state === "fetching" && isEmpty(listNode); 718 | } 719 | if (shouldShowSpinner(fsmInstance, listNodeInstance)) { 720 | // ... 721 | } 722 | ``` 723 | 724 | **[⬆ retour en haut](#sommaire)** 725 | 726 | ### Évitez les conditions négatives 727 | 728 | **Mauvais :** 729 | 730 | ```javascript 731 | function isDOMNodeNotPresent(node) { 732 | // ... 733 | } 734 | if (!isDOMNodeNotPresent(node)) { 735 | // ... 736 | } 737 | ``` 738 | 739 | **Bien :** 740 | 741 | ```javascript 742 | function isDOMNodePresent(node) { 743 | // ... 744 | } 745 | if (isDOMNodePresent(node)) { 746 | // ... 747 | } 748 | ``` 749 | 750 | **[⬆ retour en haut](#sommaire)** 751 | 752 | ### Éviter les conditions 753 | 754 | Cela semble être une tâche impossible. En entendant cela pour la première fois, la plupart des gens disent : "comment suis-je censé faire quelque chose sans une instruction "if" ?". 755 | La réponse est que vous pouvez utiliser le polymorphisme pour réaliser la même tâche dans de nombreux cas. 756 | La deuxième question est généralement "c'est génial mais pourquoi voudrais-je faire ça ?". 757 | La réponse est un concept de __clean code__ que nous avons appris précédemment : une fonction ne doit faire qu'une seule chose. 758 | Lorsque vous avez des classes et des fonctions qui ont des instructions "if", vous dites à votre utilisateur que votre fonction fait plus d'une chose. 759 | Rappelez-vous, ne faites qu'une seule chose. 760 | 761 | **Mauvais :** 762 | 763 | ```javascript 764 | class Airplane { 765 | // ... 766 | getCruisingAltitude() { 767 | switch (this.type) { 768 | case "777": 769 | return this.getMaxAltitude() - this.getPassengerCount(); 770 | case "Air Force One": 771 | return this.getMaxAltitude(); 772 | case "Cessna": 773 | return this.getMaxAltitude() - this.getFuelExpenditure(); 774 | } 775 | } 776 | } 777 | ``` 778 | 779 | **Bien :** 780 | 781 | ```javascript 782 | class Airplane { 783 | // ... 784 | } 785 | class Boeing777 extends Airplane { 786 | // ... 787 | getCruisingAltitude() { 788 | return this.getMaxAltitude() - this.getPassengerCount(); 789 | } 790 | } 791 | class AirForceOne extends Airplane { 792 | // ... 793 | getCruisingAltitude() { 794 | return this.getMaxAltitude(); 795 | } 796 | } 797 | class Cessna extends Airplane { 798 | // ... 799 | getCruisingAltitude() { 800 | return this.getMaxAltitude() - this.getFuelExpenditure(); 801 | } 802 | } 803 | ``` 804 | 805 | **[⬆ retour en haut](#sommaire)** 806 | 807 | ### Évitez la vérification de type (partie 1) 808 | 809 | JavaScript n'est pas typé, ce qui signifie que vos fonctions peuvent prendre n'importe quel type d'argument. 810 | Parfois, vous êtes mordu par cette liberté et il devient tentant de faire de la vérification de type dans vos fonctions. 811 | Il existe de nombreuses façons d'éviter de devoir le faire. 812 | La première chose à considérer est la cohérence des API. 813 | 814 | **Mauvais :** 815 | 816 | ```javascript 817 | function travelToTexas(vehicle) { 818 | if (vehicle instanceof Bicycle) { 819 | vehicle.pedal(this.currentLocation, new Location("texas")); 820 | } else if (vehicle instanceof Car) { 821 | vehicle.drive(this.currentLocation, new Location("texas")); 822 | } 823 | } 824 | ``` 825 | 826 | **Bien :** 827 | 828 | ```javascript 829 | function travelToTexas(vehicle) { 830 | vehicle.move(this.currentLocation, new Location("texas")); 831 | } 832 | ``` 833 | 834 | **[⬆ retour en haut](#sommaire)** 835 | 836 | ### Évitez la vérification de type (partie 2) 837 | 838 | Si vous travaillez avec des valeurs primitives de base comme les chaînes de caractères et les entiers, et que vous ne pouvez pas utiliser le polymorphisme mais que vous ressentez toujours le besoin de vérifier le type, 839 | vous devriez envisager d'utiliser TypeScript. 840 | Il s'agit d'une excellente alternative au JavaScript normal, car il vous offre un typage statique en plus de la syntaxe JavaScript standard. 841 | Le problème de la vérification manuelle du type de JavaScript normal est que le faire correctement nécessite tellement de verbiage supplémentaire que la fausse "sécurité de type" que vous obtenez 842 | ne compense pas la perte de lisibilité. Gardez votre JavaScript propre, écrivez de bons tests et faites de bonnes revues de code. Sinon, faites tout cela mais avec TypeScript (qui, comme je l'ai dit, est une excellente alternative !). 843 | 844 | **Mauvais :** 845 | 846 | ```javascript 847 | function combine(val1, val2) { 848 | if ( 849 | (typeof val1 === "number" && typeof val2 === "number") || 850 | (typeof val1 === "string" && typeof val2 === "string") 851 | ) { 852 | return val1 + val2; 853 | } 854 | throw new Error("Must be of type String or Number"); 855 | } 856 | ``` 857 | 858 | **Bien :** 859 | 860 | ```javascript 861 | function combine(val1, val2) { 862 | return val1 + val2; 863 | } 864 | ``` 865 | 866 | **[⬆ retour en haut](#sommaire)** 867 | 868 | ### Ne pas suroptimiser 869 | 870 | Les navigateurs modernes effectuent beaucoup d'optimisation sous le capot au moment de l'exécution. 871 | Souvent, si vous optimisez, vous perdez votre temps. 872 | [Il existe de bonnes ressources](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers) pour voir où l'optimisation fait défaut. 873 | Ciblez-les entre-temps, jusqu'à ce qu'ils soient corrigés, si possible. 874 | 875 | **Mauvais :** 876 | ```javascript 877 | // Sur les anciens navigateurs, chaque itération avec `list.length` non mis en cache serait coûteuse 878 | // à cause du recalcul de `list.length`. 879 | // Dans les navigateurs modernes, ceci est optimisé. 880 | for (let i = 0, len = list.length; i < len; i++) { 881 | // ... 882 | } 883 | ``` 884 | 885 | **Bien :** 886 | 887 | ```javascript 888 | for (let i = 0; i < list.length; i++) { 889 | // ... 890 | } 891 | ``` 892 | 893 | **[⬆ retour en haut](#sommaire)** 894 | 895 | ### Supprimer le code mort 896 | 897 | Le code mort est tout aussi mauvais que le code dupliqué. 898 | Il n'y a aucune raison de le garder dans votre base de code. S'il n'est pas appelé, débarrassez-vous-en ! 899 | Il sera toujours en sécurité dans votre historique de versions si vous en avez encore besoin. 900 | 901 | **Mauvais :** 902 | 903 | ```javascript 904 | function oldRequestModule(url) { 905 | // ... 906 | } 907 | function newRequestModule(url) { 908 | // ... 909 | } 910 | const req = newRequestModule; 911 | inventoryTracker("apples", req, "www.inventory-awesome.io"); 912 | ``` 913 | 914 | **Bien :** 915 | 916 | ```javascript 917 | function newRequestModule(url) { 918 | // ... 919 | } 920 | const req = newRequestModule; 921 | inventoryTracker("apples", req, "www.inventory-awesome.io"); 922 | ``` 923 | 924 | **[⬆ retour en haut](#sommaire)** 925 | 926 | ## **Objets et structures de données** 927 | 928 | ### Utiliser les getters et setters 929 | 930 | L'utilisation de getters et setters pour accéder aux données des objets peut s'avérer plus efficace que la simple recherche d'une propriété sur un objet. 931 | Vous vous demandez peut-être pourquoi ? 932 | 933 | Eh bien, voici une liste non organisée de raisons : 934 | 935 | - Lorsque vous voulez faire plus que d'obtenir une propriété d'un objet, vous n'avez pas besoin de rechercher et de modifier chaque accesseur dans votre codebase. 936 | - Rend l'ajout de la validation simple quand on fait un `set`. 937 | - Encapsule la représentation interne. 938 | - Il est facile d'ajouter la journalisation et la gestion des erreurs lors de l'obtention et de la définition. 939 | - Vous pouvez charger paresseusement (lazy loading) les propriétés de votre objet, par exemple en le récupérant depuis un serveur. 940 | 941 | **Mauvais :** 942 | 943 | ```javascript 944 | function makeBankAccount() { 945 | // ... 946 | return { 947 | balance: 0 948 | // ... 949 | }; 950 | } 951 | const account = makeBankAccount(); 952 | account.balance = 100; 953 | ``` 954 | 955 | **Bien :** 956 | 957 | ```javascript 958 | function makeBankAccount() { 959 | // celle-ci est privée 960 | let balance = 0 ; 961 | // un "getter", rendu public via l'objet retourné ci-dessous 962 | function getBalance() { 963 | return balance; 964 | } 965 | // un "setter", rendu public via l'objet retourné ci-dessous 966 | function setBalance(amount) { 967 | // ... valider avant de mettre à jour le solde 968 | balance = amount; 969 | } 970 | return { 971 | // ... 972 | getBalance, 973 | setBalance 974 | } ; 975 | } 976 | const account = makeBankAccount(); 977 | account.setBalance(100); 978 | ``` 979 | 980 | **[⬆ retour en haut](#sommaire)** 981 | 982 | ### Faites en sorte que les objets aient des membres privés 983 | 984 | Cela peut être réalisé par le biais de __closures__ (pour ES5 et inférieur). 985 | 986 | **Mauvais :** 987 | 988 | ```javascript 989 | const Employee = function(name) { 990 | this.name = name; 991 | }; 992 | Employee.prototype.getName = function getName() { 993 | return this.name; 994 | }; 995 | const employee = new Employee("John Doe"); 996 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe 997 | delete employee.name; 998 | console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined 999 | ``` 1000 | 1001 | **Bien :** 1002 | 1003 | ```javascript 1004 | function makeEmployee(name) { 1005 | return { 1006 | getName() { 1007 | return name; 1008 | } 1009 | }; 1010 | } 1011 | const employee = makeEmployee("John Doe"); 1012 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe 1013 | delete employee.name; 1014 | console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe 1015 | ``` 1016 | 1017 | **[⬆ retour en haut](#sommaire)** 1018 | 1019 | ## **Classes** 1020 | 1021 | ### Préférer les classes ES2015/ES6 aux fonctions simples ES5 1022 | 1023 | Il est très difficile d'obtenir un héritage de classe, une construction et des définitions de méthodes lisibles pour les classes ES5 classiques. 1024 | Si vous avez besoin de l'héritage (et sachez que vous pourriez ne pas en avoir besoin), alors préférez les classes ES2015/ES6. 1025 | Cependant, préférez les petites fonctions aux classes jusqu'à ce que vous ayez besoin d'objets plus grands et plus complexes. 1026 | 1027 | **Mauvais :** 1028 | 1029 | ```javascript 1030 | const Animal = function(age) { 1031 | if (!(this instanceof Animal)) { 1032 | throw new Error("Instantiate Animal with `new`"); 1033 | } 1034 | this.age = age; 1035 | }; 1036 | Animal.prototype.move = function move() {}; 1037 | const Mammal = function(age, furColor) { 1038 | if (!(this instanceof Mammal)) { 1039 | throw new Error("Instantiate Mammal with `new`"); 1040 | } 1041 | Animal.call(this, age); 1042 | this.furColor = furColor; 1043 | }; 1044 | Mammal.prototype = Object.create(Animal.prototype); 1045 | Mammal.prototype.constructor = Mammal; 1046 | Mammal.prototype.liveBirth = function liveBirth() {}; 1047 | const Human = function(age, furColor, languageSpoken) { 1048 | if (!(this instanceof Human)) { 1049 | throw new Error("Instantiate Human with `new`"); 1050 | } 1051 | Mammal.call(this, age, furColor); 1052 | this.languageSpoken = languageSpoken; 1053 | }; 1054 | Human.prototype = Object.create(Mammal.prototype); 1055 | Human.prototype.constructor = Human; 1056 | Human.prototype.speak = function speak() {}; 1057 | ``` 1058 | 1059 | **Bien :** 1060 | 1061 | ```javascript 1062 | class Animal { 1063 | constructor(age) { 1064 | this.age = age; 1065 | } 1066 | move() { 1067 | /* ... */ 1068 | } 1069 | } 1070 | class Mammal extends Animal { 1071 | constructor(age, furColor) { 1072 | super(age); 1073 | this.furColor = furColor; 1074 | } 1075 | liveBirth() { 1076 | /* ... */ 1077 | } 1078 | } 1079 | class Human extends Mammal { 1080 | constructor(age, furColor, languageSpoken) { 1081 | super(age, furColor); 1082 | this.languageSpoken = languageSpoken; 1083 | } 1084 | speak() { 1085 | /* ... */ 1086 | } 1087 | } 1088 | ``` 1089 | 1090 | **[⬆ retour en haut](#sommaire)** 1091 | 1092 | ### Utiliser le chaînage de méthodes 1093 | 1094 | Ce modèle est très utile en JavaScript et vous le voyez dans de nombreuses bibliothèques telles que jQuery et Lodash. 1095 | Il permet à votre code d'être explicite et moins verbeux. 1096 | Pour cette raison, je vous conseille d'utiliser le chaînage de méthodes et regardez comment votre code sera propre. 1097 | Dans vos fonctions de classe, retournez simplement `this` à la fin de chaque fonction, et vous pouvez enchaîner d'autres méthodes de classe sur celle-ci. 1098 | 1099 | **Mauvais :** 1100 | 1101 | ```javascript 1102 | class Car { 1103 | constructor(make, model, color) { 1104 | this.make = make; 1105 | this.model = model; 1106 | this.color = color; 1107 | } 1108 | setMake(make) { 1109 | this.make = make; 1110 | } 1111 | setModel(model) { 1112 | this.model = model; 1113 | } 1114 | setColor(color) { 1115 | this.color = color; 1116 | } 1117 | save() { 1118 | console.log(this.make, this.model, this.color); 1119 | } 1120 | } 1121 | const car = new Car("Ford", "F-150", "red"); 1122 | car.setColor("pink"); 1123 | car.save(); 1124 | ``` 1125 | 1126 | **Bien :** 1127 | 1128 | ```javascript 1129 | class Car { 1130 | constructor(make, model, color) { 1131 | this.make = make; 1132 | this.model = model; 1133 | this.color = color; 1134 | } 1135 | setMake(make) { 1136 | this.make = make; 1137 | // NOTE: Renvoyer 'this' pour le chaînage 1138 | return this; 1139 | } 1140 | setModel(model) { 1141 | this.model = model; 1142 | // NOTE: Renvoyer 'this' pour le chaînage 1143 | return this; 1144 | } 1145 | setColor(color) { 1146 | this.color = color; 1147 | // NOTE: Renvoyer 'this' pour le chaînage 1148 | return this; 1149 | } 1150 | save() { 1151 | console.log(this.make, this.model, this.color); 1152 | // NOTE: Renvoyer 'this' pour le chaînage 1153 | return this; 1154 | } 1155 | } 1156 | const car = new Car("Ford", "F-150", "red").setColor("pink").save(); 1157 | ``` 1158 | 1159 | **[⬆ retour en haut](#sommaire)** 1160 | 1161 | ### Préférez la composition à l'héritage 1162 | 1163 | Comme l'indique le célèbre Gang of Four dans [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns), vous devez préférer la composition à l'héritage lorsque vous le pouvez. 1164 | Il y a beaucoup de bonnes raisons d'utiliser l'héritage et beaucoup de bonnes raisons d'utiliser la composition. 1165 | Le point principal de cette maxime est que si votre esprit se dirige instinctivement vers l'héritage, demandez-vous si la composition pourrait mieux modéliser votre problème. Dans certains cas, c'est possible. 1166 | 1167 | Vous vous demandez peut-être alors : "Quand dois-je utiliser l'héritage ?" Cela dépend de votre problème à portée de main, mais voici une liste décente des cas où l'héritage a plus de sens que la composition : 1168 | 1169 | 1. Votre héritage représente une relation "est-un" et non une relation "a-un" (Human->Animal vs. User->UserDetails). 1170 | 2. Vous pouvez réutiliser le code des classes de base (les humains peuvent se déplacer comme tous les animaux). 1171 | 3. Vous voulez faire des changements globaux aux classes dérivées en changeant une classe de base. (Modifier la dépense calorique de tous les animaux lorsqu'ils se déplacent). 1172 | 1173 | **Mauvais :** 1174 | 1175 | ```javascript 1176 | class Employee { 1177 | constructor(name, email) { 1178 | this.name = name; 1179 | this.email = email; 1180 | } 1181 | // ... 1182 | } 1183 | // Mauvais car les employés "ont" des données fiscales. EmployeeTaxData n'est pas un type de Employee 1184 | class EmployeeTaxData extends Employee { 1185 | constructor(ssn, salary) { 1186 | super(); 1187 | this.ssn = ssn; 1188 | this.salary = salary; 1189 | } 1190 | // ... 1191 | } 1192 | ``` 1193 | 1194 | **Bien :** 1195 | 1196 | ```javascript 1197 | class EmployeeTaxData { 1198 | constructor(ssn, salary) { 1199 | this.ssn = ssn; 1200 | this.salary = salary; 1201 | } 1202 | // ... 1203 | } 1204 | class Employee { 1205 | constructor(name, email) { 1206 | this.name = name; 1207 | this.email = email; 1208 | } 1209 | setTaxData(ssn, salary) { 1210 | this.taxData = new EmployeeTaxData(ssn, salary); 1211 | } 1212 | // ... 1213 | } 1214 | ``` 1215 | 1216 | **[⬆ retour en haut](#sommaire)** 1217 | 1218 | ## **SOLID** 1219 | 1220 | ### Principe de responsabilité unique (SRP) 1221 | 1222 | Comme indiqué dans Clean Code, "Il ne devrait jamais y avoir plus d'une raison pour qu'une classe soit modifiée". 1223 | Il est tentant de bourrer une classe d'un grand nombre de fonctionnalités, comme lorsque vous ne pouvez prendre qu'une seule valise sur votre vol en avion. 1224 | Le problème est que votre classe ne sera pas conceptuellement cohérente et qu'elle aura de nombreuses raisons de changer. 1225 | Il est important de minimiser le nombre de fois où vous devez changer une classe. 1226 | C'est important parce que si trop de fonctionnalités se trouvent dans une classe et que vous en modifiez une partie, il peut être difficile de comprendre comment cela affectera les autres modules dépendants de votre codebase. 1227 | 1228 | **Mauvais :** 1229 | 1230 | ```javascript 1231 | class UserSettings { 1232 | constructor(user) { 1233 | this.user = user; 1234 | } 1235 | changeSettings(settings) { 1236 | if (this.verifyCredentials()) { 1237 | // ... 1238 | } 1239 | } 1240 | verifyCredentials() { 1241 | // ... 1242 | } 1243 | } 1244 | ``` 1245 | 1246 | **Bien :** 1247 | 1248 | ```javascript 1249 | class UserAuth { 1250 | constructor(user) { 1251 | this.user = user; 1252 | } 1253 | verifyCredentials() { 1254 | // ... 1255 | } 1256 | } 1257 | class UserSettings { 1258 | constructor(user) { 1259 | this.user = user; 1260 | this.auth = new UserAuth(user); 1261 | } 1262 | changeSettings(settings) { 1263 | if (this.auth.verifyCredentials()) { 1264 | // ... 1265 | } 1266 | } 1267 | } 1268 | ``` 1269 | 1270 | **[⬆ retour en haut](#sommaire)** 1271 | 1272 | ### Principe ouvert/fermé (OCP) 1273 | 1274 | Comme le dit Bertrand Meyer, "les entités logicielles (classes, modules, fonctions, etc.) doivent être ouvertes à l'extension, mais fermées à la modification." 1275 | Mais qu'est-ce que cela signifie ? 1276 | Ce principe stipule essentiellement que vous devez permettre aux utilisateurs d'ajouter de nouvelles fonctionnalités sans modifier le code existant. 1277 | 1278 | **Mauvais :** 1279 | 1280 | ```javascript 1281 | class AjaxAdapter extends Adapter { 1282 | constructor() { 1283 | super(); 1284 | this.name = "ajaxAdapter"; 1285 | } 1286 | } 1287 | class NodeAdapter extends Adapter { 1288 | constructor() { 1289 | super(); 1290 | this.name = "nodeAdapter"; 1291 | } 1292 | } 1293 | class HttpRequester { 1294 | constructor(adapter) { 1295 | this.adapter = adapter; 1296 | } 1297 | fetch(url) { 1298 | if (this.adapter.name === "ajaxAdapter") { 1299 | return makeAjaxCall(url).then(response => { 1300 | // transform response and return 1301 | }); 1302 | } else if (this.adapter.name === "nodeAdapter") { 1303 | return makeHttpCall(url).then(response => { 1304 | // transform response and return 1305 | }); 1306 | } 1307 | } 1308 | } 1309 | function makeAjaxCall(url) { 1310 | // request and return promise 1311 | } 1312 | function makeHttpCall(url) { 1313 | // request and return promise 1314 | } 1315 | ``` 1316 | 1317 | **Bien :** 1318 | 1319 | ```javascript 1320 | class AjaxAdapter extends Adapter { 1321 | constructor() { 1322 | super(); 1323 | this.name = "ajaxAdapter"; 1324 | } 1325 | request(url) { 1326 | // request and return promise 1327 | } 1328 | } 1329 | class NodeAdapter extends Adapter { 1330 | constructor() { 1331 | super(); 1332 | this.name = "nodeAdapter"; 1333 | } 1334 | request(url) { 1335 | // request and return promise 1336 | } 1337 | } 1338 | class HttpRequester { 1339 | constructor(adapter) { 1340 | this.adapter = adapter; 1341 | } 1342 | fetch(url) { 1343 | return this.adapter.request(url).then(response => { 1344 | // transform response and return 1345 | }); 1346 | } 1347 | } 1348 | ``` 1349 | 1350 | **[⬆ retour en haut](#sommaire)** 1351 | 1352 | ### Principe de substitution de Liskov (LSP) 1353 | 1354 | Il s'agit d'un terme effrayant pour un concept très simple. 1355 | Il est formellement défini comme suit : "Si S est un sous-type de T, alors les objets de type T peuvent être remplacés par des objets de type S sans altérer aucune des propriétés désirées de ce programme (correction, tâche exécutée, etc.)". 1356 | C'est une définition encore plus effrayante. 1357 | 1358 | La meilleure explication pour cela est que si vous avez une classe parent et une classe enfant, alors la classe de base et la classe enfant peuvent être utilisées de manière interchangeable sans obtenir de résultats incorrects. Cela peut encore prêter à confusion, alors examinons l'exemple classique du carré et du rectangle. 1359 | 1360 | Mathématiquement, un carré est un rectangle, mais si vous le modélisez en utilisant la relation "est-un" via l'héritage, vous aurez rapidement des problèmes. 1361 | 1362 | **Mauvais :** 1363 | 1364 | ```javascript 1365 | class Rectangle { 1366 | constructor() { 1367 | this.width = 0; 1368 | this.height = 0; 1369 | } 1370 | setColor(color) { 1371 | // ... 1372 | } 1373 | render(area) { 1374 | // ... 1375 | } 1376 | setWidth(width) { 1377 | this.width = width; 1378 | } 1379 | setHeight(height) { 1380 | this.height = height; 1381 | } 1382 | getArea() { 1383 | return this.width * this.height; 1384 | } 1385 | } 1386 | class Square extends Rectangle { 1387 | setWidth(width) { 1388 | this.width = width; 1389 | this.height = width; 1390 | } 1391 | setHeight(height) { 1392 | this.width = height; 1393 | this.height = height; 1394 | } 1395 | } 1396 | function renderLargeRectangles(rectangles) { 1397 | rectangles.forEach(rectangle => { 1398 | rectangle.setWidth(4); 1399 | rectangle.setHeight(5); 1400 | const area = rectangle.getArea(); // Mauvais: Retourne 25 pour Square. Devrait retourner 20. 1401 | rectangle.render(area); 1402 | }); 1403 | } 1404 | const rectangles = [new Rectangle(), new Rectangle(), new Square()]; 1405 | renderLargeRectangles(rectangles); 1406 | ``` 1407 | 1408 | **Bien :** 1409 | 1410 | ```javascript 1411 | class Shape { 1412 | setColor(color) { 1413 | // ... 1414 | } 1415 | render(area) { 1416 | // ... 1417 | } 1418 | } 1419 | class Rectangle extends Shape { 1420 | constructor(width, height) { 1421 | super(); 1422 | this.width = width; 1423 | this.height = height; 1424 | } 1425 | getArea() { 1426 | return this.width * this.height; 1427 | } 1428 | } 1429 | class Square extends Shape { 1430 | constructor(length) { 1431 | super(); 1432 | this.length = length; 1433 | } 1434 | getArea() { 1435 | return this.length * this.length; 1436 | } 1437 | } 1438 | function renderLargeShapes(shapes) { 1439 | shapes.forEach(shape => { 1440 | const area = shape.getArea(); 1441 | shape.render(area); 1442 | }); 1443 | } 1444 | const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; 1445 | renderLargeShapes(shapes); 1446 | ``` 1447 | 1448 | **[⬆ retour en haut](#sommaire)** 1449 | 1450 | ### Principe de ségrégation des interfaces (ISP) 1451 | 1452 | JavaScript n'ayant pas d'interfaces, ce principe ne s'applique pas aussi strictement que les autres. 1453 | Cependant, il est important et pertinent même avec l'absence de système de types en JavaScript. 1454 | 1455 | L'ISP stipule que "les clients ne doivent pas être forcés de dépendre d'interfaces qu'ils n'utilisent pas". 1456 | Les interfaces sont des contrats implicites en JavaScript en raison de l'absence de typage. 1457 | 1458 | Un bon exemple à regarder qui démontre ce principe en JavaScript est pour les classes qui nécessitent de grands objets de paramétrage. 1459 | Il est avantageux de ne pas demander aux clients de configurer un grand nombre d'options, car la plupart du temps, ils n'auront pas besoin de tous les paramètres. 1460 | Les rendre optionnels permet d'éviter d'avoir une "grosse interface". 1461 | 1462 | **Mauvais :** 1463 | 1464 | ```javascript 1465 | class DOMTraverser { 1466 | constructor(settings) { 1467 | this.settings = settings; 1468 | this.setup(); 1469 | } 1470 | setup() { 1471 | this.rootNode = this.settings.rootNode; 1472 | this.settings.animationModule.setup(); 1473 | } 1474 | traverse() { 1475 | // ... 1476 | } 1477 | } 1478 | const $ = new DOMTraverser({ 1479 | rootNode: document.getElementsByTagName("body"), 1480 | animationModule() {} // La plupart du temps, nous n'aurons pas besoin d'animer lors de la traversée. 1481 | // ... 1482 | }); 1483 | ``` 1484 | 1485 | **Bien :** 1486 | 1487 | ```javascript 1488 | class DOMTraverser { 1489 | constructor(settings) { 1490 | this.settings = settings; 1491 | this.options = settings.options; 1492 | this.setup(); 1493 | } 1494 | setup() { 1495 | this.rootNode = this.settings.rootNode; 1496 | this.setupOptions(); 1497 | } 1498 | setupOptions() { 1499 | if (this.options.animationModule) { 1500 | // ... 1501 | } 1502 | } 1503 | traverse() { 1504 | // ... 1505 | } 1506 | } 1507 | const $ = new DOMTraverser({ 1508 | rootNode: document.getElementsByTagName("body"), 1509 | options: { 1510 | animationModule() {} 1511 | } 1512 | }); 1513 | ``` 1514 | 1515 | **[⬆ retour en haut](#sommaire)** 1516 | 1517 | ### Principe d'inversion des dépendances (DIP) 1518 | 1519 | Ce principe énonce deux choses essentielles : 1520 | 1521 | 1. Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d'abstractions. 1522 | 2. Les abstractions ne doivent pas dépendre des détails. Les détails doivent dépendre des abstractions. 1523 | 1524 | Cela peut être difficile à comprendre au début, mais si vous avez travaillé avec AngularJS, vous avez vu une mise en œuvre de ce principe sous la forme de l'injection de dépendances (DI). 1525 | Bien qu'il ne s'agisse pas de concepts identiques, l'injection de dépendance empêche les modules de haut niveau de connaître les détails de ses modules de bas niveau et de les mettre en place. 1526 | Il peut y parvenir grâce à l'injection de dépendance. L'un des grands avantages de cette méthode est qu'elle réduit le couplage entre les modules. 1527 | Le couplage est un très mauvais modèle de développement car il rend votre code difficile à remanier. 1528 | 1529 | Comme indiqué précédemment, JavaScript n'a pas d'interfaces et les abstractions dont on dépend sont des contrats implicites. 1530 | C'est-à-dire les méthodes et les propriétés qu'un objet/classe expose à un autre objet/classe. 1531 | Dans l'exemple ci-dessous, le contrat implicite est que tout module de requête pour un `InventoryTracker` aura une méthode `requestItems`. 1532 | 1533 | **Mauvais :** 1534 | 1535 | ```javascript 1536 | class InventoryRequester { 1537 | constructor() { 1538 | this.REQ_METHODS = ["HTTP"]; 1539 | } 1540 | requestItem(item) { 1541 | // ... 1542 | } 1543 | } 1544 | class InventoryTracker { 1545 | constructor(items) { 1546 | this.items = items; 1547 | // MAUVAIS : Nous avons créé une dépendance sur une implémentation de requête spécifique. 1548 | // Nous devrions simplement faire dépendre les requestItems d'une méthode de requête : `request`. 1549 | this.requester = new InventoryRequester(); 1550 | } 1551 | requestItems() { 1552 | this.items.forEach(item => { 1553 | this.requester.requestItem(item); 1554 | }); 1555 | } 1556 | } 1557 | const inventoryTracker = new InventoryTracker(["apples", "bananas"]); 1558 | inventoryTracker.requestItems(); 1559 | ``` 1560 | 1561 | **Bien :** 1562 | 1563 | ```javascript 1564 | class InventoryTracker { 1565 | constructor(items, requester) { 1566 | this.items = items; 1567 | this.requester = requester; 1568 | } 1569 | requestItems() { 1570 | this.items.forEach(item => { 1571 | this.requester.requestItem(item); 1572 | }); 1573 | } 1574 | } 1575 | class InventoryRequesterV1 { 1576 | constructor() { 1577 | this.REQ_METHODS = ["HTTP"]; 1578 | } 1579 | requestItem(item) { 1580 | // ... 1581 | } 1582 | } 1583 | class InventoryRequesterV2 { 1584 | constructor() { 1585 | this.REQ_METHODS = ["WS"]; 1586 | } 1587 | requestItem(item) { 1588 | // ... 1589 | } 1590 | } 1591 | // En construisant nos dépendances en externe et en les injectant, nous pouvons facilement 1592 | // remplacer notre module de requête par un nouveau module fantaisiste qui utilise les WebSockets. 1593 | const inventoryTracker = new InventoryTracker( 1594 | ["apples", "bananas"], 1595 | new InventoryRequesterV2() 1596 | ); 1597 | inventoryTracker.requestItems(); 1598 | ``` 1599 | 1600 | **[⬆ retour en haut](#sommaire)** 1601 | 1602 | ## **Tests** 1603 | 1604 | Les tests sont plus importants que la livraison. 1605 | Si vous n'avez pas de tests ou si vous en avez une quantité insuffisante, à chaque fois que vous livrerez du code, vous ne serez jamais sûr de n'avoir rien cassé. 1606 | C'est à votre équipe de décider ce qui constitue une quantité adéquate, mais une couverture à 100% (toutes les déclarations et branches) est le moyen d'obtenir une confiance très élevée et la tranquillité des développeurs. 1607 | Cela signifie qu'en plus de disposer d'un excellent cadre de test, vous devez également utiliser un [bon outil de couverture](https://gotwarlost.github.io/istanbul/). 1608 | 1609 | Il n'y a aucune excuse pour ne pas écrire de tests. Il existe [une multitude de bons cadres de test JS](https://jstherightway.org/#testing-tools), trouvez-en un que votre équipe préfère. 1610 | Lorsque vous aurez trouvé celui qui convient à votre équipe, efforcez-vous de toujours écrire des tests pour chaque nouvelle fonctionnalité/module que vous introduisez. 1611 | Si votre méthode préférée est le développement piloté par les tests (TDD), c'est parfait, mais l'essentiel est de vous assurer que vous atteignez vos objectifs de couverture avant de lancer une fonctionnalité ou de remanier une fonctionnalité existante. 1612 | 1613 | ### Un seul concept par test 1614 | 1615 | **Mauvais :** 1616 | 1617 | ```javascript 1618 | import assert from "assert"; 1619 | describe("MomentJS", () => { 1620 | it("handles date boundaries", () => { 1621 | let date; 1622 | date = new MomentJS("1/1/2015"); 1623 | date.addDays(30); 1624 | assert.equal("1/31/2015", date); 1625 | date = new MomentJS("2/1/2016"); 1626 | date.addDays(28); 1627 | assert.equal("02/29/2016", date); 1628 | date = new MomentJS("2/1/2015"); 1629 | date.addDays(28); 1630 | assert.equal("03/01/2015", date); 1631 | }); 1632 | }); 1633 | ``` 1634 | 1635 | **Bien :** 1636 | 1637 | ```javascript 1638 | import assert from "assert"; 1639 | describe("MomentJS", () => { 1640 | it("handles 30-day months", () => { 1641 | const date = new MomentJS("1/1/2015"); 1642 | date.addDays(30); 1643 | assert.equal("1/31/2015", date); 1644 | }); 1645 | it("handles leap year", () => { 1646 | const date = new MomentJS("2/1/2016"); 1647 | date.addDays(28); 1648 | assert.equal("02/29/2016", date); 1649 | }); 1650 | it("handles non-leap year", () => { 1651 | const date = new MomentJS("2/1/2015"); 1652 | date.addDays(28); 1653 | assert.equal("03/01/2015", date); 1654 | }); 1655 | }); 1656 | ``` 1657 | 1658 | **[⬆ retour en haut](#sommaire)** 1659 | 1660 | ## **Concurrence** 1661 | 1662 | ### Utiliser des promesses, pas des callbacks 1663 | 1664 | Les callbacks ne sont pas propres, et ils provoquent des quantités excessives d'imbrication. Avec ES2015/ES6, 1665 | les promesses sont un type global intégré. Utilisez-les ! 1666 | 1667 | **Mauvais :** 1668 | 1669 | ```javascript 1670 | import { get } from "request"; 1671 | import { writeFile } from "fs"; 1672 | get( 1673 | "https://en.wikipedia.org/wiki/Robert_Cecil_Martin", 1674 | (requestErr, response, body) => { 1675 | if (requestErr) { 1676 | console.error(requestErr); 1677 | } else { 1678 | writeFile("article.html", body, writeErr => { 1679 | if (writeErr) { 1680 | console.error(writeErr); 1681 | } else { 1682 | console.log("File written"); 1683 | } 1684 | }); 1685 | } 1686 | } 1687 | ); 1688 | ``` 1689 | 1690 | **Bien :** 1691 | 1692 | ```javascript 1693 | import { get } from "request-promise"; 1694 | import { writeFile } from "fs-extra"; 1695 | get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") 1696 | .then(body => { 1697 | return writeFile("article.html", body); 1698 | }) 1699 | .then(() => { 1700 | console.log("File written"); 1701 | }) 1702 | .catch(err => { 1703 | console.error(err); 1704 | }); 1705 | ``` 1706 | 1707 | **[⬆ retour en haut](#sommaire)** 1708 | 1709 | ### Async/Await sont encore plus propres que les promesses 1710 | 1711 | Les promesses sont une alternative très propre aux callbacks, mais ES2017/ES8 apporte async et await qui offrent une solution encore plus propre. 1712 | Tout ce dont vous avez besoin est une fonction qui est préfixée par un mot-clé `async`, et ensuite vous pouvez écrire votre logique de manière impérative sans 1713 | une chaîne de fonctions `then`. Utilisez ceci si vous pouvez profiter des fonctionnalités de ES2017/ES8 dès aujourd'hui ! 1714 | 1715 | **Mauvais :** 1716 | 1717 | ```javascript 1718 | import { get } from "request-promise"; 1719 | import { writeFile } from "fs-extra"; 1720 | get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin") 1721 | .then(body => { 1722 | return writeFile("article.html", body); 1723 | }) 1724 | .then(() => { 1725 | console.log("File written"); 1726 | }) 1727 | .catch(err => { 1728 | console.error(err); 1729 | }); 1730 | ``` 1731 | 1732 | **Bien :** 1733 | 1734 | ```javascript 1735 | import { get } from "request-promise"; 1736 | import { writeFile } from "fs-extra"; 1737 | async function getCleanCodeArticle() { 1738 | try { 1739 | const body = await get( 1740 | "https://en.wikipedia.org/wiki/Robert_Cecil_Martin" 1741 | ); 1742 | await writeFile("article.html", body); 1743 | console.log("File written"); 1744 | } catch (err) { 1745 | console.error(err); 1746 | } 1747 | } 1748 | getCleanCodeArticle() 1749 | ``` 1750 | 1751 | **[⬆ retour en haut](#sommaire)** 1752 | 1753 | ## **Gestion des erreurs** 1754 | 1755 | Les erreurs capturées sont une bonne chose ! 1756 | Elles signifient que le runtime a réussi à identifier le moment où quelque chose dans votre programme a mal tourné et il vous le fait savoir en arrêtant l'exécution de la fonction sur la pile actuelle, en tuant le processus (dans Node), et en vous notifiant dans la console avec une trace de pile (stack trace). 1757 | 1758 | ### N'ignorez pas les erreurs détectées 1759 | 1760 | Ne rien faire face à une erreur détectée ne vous donne pas la possibilité de la corriger ou de réagir à cette erreur. Enregistrer l'erreur dans la console (`console.log`) n'est pas beaucoup mieux car souvent elle peut se perdre dans un océan d'affichages par la console. 1761 | Si vous enveloppez un bout de code dans un `try/catch`, cela signifie que vous pensez qu'une erreur peut se produire à cet endroit et que vous devez donc avoir un plan, ou créer un chemin de code, pour le cas où elle se produirait. 1762 | 1763 | **Mauvais :** 1764 | 1765 | ```javascript 1766 | try { 1767 | functionThatMightThrow(); 1768 | } catch (error) { 1769 | console.log(error); 1770 | } 1771 | ``` 1772 | 1773 | **Bien :** 1774 | 1775 | ```javascript 1776 | try { 1777 | functionThatMightThrow(); 1778 | } catch (error) { 1779 | // Une option (plus percutant que console.log) : 1780 | console.error(error) ; 1781 | // Une autre option : 1782 | notifyUserOfError(error) ; 1783 | // Une autre option : 1784 | reportErrorToService(error) ; 1785 | // OU faites les trois ! 1786 | } 1787 | ``` 1788 | 1789 | ### N'ignorez pas les promesses rejetées 1790 | 1791 | Pour la même raison, vous ne devez pas ignorer les erreurs attrapées de `try/catch`. 1792 | 1793 | **Mauvais :** 1794 | 1795 | ```javascript 1796 | getdata() 1797 | .then(data => { 1798 | functionThatMightThrow(data); 1799 | }) 1800 | .catch(error => { 1801 | console.log(error); 1802 | }); 1803 | ``` 1804 | 1805 | **Bien :** 1806 | 1807 | ```javascript 1808 | getdata() 1809 | .then(data => { 1810 | functionThatMightThrow(data); 1811 | }) 1812 | .catch(error => { 1813 | // Une option (plus percutant que console.log) : 1814 | console.error(error) ; 1815 | // Une autre option : 1816 | notifyUserOfError(error) ; 1817 | // Une autre option : 1818 | reportErrorToService(error) ; 1819 | // OU faites les trois ! 1820 | }); 1821 | ``` 1822 | 1823 | **[⬆ retour en haut](#sommaire)** 1824 | 1825 | ## **Formatage** 1826 | 1827 | Le formatage est subjectif. Comme beaucoup de règles dans le présent document, il n'y a pas de règle absolue que vous devez suivre. 1828 | L'essentiel est de NE PAS SE BATTRE sur le formatage. 1829 | Il existe [des tonnes d'outils](https://standardjs.com/rules.html) pour automatiser ce processus. 1830 | Utilisez-en un ! C'est une perte de temps et d'argent pour les ingénieurs que de se disputer sur le formatage. 1831 | 1832 | Pour les choses qui ne relèvent pas du formatage automatique (indentation, tabulations ou espaces, guillemets doubles ou simples, etc.), voici quelques conseils. 1833 | 1834 | ### Utiliser des majuscules cohérentes 1835 | 1836 | JavaScript n'étant pas typé, les majuscules en disent long sur vos variables, fonctions, etc. Ces règles sont subjectives, donc votre équipe peut choisir ce qu'elle veut. 1837 | Le fait est que, peu importe ce que vous choisissez, soyez cohérent. 1838 | 1839 | **Mauvais :** 1840 | 1841 | ```javascript 1842 | const DAYS_IN_WEEK = 7; 1843 | const daysInMonth = 30; 1844 | const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"]; 1845 | const Artists = ["ACDC", "Led Zeppelin", "The Beatles"]; 1846 | function eraseDatabase() {} 1847 | function restore_database() {} 1848 | class animal {} 1849 | class Alpaca {} 1850 | ``` 1851 | 1852 | **Bien :** 1853 | 1854 | ```javascript 1855 | const DAYS_IN_WEEK = 7; 1856 | const DAYS_IN_MONTH = 30; 1857 | const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"]; 1858 | const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"]; 1859 | function eraseDatabase() {} 1860 | function restoreDatabase() {} 1861 | class Animal {} 1862 | class Alpaca {} 1863 | ``` 1864 | 1865 | **[⬆ retour en haut](#sommaire)** 1866 | 1867 | ### Les fonctions appelantes et appelées doivent être proches 1868 | 1869 | Si une fonction en appelle une autre, gardez ces fonctions verticalement proches dans le fichier source. 1870 | Idéalement, gardez l'appelant juste au-dessus de l'appelé. Nous avons tendance à lire le code de haut en bas, comme un journal. 1871 | Pour cette raison, faites en sorte que votre code se lise de cette façon. 1872 | 1873 | **Mauvais :** 1874 | 1875 | ```javascript 1876 | class PerformanceReview { 1877 | constructor(employee) { 1878 | this.employee = employee; 1879 | } 1880 | lookupPeers() { 1881 | return db.lookup(this.employee, "peers"); 1882 | } 1883 | lookupManager() { 1884 | return db.lookup(this.employee, "manager"); 1885 | } 1886 | getPeerReviews() { 1887 | const peers = this.lookupPeers(); 1888 | // ... 1889 | } 1890 | perfReview() { 1891 | this.getPeerReviews(); 1892 | this.getManagerReview(); 1893 | this.getSelfReview(); 1894 | } 1895 | getManagerReview() { 1896 | const manager = this.lookupManager(); 1897 | } 1898 | getSelfReview() { 1899 | // ... 1900 | } 1901 | } 1902 | const review = new PerformanceReview(employee); 1903 | review.perfReview(); 1904 | ``` 1905 | 1906 | **Bien :** 1907 | 1908 | ```javascript 1909 | class PerformanceReview { 1910 | constructor(employee) { 1911 | this.employee = employee; 1912 | } 1913 | perfReview() { 1914 | this.getPeerReviews(); 1915 | this.getManagerReview(); 1916 | this.getSelfReview(); 1917 | } 1918 | getPeerReviews() { 1919 | const peers = this.lookupPeers(); 1920 | // ... 1921 | } 1922 | lookupPeers() { 1923 | return db.lookup(this.employee, "peers"); 1924 | } 1925 | getManagerReview() { 1926 | const manager = this.lookupManager(); 1927 | } 1928 | lookupManager() { 1929 | return db.lookup(this.employee, "manager"); 1930 | } 1931 | getSelfReview() { 1932 | // ... 1933 | } 1934 | } 1935 | const review = new PerformanceReview(employee); 1936 | review.perfReview(); 1937 | ``` 1938 | 1939 | **[⬆ retour en haut](#sommaire)** 1940 | 1941 | ## **Commentaires** 1942 | 1943 | ### Ne commentez que les éléments dont la logique métier est complexe. 1944 | 1945 | Les commentaires sont une excuse, pas une exigence. Un bon code se documente __principalement__ de lui-même. 1946 | 1947 | **Mauvais :** 1948 | 1949 | ```javascript 1950 | function hashIt(data) { 1951 | // Le hachage 1952 | let hash = 0 ; 1953 | // Longueur de la chaîne 1954 | const length = data.length ; 1955 | // Boucle sur chaque caractère des données 1956 | for (let i = 0 ; i < length ; i++) { 1957 | // Obtention du code du caractère. 1958 | const char = data.charCodeAt(i) ; 1959 | // Créer le hachage 1960 | hash = (hash << 5) - hash + char ; 1961 | // Convertit en un entier de 32 bits 1962 | hash &= hash ; 1963 | } 1964 | } 1965 | ``` 1966 | 1967 | **Bien :** 1968 | 1969 | ```javascript 1970 | function hashIt(data) { 1971 | let hash = 0; 1972 | const length = data.length; 1973 | for (let i = 0; i < length; i++) { 1974 | const char = data.charCodeAt(i); 1975 | hash = (hash << 5) - hash + char; 1976 | // Convertit en un nombre entier de 32 bits 1977 | hash &= hash; 1978 | } 1979 | } 1980 | ``` 1981 | 1982 | **[⬆ retour en haut](#sommaire)** 1983 | 1984 | ### Ne laissez pas de code commenté dans votre base de données. 1985 | 1986 | Le contrôle de version existe pour une raison. Laissez l'ancien code dans votre historique. 1987 | 1988 | **Mauvais :** 1989 | 1990 | ```javascript 1991 | doStuff(); 1992 | // doOtherStuff(); 1993 | // doSomeMoreStuff(); 1994 | // doSoMuchStuff(); 1995 | ``` 1996 | 1997 | **Bien :** 1998 | 1999 | ```javascript 2000 | doStuff(); 2001 | ``` 2002 | 2003 | **[⬆ retour en haut](#sommaire)** 2004 | 2005 | ### Ne pas avoir de commentaires de journal 2006 | 2007 | Rappelez-vous, utilisez le contrôle de version ! Il n'y a pas besoin de code mort, de code commenté, et surtout de commentaires de journal. 2008 | Utilisez `git log` pour obtenir l'historique ! 2009 | 2010 | **Mauvais :** 2011 | 2012 | ```javascript 2013 | /** 2014 | * 2016-12-20 : Suppression des monades, je ne les comprenais pas (RM). 2015 | * 2016-10-01 : Amélioration de l'utilisation des monades spéciales (JP) 2016 | * 2016-02-03 : Suppression du contrôle de type (LI) 2017 | * 2015-03-14 : Ajouté la combinaison avec le contrôle de type (JR) 2018 | */ 2019 | function combine(a, b) { 2020 | return a + b; 2021 | } 2022 | ``` 2023 | 2024 | **Bien :** 2025 | 2026 | ```javascript 2027 | function combine(a, b) { 2028 | return a + b; 2029 | } 2030 | ``` 2031 | 2032 | **[⬆ retour en haut](#sommaire)** 2033 | 2034 | ### Évitez les marqueurs de position 2035 | 2036 | Ils ne font généralement que brouiller. Laissez les fonctions et les noms de variables, ainsi que l'indentation et le formatage appropriés donner la structure visuelle de votre code. 2037 | 2038 | **Mauvais :** 2039 | 2040 | ```javascript 2041 | //////////////////////////////////////////////////////////////////////////////// 2042 | // Instanciation du modèle d'application 2043 | //////////////////////////////////////////////////////////////////////////////// 2044 | $scope.model = { 2045 | menu: "foo", 2046 | nav: "bar" 2047 | }; 2048 | //////////////////////////////////////////////////////////////////////////////// 2049 | // Configuration de l'action 2050 | //////////////////////////////////////////////////////////////////////////////// 2051 | const actions = function() { 2052 | // ... 2053 | }; 2054 | ``` 2055 | 2056 | **Bien :** 2057 | 2058 | ```javascript 2059 | $scope.model = { 2060 | menu: "foo", 2061 | nav: "bar" 2062 | }; 2063 | const actions = function() { 2064 | // ... 2065 | }; 2066 | ``` 2067 | 2068 | **[⬆ retour en haut](#sommaire)** --------------------------------------------------------------------------------