forked from IreneRuedaVega/clean-code-javascript
-
Notifications
You must be signed in to change notification settings - Fork 5
/
README.md
2411 lines (1874 loc) · 63.3 KB
/
README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# clean-code-javascript
Este contenido no es original. está traducido de [aquí](https://github.com/ryanmcdermott/clean-code-javascript).
Tampoco significa que todo lo que esté en este repositorio lo comparta. De hecho,
hay unas cosas en las que no estoy de acuerdo.
## Contenido
1. [Introducción](#introduction)
2. [Variables](#variables)
3. [Funciones](#functions)
4. [Objetos y estructuras de datos](#objects-and-data-structures)
5. [Clases](#classes)
6. [SOLID](#solid)
7. [Testing](#testing)
8. [Concurrencia](#concurrency)
9. [Manejo de errores](#error-handling)
10. [Formato](#formatting)
11. [Comentarios](#comments)
12. [Traducciones](#translation)
## Introducción
![Humorous image of software quality estimation as a count of how many expletives
you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg)
Principios de Ingeniería de Software por Robert C. Martin en el libro
[_Código Limpio_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882),
adaptado al Javascript. Esto no es una guía de estilos. Esto es una guía
para crear código [legible, reutilizable y de fácil modificación](https://github.com/ryanmcdermott/3rs-of-software-architecture)
en Javascript.
No se deben seguir estrictamente todos los principios e incluso aún menos,
como tampoco, éstos van a ser dogmas internacionales ni verdades absolutas.
Los conceptos explicados no son más una compilación de buenas prácticas que
han sido agrupadas a lo largo de muchos años de experiencia colectiva por
los autores de _Código Limpio_.
Nuestro oficio de ingeniería de software tiene poco más de 50 años y todavía
estamos aprendiendo mucho. Quizás cuando la arquitectura del software sea tan
antigua como la arquitectura tradicional en sí misma, tengamos reglas más
definidas que seguir. Por ahora, dejemos que estas pautas sirvan de faro para
evaluar la calidad del código JavaScript que tu equipo y tu producís.
Una cosa más: Debes saber que estos principios, consejos o como quieras llamarlo,
no te hará instantáneamente un mejor desarrollador de software y que trabajar con
ellos durante muchos años tampoco significa que no vayas a hacer más errores.
Cada trozo de código comienza como un primer borrador, igual que un jarrón precioso
empieza con un trozo de arcilla feo y húmedo el cual vamos moldeando hasta conseguir el
resultado final. Finalmente, limamos las imperfecciones cuando lo revisamos con
nuestros compañeros a base de iteraciones. No te castigues por la posible mejora
de los primeros borradores. En vez de eso, ¡Vence al código!
## Variables
### Utiliza nombres con sentido y de fácil pronunciación para las variables
**🙅 Mal:**
```javascript
const yyyymmdstr = moment().format("YYYY/MM/DD");
```
**👨🏫 Bien:**
```javascript
const fechaACtual = moment().format("YYYY/MM/DD");
```
**[⬆ Volver arriba](#contenido)**
### Utiliza el mismo tipo de vocabulario para el mismo tipo de variables
**🙅 Mal:**
```javascript
conseguirInformacionUsuario();
conseguirDatosCliente();
conseguirRegistroCliente();)
```
**👨🏫 Bien:**
```javascript
conseguirUsuario();
```
**[⬆ Volver arriba](#contenido)**
### Utiliza nombres que puedan ser buscados
Leeremos más código del que jamás escribiremos. Es importante que el código que
escribamos sea legible y se puede buscar en él. Al no crear variables que sean
significativas para entender nuestro código... Estamos entorpeciendo a sus lectores.
Haz tus variables sean fáciles de entender y buscar. Herramientas como
[buddy.js](https://github.com/danielstjules/buddy.js) y
[ESLint](https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md)
pueden ayudan a identificar constantes no nombradas.
**🙅 Mal:**
```javascript
// Para que cojones sirve 86400000?
setTimeout(blastOff, 86400000);
```
**👨🏫 Bien:**
```javascript
// Declaralas como constantes nombradas
const MILISEGUNDOS_POR_DIA = 86400000;
setTimeout(blastOff, MILISEGUNDOS_POR_DIA);
```
**[⬆ Volver arriba](#contenido)**
### Utiliza variables explicativas
**🙅 Mal:**
```javascript
const direccion = "Calle Mallorca, Barcelona 95014";
const expresionRegularCodigoPostalCiudad = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
guardarCP(
direccion.match(expresionRegularCodigoPostalCiudad)[1],
direccion.match(expresionRegularCodigoPostalCiudad)[2]
);
```
**👨🏫 Bien:**
```javascript
const direccion = "One Infinite Loop, Cupertino 95014";
const expresionRegularCodigoPostalCiudad = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, ciudad, codigoPostal] =
direccion.match(expresionRegularCodigoPostalCiudad) || [];
guardarCP(ciudad, codigoPostal);
```
**[⬆ Volver arriba](#contenido)**
### Evita relaciones mentales
Explícito es mejor que implícito.
**🙅 Mal:**
```javascript
const ciudades = ["Barcelona", "Madrid", "Sitges"];
ciudades.forEach(l => {
hacerAlgo();
hacerAlgoMas();
// ...
// ...
// ...
// Espera, para que era `l`?
dispatch(l);
});
```
**👨🏫 Bien:**
```javascript
const ciudades = ["Barcelona", "Madrid", "Sitges"];
ciudades.forEach(direccion => {
hacerAlgo();
hacerAlgoMas();
// ...
// ...
// ...
dispatch(direccion);
});
```
**[⬆ Volver arriba](#contenido)**
### No añadas contexto innecesario
Si tu nombre de clase/objeto ya dice algo, no lo repitas en tu nombre de variable
**🙅 Mal:**
```javascript
const Coche = {
marcaCoche: "Honda",
modeloCoche: "Accord",
colorCoche: "Azul"
};
function pintarCoche(coche) {
coche.colorCoche = "Rojo";
}
```
**👨🏫 Bien:**
```javascript
const Coche = {
marca: "Honda",
modelo: "Accord",
color: "Rojo"
};
function pintarCoche(coche) {
coche.color = "Rojo";
}
```
**[⬆ Volver arriba](#contenido)**
### Utiliza argumentos por defecto en vez de circuitos cortos o condicionales
Los argumentos por defecto suelen ser más limpios que los cortocircuitos. Ten
en cuenta que si los usas, solo se asignara ese valor por defectos cuando el
valor del parámetro sea `undefined`. Otros valores "falsos" como `''`, `" "`,
`false`,`null`, `0` y `NaN`, no serán reemplazado por un valor predeterminado
pues se consideran valores como tal.
**🙅 Mal:**
```javascript
function crearMicroCerveceria(nombre) {
const nombreMicroCerveceria = nombre || "Hipster Brew Co.";
// ...
}
```
**👨🏫 Bien:**
```javascript
function crearMicroCerveceria(nombre = "Hipster Brew Co.") {
// ...
}
```
**[⬆ Volver arriba](#contenido)**
## Funciones
### Argumentos de una función (idealmente 2 o menos)
Limitar la cantidad de parámetros de una función es increíblemente importante
porque hacen que _las pruebas_ de tu función sean más sencillas. Tener más de tres
lleva a una locura combinatoria donde tienes que probar toneladas de casos
diferentes con cada argumento por separado.
El caso ideal es usar uno o dos argumentos, tres... deben evitarse si es posible.
Cualquier número superior a eso, debería ser agrupado. Por lo general, si tienes
más de dos argumentos, tu función debe de estar haciendo demasiadas cosas. En los
casos donde no es así, la mayoría de las veces un objeto de nivel superior será
suficiente como un argumento _parámetro objeto_.
Ya que Javascript te permite crear objetos al vuelo sin tener que hacer mucho
código repetitivo en una clase, puedes usar un objeto en caso de estar necesitando
muchos parámetros.
Para indicar que propiedades espera la función, puedes usar las funcionalidades
de desestructuración que nos ofrece ES2015/ES6. Éstas tienen algunas ventajas:
1. Cuando alguien mira la firma de la función, sabe inmediatamente que propiedades
están siendo usadas
2. La desetructuración también clona los valores primitivos especificados del `objeto argumento`
pasado a la función. Esto puede servir de ayuda para prevenir efectos adversos.
_Nota: Los objetos y los arrays que son desestructurados del objeto parámetro NO son clonados._
3. Las herramientas lintera o _linterns_ pueden avisarte de qué propiedades del
objeto parámetro no están en uso. _Cosa que es imposile sin desestructuración._
**🙅 Mal:**
```javascript
function crearMenu(titulo, cuerpo, textoDelBoton, cancelable) {
// ...
}
```
**👨🏫 Bien:**
```javascript
function crearMenu({ titulo, cuerpo, textoDelBoton, cancelable }) {
// ...
}
crearMenu({
titulo: "Foo",
cuerpo: "Bar",
textoDelBoton: "Baz",
cancelable: true
});
```
**[⬆ Volver arriba](#contenido)**
### Las funciones deberían hacer una cosa
De lejos, es la regla más importante en la ingeniería del software. Cuando
las funciones hacen más de una cosa, son difíciles de componer y _testear_
entre otras cosas. Si isolamos las funciones por acciones, éstas pueden ser
modificadas y mantenidas con mayor facilidad y tu código será mucho más limpio.
De toda esta guía... si has de aprender algo, que sea esto. Ya estarás mmuy
por delante de muchos desarrolladores de software.
**🙅 Mal:**
```javascript
function enviarCorreoAClientes(clientes) {
clientes.forEach(cliente => {
const historicoDelCliente = baseDatos.buscar(cliente);
if (historicoDelCliente.estaActivo()) {
enviarEmail(cliente);
}
});
}
```
**👨🏫 Bien:**
```javascript
function enviarCorreoClientesActivos(clientes) {
clientes.filter(esClienteActive).forEach(enviarEmail);
}
function esClienteActivo(cliente) {
const historicoDelCliente = baseDatos.buscar(cliente);
return historicoDelCliente.estaActivo();
}
```
**[⬆ Volver arriba](#contenido)**
### Los nombres de las funciones deberían decir lo que hacen
**🙅 Mal:**
```javascript
function añadirAFecha(fecha, mes) {
// ...
}
const fecha = new Date();
// Es difícil saber que se le está añadiendo a la fecha en este caso
añadirAFecha(fecha, 1);
```
**👨🏫 Bien:**
```javascript
function añadirMesAFecha(mes, fecha) {
// ...
}
const fecha = new Date();
añadirMesAFecha(1, fecha);
```
**[⬆ Volver arriba](#contenido)**
### Las funciones deberían ser únicamente de un nivel de abstracción
Cuando tienes más de un nivel de abstracción, tu función normalmente está
hacicendo demasiado. Separarla en funciones más pequeñas te ayudará a poder
reutilizar código y te facilitará el _testear_ éstas.
**🙅 Mal:**
```javascript
function analizarMejorAlternativaJavascript(codigo) {
const EXPRESIONES_REGULARES = [
// ...
];
const declaraciones = codigo.split(" ");
const tokens = [];
EXPRESIONES_REGULARES.forEach(EXPRESION_REGULAR => {
declaraciones.forEach(declaracion => {
// ...
});
});
const ast = [];
tokens.forEach(token => {
// lex...
});
ast.forEach(nodo => {
// parse...
});
}
```
**👨🏫 Bien:**
```javascript
function analizarMejorAlternativaJavascript(codigo) {
const tokens = tokenize(codigo);
const ast = lexer(tokens);
ast.forEach(nodo => {
// parse...
});
}
function tokenize(codigo) {
const EXPRESIONES_REGULARES = [
// ...
];
const declaraciones = codigo.split(" ");
const tokens = [];
EXPRESIONES_REGULARES.forEach(EXPRESION_REGULAR => {
declaraciones.forEach(declaracion => {
tokens.push(/* ... */);
});
});
return tokens;
}
function lexer(tokens) {
const ast = [];
tokens.forEach(token => {
ast.push(/* ... */);
});
return ast;
}
```
**[⬆ Volver arriba](#contenido)**
### Elimina código duplicado
Haz todo lo posible para evitar duplicación de código. Duplicar código es
malo porque significa que para editar un comportamiento... tendrás que modificarlko
en más de un sitio. ¿Y no queremos trabajar de más, verdad?
Como caso práctico: Imagina que tienes un restaurante. Llevas el registro del
inventario: Todos tus tomates, cebollas, ajos, especies, etc... Si tuvieras más
de una lista que tuvieras que actualizar cada vez que sirves un tomate o usas
una especie, sería más fácil de cometer errores, además de todo el tiempo perdido.
Si solo tienes una, la posibilidad de cometer una error se reduce a ésta!
A menudo tienes código duplicado porque tienes dos o más cosas ligeramente
diferentes, que tienen mucho en común, pero sus diferencias te obligan a tener
ese código de más. Borrar la duplicación de código significa crear una abstracción
que pueda manejar este conjunto de cosas diferentes con una sola función/módulo/clase.
Hacer que la abstracción sea correcta es fundamental y a veces bastante complejo.
Es por eso que debes seguir los Principios `SOLID` establecidos en la sección _Clases_.
Las malas abstracciones pueden ser peores que el código duplicado. ¡Así que ten cuidado!
Dicho esto, si se puede hacer una buena abstracción, ¡Házla! Evita repetirte
porque de lo contrario, como hemos comentado anteriormente, te verás editando
en más de un lugar para modificar un comportamiento.
**🙅 Mal:**
```javascript
function mostrarListaDesarrolladores(desarrolladores) {
desarrolladores.forEach(desarrollador => {
const salarioEsperado = desarrollador.calcularSalarioEsperado();
const experiencia = desarrollador.conseguirExperiencia();
const enlaceGithub = desarrollador.conseguirEnlaceGithub();
const datos = {
salarioEsperado,
experiencia,
enlaceGithub
};
render(datos);
});
}
function mostrarListaJefes(jefes) {
jefes.forEach(jefe => {
const salarioEsperado = desarrollador.calcularSalarioEsperado();
const experiencia = desarrollador.conseguirExperiencia();
const experienciaLaboral = jefe.conseguirProyectosMBA();
const data = {
salarioEsperado,
experiencia,
experienciaLaboral
};
render(data);
});
}
```
**👨🏫 Bien:**
```javascript
function mostrarListaEmpleados(empleados) {
empleados.forEach(empleado => {
const salarioEsperado = empleado.calcularSalarioEsperado();
const experiencia = empleado.conseguirExperiencia();
const datos = {
salarioEsperado,
experiencia
};
switch (empleado.tipo) {
case "jefe":
datos.portafolio = empleado.conseguirProyectosMBA();
break;
case "desarrollador":
datos.enlaceGithub = empleado.conseguirEnlaceGithub();
break;
}
render(datos);
});
}
```
**[⬆ Volver arriba](#contenido)**
### Asigna objetos por defecto con Object.assign
**🙅 Mal:**
```javascript
const configuracionMenu = {
titulo: null,
contenido: "Bar",
textoBoton: null,
cancelable: true
};
function crearMenu(config) {
config.titulo = config.titulo || "Foo";
config.contenido = config.contenido || "Bar";
config.textoBoton = config.textoBoton || "Baz";
config.cancelable =
config.cancelable !== undefined ? config.cancelable : true;
}
crearMenu(configuracionMenu);
```
**👨🏫 Bien:**
```javascript
const configuracionMenu = {
titulo: "Order",
// El usuario no incluyó la clave 'contenido'
textoBoton: "Send",
cancelable: true
};
function crearMenu(configuracion) {
configuracion = Object.assign(
{
titulo: "Foo",
contenido: "Bar",
textoBoton: "Baz",
cancelable: true
},
configuracion
);
// configuracion ahora es igual a: {titulo: "Order", contenido: "Bar", textoBoton: "Send", cancelable: true}
// ...
}
crearMenu(configuracionMenu);
```
**[⬆ Volver arriba](#contenido)**
### No utilices banderas o flags
Las banderas o _flags_ te indican de que esa función hace más de una cosa. Ya
que como vamos repitiendo, nuestras funciones solo deberían hacer una cosa, separa
esa lógica que es diferenciada por la bandera o _flag_ en una nueva función.
**🙅 Mal:**
```javascript
function crearFichero(nombre, temporal) {
if (temporal) {
fs.create(`./temporal/${nombre}`);
} else {
fs.create(nombre);
}
}
```
**👨🏫 Bien:**
```javascript
function crearFichero(nombre) {
fs.create(nombre);
}
function crearFicheroTemporal(nombre) {
crearFichero(`./temporal/${nombre}`);
}
```
**[⬆ Volver arriba](#contenido)**
### Evita los efectos secundarios (parte 1)
Una función produce un efecto adverso/colateral si hace otra cosa que recibir
un parámetro de entrada y retornar otro valor o valores. Un efecto adverso puede
ser escribir un fichero, modificar una variable global o accidentalmente enviar
todo tu dinero a un desconocido.
Ahora bien, a veces necesitamos efectos adversos en nuestros programas. Como
en el ejemplo anterior, quizás necesitas escribir en un fichero. Así pues, lo que
queremos es centralizar donde se hace esta acción. No queremos que esta lógica
la tengamos que escribir en cada una de las funciones o clases que van a utilizarla.
Para eso, la encapsularemos en un servicio que haga eso. Sólo eso.
El objetivo principal es evitar errores comunes como compartir el estado entre objetos
sin ninguna estructura, usando tipos de datos mutables que pueden ser escritos por cualquier cosa
y no centralizar donde se producen sus efectos secundarios. Si puedes hacer esto, serás
más feliz que la gran mayoría de otros programadores.
**🙅 Mal:**
```javascript
// Variable Global referenciada por la siguiente función
// Si tuvieramos otra función que usara ese nombre, podría ser un array y lo estaríamos rompiendo
// If we had another function that used this name, now it'd be an array and it could break it.
let nombre = 'Ryan McDermott';
function separarEnNombreYApellido) {
nombre = nombre.split(' ');
}
separarEnNombreYApellido();
console.log(nombre); // ['Ryan', 'McDermott'];
```
**👨🏫 Bien:**
```javascript
function separarEnNombreYApellido) {
return nombre.split(' ');
}
const nombre = 'Ryan McDermott';
const nuevoNombre = separarEnNombreYApellidoe);
console.log(nombre); // 'Ryan McDermott';
console.log(nuevoNombre); // ['Ryan', 'McDermott'];
```
**[⬆ Volver arriba](#contenido)**
### Evita los efectos secundarios (parte 2)
En JavaScript, los primitivos se pasan por valor y los objetos / arrays se pasan por
referencia. En el caso de objetos y arrays, si su función hace un cambio como por ejemplo,
añadiendo un elemento al array que representa el carrito de la compra, entonces cualquier
otra función que use ese array `carrito` se verá afectada por esta modificación.
Eso puede ser genial, sin embargo, también puede ser malo. Imaginemos una mala situación:
El usuario hace clic en el botón "Comprar", que llama a una función de "compra" que
genera una petición de red y envía el array `carrito` al servidor. Dada una mala
conexión de red, la función `comprar` tiene que seguir reintentando la solicitud.
Ahora, ¿Qué pasa si mientras tanto el usuario hace clic accidentalmente en el botón
_"Agregar al carrito"_ en un elemento que realmente no quiere, antes de que comience
la solicitud de red? Si esto sucede y la solicitud de red comienza, entonces esa
función de compra enviará el artículo agregado accidentalmente porque tiene una
referencia al objeto dado que la función `añadirObjetoAlCarrito` modificó el `carrito`
agregando un elemento que no deseado.
Una buena solución para `añadirObjetoAlCarrito` podría ser clonar el `carrito`, editarlo,
y retornar la copia. Esto nos asegura que ninguna otra función tiene referencia al
objeto con los campos modificados. Así pues, ninguna otra función se verá afectada
por nuestros cambios.
Dos advertencias que mencionar para este enfoque:
1. Puede haber casos en los que realmente desee modificar el objeto de entrada,
pero cuando adopte esta práctica de programación encontrará que esos casos son
bastante raros ¡La mayoría de las cosas se pueden refactorizar para que no tengan
efectos secundarios!
2. Clonar objetos grandes puede ser muy costosa en términos de rendimiento.
Por suerte, en la práctica, esto no es un gran problema dado que hay
[buenas librerías](https://facebook.github.io/immutable-js/) que permiten este
tipo de enfoque de programación. Es rápido y no requiere tanta memoria como te
costaría a ti clonar manualmente los arrays y los objetos.
**🙅 Mal:**
```javascript
const añadirObjetoAlCarrito = (carrito, objeto) => {
carrito.push({ objeto, fecha: Date.now() });
};
```
**👨🏫 Bien:**
```javascript
const añadirObjetoAlCarrito = (carrito, objeto) => {
return [...carrito, { objeto, fecha: Date.now() }];
};
```
**[⬆ Volver arriba](#contenido)**
### No escribas en variables globales
La contaminación global es una mala práctica en JavaScript porque podría chocar
con otra librería y usuarios usuarios de tu API no serían conscientes de ello hasta
que tuviesen un error en producción. Pensemos en un ejemplo: ¿Qué pasaría si quisieras
extender los arrays de Javascript para tener un método `diff` que pudiera enseñar la
diferencia entre dos arrays? Podrías escribir tu nueva función en el `Array.prototype`,
pero podría chocar con otra librería que intentó hacer lo mismo. ¿Qué pasa si esa otra
librería estaba usando `diff` para encontrar la diferencia entre los elementos primero
y último de una matriz? Tendríamos problemas... Por eso, sería mucho mejor usar las
clases ES2015 / ES6 y simplemente extender el `Array` global.
**🙅 Mal:**
```javascript
Array.prototype.diff = function diff(matrizDeComparación) {
const hash = new Set(matrizDeComparación);
return this.filter(elemento => !hash.has(elemento));
};
```
**👨🏫 Bien:**
```javascript
class SuperArray extends Array {
diff(matrizDeComparación) {
const hash = new Set(matrizDeComparación);
return this.filter(elemento => !hash.has(elemento));
}
}
```
**[⬆ Volver arriba](#contenido)**
### Da prioridad a la programación funcional en vez de la programación imperativa
Javascript no es un lenguage funcional en la misma medida que lo es Haskell, pero
tiene aspectos que lo favorecen. Los lenguages funcionales pueden ser más fáciles
y limpios de _testear_. Favorece este estilo de programación siempre que puedas.
**🙅 Mal:**
```javascript
const datosSalidaProgramadores = [
{
nombre: "Uncle Bobby",
liniasDeCodigo: 500
},
{
nombre: "Suzie Q",
liniasDeCodigo: 1500
},
{
nombre: "Jimmy Gosling",
liniasDeCodigo: 150
},
{
nombre: "Gracie Hopper",
liniasDeCodigo: 1000
}
];
let salidaFinal = 0;
for (let i = 0; i < datosSalidaProgramadores.length; i++) {
salidaFinal += datosSalidaProgramadores[i].liniasDeCodigo;
}
```
**👨🏫 Bien:**
```javascript
const datosSalidaProgramadores = [
{
nombre: "Uncle Bobby",
liniasDeCodigo: 500
},
{
nombre: "Suzie Q",
liniasDeCodigo: 1500
},
{
nombre: "Jimmy Gosling",
liniasDeCodigo: 150
},
{
nombre: "Gracie Hopper",
liniasDeCodigo: 1000
}
];
const salidaFinal = datosSalidaProgramadores
.map(salida => salida.linesOfCode)
.reduce((totalLinias, linias) => totalLinias + linias);
```
**[⬆ Volver arriba](#contenido)**
### Encapsula los condicionales
**🙅 Mal:**
```javascript
if (fsm.state === "cogiendoDatos" && estaVacio(listaNodos)) {
// ...
}
```
**👨🏫 Bien:**
```javascript
function deberiaMostrarSpinner(fsm, listaNodos) {
return fsm.state === "cogiendoDatos" && estaVacio(listaNodos);
}
if (deberiaMostrarSpinner(fsmInstance, listNodeInstance)) {
// ...
}
```
**[⬆ Volver arriba](#contenido)**
### Evita condicionales negativos
**🙅 Mal:**
```javascript
function noEstaElNodoPresente(node) {
// ...
}
if (!noEstaElNodoPresente(node)) {
// ...
}
```
**👨🏫 Bien:**
```javascript
function estaElNodoPresente(node) {
// ...
}
if (estaElNodoPresente(node)) {
// ...
}
```
**[⬆ Volver arriba](#contenido)**
### Evita condicionales
Esto parece una tarea imposible. Al escuchar esto por primera vez, la mayoría de
la gente dice _"¿como voy a ser capaz de hacer cosas sin un `if`"?_ La respuesta a eso,
es que deberías usar polimorfismo para conserguir lo mismo en la gran mayoría de los
casos. La segunda pregunta que normalmente la gente hace es, _¿Bueno está bien pero
para que voy a querer hacerlo?_ La respuesta es uno de los conceptos previos que
hemos visto de _Código limpio_: Una función debería hacer únicamente una cosa.
Cuando tienes una función o clase que posee un `if`, le estás diciendo al usuario
que tu función está haciendo más de una cosa. Recuerda, tan sólo una cosa.
**🙅 Mal:**
```javascript
class Avion {
// ...
obtenerAlturaDeVuelo() {
switch (this.tipo) {
case "777":
return this.cogerAlturaMaxima() - this.conseguirNumeroPasajeros();
case "Air Force One":
return this.cogerAlturaMaxima();
case "Cessna":
return this.cogerAlturaMaxima() - this.getFuelExpenditure();
}
}
}
```
**👨🏫 Bien:**
```javascript
class Avion {
// ...
}
class Boeing777 extends Avion {
// ...
obtenerAlturaDeVuelo() {
return this.cogerAlturaMaxima() - this.conseguirNumeroPasajeros();
}
}
class AirForceOne extends Avion {
// ...
obtenerAlturaDeVuelo() {
return this.cogerAlturaMaxima();
}
}
class Cessna extends Avion {
// ...
obtenerAlturaDeVuelo() {
return this.cogerAlturaMaxima() - this.getFuelExpenditure();
}
}
```
**[⬆ Volver arriba](#contenido)**
### Evita el control de tipos (parte 1)
Javascript es un lenguaje no tipado. Esto significa que las funciones pueden recibir
cualquier tipo como argumento. A veces, nos aprovechamos de eso... y es por eso, que
se vuelve muy tentador el controlar los tipos de los argumentos de la función. Hay
algunas soluciones para evitar esto. La primera, son APIs consistentes. Por API se
entiende de que manera nos comunicamos con ese módulo/función.
**🙅 Mal:**
```javascript
function viajarATexas(vehiculo) {
if (vehiculo instanceof Bicicleta) {
vehiculo.pedalear(this.ubicacionActual, new Localizacion("texas"));
} else if (vehiculo instanceof Car) {
vehiculo.conducir(this.ubicacionActual, new Localizacion("texas"));
}
}
```
**👨🏫 Bien:**
```javascript
function viajarATexas(vehiculo) {
vehiculo.mover(this.ubicacionActual, new Localizacion("texas"));
}
```
**[⬆ Volver arriba](#contenido)**
### Evita control de tipos (parte 2)
Si estás trabajando con los tipos primitivos como son las `cadenas` o `enteros`,
y no puedes usar polimorfismo pero aún ves la necesidad del control de tipos,
deberías considerar `Typescript`. Es una excelente alternativa al `Javascript`
convencional que nos aporta control de tipos de manera estática entre otras
muchas cosas. El problema de controlar manualmente el tipado en `Javascript` es
que para hacerlo bien, necesitamos añadir mucho código a bajo nivel que afecta a
la legibilidad del código. Mantén tu código `Javascript` limpio, escribe _tests_
y intenta tener revisiones de código. Si no, intenta cubrir el máximo de cosas con
`Typescript` que como ya hemos dicho, es una muy buena alternativa.
**🙅 Mal:**
```javascript
function combina(valor1, valor2) {
if (
(typeof valor1 === "number" && typeof valor2 === "number") ||
(typeof valor1 === "string" && typeof valor2 === "string")
) {
return valor1 + valor2;
}
throw new Error("Debería ser una cadena o número");
}
```
**👨🏫 Bien:**
```javascript
function combina(valor1, valor2) {
return valor1 + valor2;
}
```
**[⬆ Volver arriba](#contenido)**
### No optimizes al máximo
Los navegadores modernos hacen mucha optimización por detrás en tiempo de ejecución.
Muchas veces, al interntar optimizar tu código... estás perdiendo el tiempo.
[Esta es una buena documentación](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers)
para ver donde falta optimización. Pon el foco en éstas hasta que estén arregladas/hechas
si es que se pueden.
**🙅 Mal:**
```javascript
// En los navegadores antiguos, cada iteración en la que `list.length` no esté cacheada
// podría ser costosa por el recálculo de este valor. En los modernos, ya está optimizado
for (let i = 0, tamaño = lista.length; i < tamaño; i++) {
// ...
}