Mejores Prácticas de Código Limpio en JavaScript
Mejores Prácticas de Código Limpio en JavaScript
Tabla de Contenidos
[Link]ón
[Link]
3. Funciones
[Link] y Estructuras de Datos
[Link]
6.SÓLIDO
[Link]
[Link]
[Link] de Errores
[Link]
[Link]
12. Traducción
Introducción
Figura 1: Imagen humorística de la estimación de la calidad del software como un conteo de cuánto
muchos expletivos gritas al leer código
1
Una cosa más: saber esto no te hará inmediatamente un mejor software
desarrollador, y trabajar con ellos durante muchos años no significa que no cometerás
mistakes. Every piece of code starts as a first draft, like wet clay getting shaped
en su forma final. Finalmente, esculpimos las imperfecciones cuando lo revisamos
con nuestros compañeros. No te castigues por los borradores iniciales que necesitan mejora.
¡Golpea el código en su lugar!
Variables
Usa nombres de variables significativos y pronunciables
Malo
constyyyymmdstr=moment().format("YYYY/MM/DD");
Good:
constantecurrentDate=moment().format("YYYY/MM/DD");
volver arriba
2
setTimeout(blastOff, MILISEGUNDOS_POR_DÍA);
volver arriba
Malo:
constaddress="One Infinite Loop, Cupertino 95014";
constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
guardarCodigoPostalCiudad(
[Link](cityZipCodeRegex)[1],
[Link](cityZipCodeRegex)[2]
);
Buen:
constaddress="One Infinite Loop, Cupertino 95014";
constantecityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
constante[_,ciudad,códigoPostal]=direcció[Link](expresionRegularCiudadCódigoPostal)||[];
saveCityZipCode(city,zipCode);
volver arriba
Malo:
constlocations=["Austin","New York","San Francisco"];
[Link](l=> {
hacerCosas();
hacerOtrasCosas();
// ...
// ...
// ...
// Espera, ¿para qué es `l` otra vez?
despachar(l);
});
Bueno:
constantelocations=["Austin","New York","San Francisco"];
[Link](location=> {
doStuff();
hacerOtrasCosas();
// ...
// ...
// ...
3
despachar(lugar);
});
volver arriba
If your class/object name tells you something, don’t repeat that in your variable
nombre.
Malo
constCar={
carMake:"Honda",
carModel:"Accord",
carColor:"Blue"
};
funciónpintarCoche(coche,color) {
[Link]=color;
}
Bueno:
constCar={
make:"Honda",
model:"Accord",
color:"Blue"
};
funciónpintarCoche(coche,color) {
[Link]=color;
}
⏎ volver arriba
Malo:
funcióncreateMicrobrewery(name) {
constbreweryName=name||"Hipster Brew Co.";
// ...
}
Good:
4
funcióncrearMicrocervecería(nombre="Hipster Brew Co.") {
// ...
}
volver al principio
Functions
Argumentos de función (idealmente 2 o menos)
Limitar la cantidad de parámetros de función es increíblemente importante porque
hace que probar tu función sea más fácil. Tener más de tres lleva a una combina-
explosión de tutorial donde tienes que probar toneladas de casos diferentes con cada separado
argumento.
Uno o dos argumentos es el caso ideal, y tres deberían ser evitados si es posible.
Cualquier cosa más que eso debería ser consolidada. Normalmente, si tienes más de
dos argumentos entonces tu función está tratando de hacer demasiado. En casos donde es
no, la mayor parte del tiempo un objeto de nivel superior será suficiente como argumento.
Dado que JavaScript te permite crear objetos sobre la marcha, sin una gran cantidad de clases
plantilla, puedes usar un objeto si te ves necesitando mucho de
argumentos.
Para hacer obvias las propiedades que la función espera, puedes usar el
La sintaxis de desestructuración de ES2015/ES6. Esto tiene algunas ventajas:
crearMenú("Foo","Bar","Baz",verdadero);
Bueno:
funcióncreateMenu({ title,body,buttonText,cancellable }) {
// ...
}
5
createMenu({
title:"Foo",
body:"Bar",
buttonText:"Baz",
cancellable:verdadero
});
Volver arriba
Malo:
funciónemailClients(clients) {
[Link](cliente=> {
constanteclientRecord=[Link](client);
si([Link]()) {
email(client);
}
});
}
Bueno:
funciónemailActiveClients(clients) {
[Link](esClienteActivo).forEach(correo);
}
funciónesClienteActivo(cliente) {
constclientRecord=[Link](client);
[Link]();
}
volver arriba
constantedate=nuevoDate();
6
Es difícil saber por el nombre de la función qué se agrega
agregarAFecha(fecha,1);
Bueno:
funciónagregarMesALaFecha(mes,fecha) {
// ...
}
constantedate=nuevoDate();
agregarMesAFecha(1,fecha);
volver arriba
conststatements=[Link](" ");
const tokens=[];
[Link](REGEX=> {
[Link](declaración=> {
// ...
});
});
constast=[];
[Link](token=> {
// léx...
});
[Link](nodo=> {
// analizar...
});
}
Bueno:
funciónparseBetterJSAlternative(code) {
consttokens=tokenizar(código);
7
constantesyntaxTree=parse(tokens);
[Link](nodo=> {
// analizar...
});
}
funcióntokenizar(código) {
constREGEXES=[
// ...
];
regresartokens;
}
funciónanalizar(tokens) {
constantesyntaxTree=[];
[Link](token=> {
[Link](/* ... */);
});
devolversyntaxTree;
}
volver arriba
8
el código plicate significa crear una abstracción que pueda manejar este conjunto de diferentes
cosas con solo una función/módulo/clase.
Ajustar la abstracción correctamente es crítico, por eso deberías seguir el SOLID
los principios expuestos en la sección de Clases. Las malas abstracciones pueden ser peores que
¡Código duplicado, así que ten cuidado! Dicho esto, si puedes hacer un buen ab-
¡Abstracción, hazlo! No te repitas, de lo contrario te encontrarás actualizando
múltiples lugares en cualquier momento que quieras cambiar una cosa.
Malo
funciónmostrarListaDesarrolladores(desarrolladores) {
[Link](desarrollador=> {
constexpectedSalary=[Link]();
constanteexperience=[Link]();
constantegithubLink=[Link]();
const data={
expectedSalary,
experiencia
githubLink
};
renderizar(datos);
});
}
funciónshowManagerList(managers) {
[Link](gerente=> {
constexpectedSalary=[Link]();
constexperience=[Link]();
constportfolio=[Link]();
constdata={
expectedSalary,
experiencia
portafolio
};
render(data);
});
}
Bueno:
function mostrarListaDeEmpleados(empleados) {
[Link](empleado=> {
constexpectedSalary=[Link]();
constexperience=[Link]();
9
constdata={
expectedSalary,
experiencia
};
interruptortipo
de empleado
caso"manager":
[Link]=[Link]();
romper;
case "developer":
[Link]=[Link]();
romper;
}
renderizar(datos);
});
}
volver al principio
Malo:
constmenuConfig={
title:null,
body:"Bar",
buttonText:null,
cancellable:verdadero
};
funcióncrearMenu(config) {
[Link]=[Link]||"Foo";
[Link]=[Link]||"Bar";
[Link]=[Link]||"Baz";
[Link]=
[Link]!==indefinido?[Link]: verdadero;
}
createMenu(menuConfig);
Bueno:
constmenuConfig={
title:"Order",
// User did not include 'body' key
buttonText:"Send",
cancellable:verdadero
10
};
funcióncreateMenu(config) {
dejarfinalConfig=[Link](
{
title:"Foo",
body:"Bar",
buttonText:"Baz",
cancellable:true
},
configuración
);
devolverfinalConfig
// config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
volver arriba
Las banderas le informan a su usuario que esta función hace más de una cosa. Funciones
debería hacer una cosa. Separa tus funciones si están siguiendo un código diferente
rutas basadas en un booleano.
Malo:
funcióncrearArchivo(nombre,temp) {
si(temp) {
[Link](`./temp/${nombre}`);
} else{
[Link](name);
}
}
Bueno:
function crearArchivo(nombre) {
[Link](nombre);
}
funcióncreateTempFile(nombre) {
createFile(`./temp/${name}`);
}
volver arriba
11
Evitarefectossecundarios(parte1)
Una función produce un efecto secundario si hace algo más que tomar un valor.
en y devolver otro valor o valores. Un efecto secundario podría ser escribir en un
archivo, modificando alguna variable global, o enviando accidentalmente todo tu dinero a un
extraño.
Ahora, a veces necesitas tener efectos secundarios en un programa. Como el
en el ejemplo anterior, es posible que necesites escribir en un archivo. Lo que quieres hacer es
centralizar dónde estás haciendo esto. No tener varias funciones y clases
that write to a particular file. Have one service that does it. One and only one.
El punto principal es evitar trampas comunes como compartir estado entre objetos.
sin ninguna estructura, utilizando tipos de datos mutables que pueden ser escritos por
cualquier cosa, y no centralizando dónde ocurren tus efectos secundarios. Si puedes hacer esto,
you will be happier than the vast majority of other programmers.
Malo
// Variable global referenciada por la siguiente función.
// Si tuviéramos otra función que usara este nombre, ahora sería un arreglo y podría romperse
dejarname="Ryan McDermott";
funciónsplitIntoFirstAndLastName() {
name=[Link](" ");
}
dividirEnNombreYApellido();
constname="Ryan McDermott";
constnewName=splitIntoFirstAndLastName(name);
[Link](nombre);Ryan McDermott
[Link](nuevoNombre);// ['Ryan', 'McDermott'];
volver arriba
EvitarEfectosSecundarios(parte2)
En JavaScript, algunos valores son inmutables y otros son cambiables.
tipo (mutable). Los objetos y los arreglos son dos tipos de valores mutables, así que es
es importante manejarlos con cuidado cuando se pasan como parámetros a un
12
función. Una función de JavaScript puede cambiar las propiedades de un objeto o alterar el
el contenido de un array que podría causar fácilmente errores en otras partes.
Supongamos que hay una función que acepta un parámetro de array que representa una tienda.
carrito de compras. Si la función hace un cambio en ese arreglo del carrito de compras - al agregar
un artículo para comprar, por ejemplo - luego cualquier otra función que utilice ese mismo
el arreglo del carrito se verá afectado por esta adición. Eso puede ser genial, sin embargo,
también podría 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 solicitud de red y envía el array de carrito al servidor. Debido a un
mala conexión de red, la función de compra tiene que seguir reintentando la solicitud.
Ahora, ¿qué pasa si mientras tanto el usuario hace clic accidentalmente en 'Agregar al carrito'?
¿un botón en un artículo que en realidad no quieren antes de que comience la solicitud de red?
Si eso sucede y la solicitud de la red comienza, entonces esa función de compra
enviará el artículo añadido accidentalmente porque se modificó el array de la cesta.
1. Puede haber casos en los que realmente desees modificar el objeto de entrada,
pero cuando adoptes esta práctica de programación, encontrarás que esos
los casos son bastante raros. ¡La mayoría de las cosas se pueden refactorizar para no tener efectos secundarios!
13
extender el método nativo Array de JavaScript para tener un método adiff que podría
muestra la diferencia entre dos arreglos? Podrías escribir tu nueva función
to [Link], but it could clash with another library that tried to
haz lo mismo. ¿Qué pasaría si esa otra biblioteca solo usara diffto para encontrar el
¿diferencia entre el primer y el último elemento de un arreglo? Por eso sería
sería mucho mejor simplemente usar clases de ES2015/ES6 y extender el arreglo
global.
Malo
[Link]=funcióndiferencia(comparisonArray) {
constantehash=new Set(comparisonArray);
devuelve [Link](elem=> );
};
Bueno:
claseSuperArrayextiendeArray {
diff(comparisonArray) {
consthash=nuevoConjunto(comparisonArray);
devuelve [Link](elem=> );
}
}
volver arriba
Malo:
constprogrammerOutput=[
{
name:"Uncle Bobby",
linesOfCode:500
},
{
name:"Suzie Q",
linesOfCode:1500
},
{
name:"Jimmy Gosling",
linesOfCode:150
},
{
name:"Gracie Hopper",
14
linesOfCode:1000
}
];
dejartotalOutput=0;
para(dejari=0;i<[Link];i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
Bueno:
constprogrammerOutput=[
{
name:"Uncle Bobby",
linesOfCode:500
},
{
name:"Suzie Q",
linesOfCode:1500
},
{
name:"Jimmy Gosling",
linesOfCode:150
},
{
name:"Gracie Hopper",
linesOfCode:1000
}
];
consttotalOutput=[Link](
(totalLines,output)=> totalLines+[Link],
0
);
volver arriba
Encapsular condicionales
Malo
si([Link]==="fetching"&&isEmpty(listNode)) {
// ...
}
Bueno
funciónshouldShowSpinner(fsm,listNode) {
15
[Link] === "fetching" && estáVacío(nodoLista);
}
si(shouldShowSpinner(fsmInstance,listNodeInstance)) {
// ...
}
volver arriba
si(!isDOMNodeNotPresent(node)) {
// ...
}
Bueno:
funciónisDOMNodePresent(nodo) {
// ...
}
si(isDOMNodePresent(node)) {
// ...
}
volver arriba
Evitarcondicionales
Esto parece una tarea imposible. Al oír esto por primera vez, la mayoría de las personas dicen,
¿Cómo se supone que voy a hacer algo sin una declaración if?
que puedes usar el polimorfismo para lograr la misma tarea en muchos casos. El
La segunda pregunta suele ser: “bueno, eso es genial, pero ¿por qué querría hacer eso?”
La respuesta es un concepto previo de código limpio que aprendimos: una función debe sólo
haz una cosa. Cuando tengas clases y funciones que tienen declaraciones if, tú
estás diciendo a tu usuario que tu función hace más de una cosa. Recuerda,
solo haz una cosa.
Malo
claseAvión {
// ...
getCruisingAltitude() {
interruptor([Link]) {
16
caso"777":
devuelve [Link]()-[Link]();
casoFuerza Aérea Uno
devuelve [Link]();
caso"Cessna":
devuelve [Link]()-[Link]();
}
}
}
Bueno
claseAvión
// ...
}
claseBoeing777extiendeAvión
// ...
getCruisingAltitude() {
devuelve [Link]()-[Link]();
}
}
claseCessnaextiendeAvión
// ...
getCruisingAltitude() {
devuelve [Link]()-[Link]();
}
}
de vuelta arriba
17
funcióntravelToTexas(vehicle) {
si(vehículoinstancia deBicicleta)
vehí[Link]([Link],nuevoUbicación("texas");
} sino si(vehículoinstanceofCoche) {
vehí[Link]([Link],nuevoLocation("texas"));
}
}
Bueno:
funcióntravelToTexas(vehicle) {
vehí[Link]([Link],nuevoLocation("texas"));
}
Volver arriba
18
No sobreoptimices
Los navegadores modernos hacen mucha optimización en segundo plano en tiempo de ejecución. Mucho de
veces, si estás optimizando entonces solo estás perdiendo tu [Link]
buenos recursospara ver dónde falta optimización. Apunta a aquellos en el
mientras tanto, hasta que sean arreglados si es que pueden serlo.
Malo:
// En navegadores antiguos, cada iteración con `[Link]` sin caché sería costosa
// debido a la recomputación de `[Link]`. En navegadores modernos, esto está optimizado.
para(dejari=0,longitud=[Link];i<longitud;i++) {
// ...
}
Bueno:
para(dejari=0;i<[Link];i++) {
// ...
}
volver arriba
El código muerto es tan malo como el código duplicado. No hay razón para mantenerlo en tu
código base. ¡Si no se está utilizando, deshazte de él! Todavía estará seguro en tu versión
historia si todavía la necesitas.
Malo:
funciónoldRequestModule(url) {
// ...
}
funciónnewRequestModule(url) {
// ...
}
constreq=newRequestModule;
inventoryTracker("apples",req,"[Link]");
Bueno:
funciónnewRequestModule(url) {
// ...
}
constreq=newRequestModule;
inventarioRastreador("manzanas",req,"[Link]");
19
regresar a arriba
• Cuando quieres hacer más allá de obtener una propiedad de un objeto, no lo haces.
tienes que buscar y cambiar cada accesorio en tu base de código.
• Hace que agregar validación sea simple al hacer un aset.
• Encapsula la representación interna.
• Fácil de añadir registro y manejo de errores al obtener y establecer.
• Puedes cargar perezosamente las propiedades de tu objeto, digamos obteniéndolo de un
servidor.
Malo
funciónmakeBankAccount() {
// ...
devolver{
balance:0
// ...
};
}
constaccount=makeBankAccount();
[Link]=100;
Bueno
funciónhacerCuentaBancaria() {
// este es privado
dejarbalance=0;
20
devolver{
// ...
obtenerSaldo
setBalance
};
}
constaccount=makeBankAccount();
[Link](100);
volver arriba
Malo:
constEmployee=función(nombre) {
[Link]=name;
};
[Link]=funcióngetName() {
devuelve [Link];
};
constemployee=nuevoEmployee("John Doe");
[Link](`Nombre del empleado: ${[Link]()}`);// Employee name: John Doe
[Link];
[Link](`Employee name: ${[Link]()}`);// Employee name: undefined
Bueno
function makeEmployee(name) {
regresar{
getNombre() {
regresarname;
}
};
}
constemployee=makeEmployee("John Doe");
[Link](`Nombre del empleado: ${[Link]()}`);// Employee name: John Doe
[Link];
[Link](`Nombre del empleado: ${[Link]()}`);// Employee name: John Doe
volver al principio
21
Clases
Prefiere las clases ES2015/ES6 sobre las funciones planas de ES5
[Link]=age;
};
[Link]=funciónmove() {};
constMammal=función(age,furColor) {
si(!(esto es una instancia deMamífero)) {
lanzar nuevoError("Instanciar Mamífero con 'new'");
}
[Link](esto,edad);
[Link]=furColor;
};
[Link] = [Link]([Link]);
[Link] = Mammal;
[Link]=funciónnacerVivo() {};
constHuman=función(age,furColor,languageSpoken) {
si(!(esto es una instancia deHumano)) {
lanzar nuevoError("Instanciar Humano con `new`");
}
Mamá.call(esto,edad,colorDePelo;
[Link]=languageSpoken;
};
[Link] = [Link]([Link]);
[Link]=Human;
[Link]=funciónhablar() {};
22
Bueno:
claseAnimal {
constructor(edad) {
[Link]=age;
}
move() {
/* ... */
}
}
claseMamíferoextiendeAnimal {
constructor(edad,colorDePelo) {
súper(age);
[Link]=furColor;
}
nacimientoVivo() {
/* ... */
}
}
claseHumanoextiendeMammal {
constructor(edad,colorPelaje,idiomaHablado) {
super(edad,colorDePelo);
[Link]=languageSpoken;
}
hablar() {
/* ... */
}
}
volver arriba
23
[Link]=make;
[Link]=model;
[Link]=color;
}
setMake(make) {
[Link]=make;
}
setModel(model) {
[Link]=model;
}
setColor(color) {
[Link]=color;
}
guardar() {
[Link]([Link]);
}
}
constcar=nuevoCar("Ford","F-150","red");
[Link]("rosa");
[Link]();
Bueno:
claseCoche {
constructor(make,model,color) {
[Link]=make;
[Link]=model;
[Link]=color;
}
setMake(make) {
[Link]=make;
// NOTADevolviendo esto para encadenar
devuelve esto;
}
setModel(model) {
[Link]=model;
// NOTADevolviendo esto para encadenar
devuelve esto;
}
24
setColor(color) {
[Link]=color;
// NOTADevolviendo esto para encadenar
devuelve esto;
}
guardar() {
[Link]([Link],[Link],[Link]);
// NOTADevolviendo esto para encadenar
devuelve esto;
}
}
const car=nuevoCoche("Ford","F-150","rojo").cambiarColor("rosa").guardar();
volver arriba
Como se declaró famosamente enPatrones de diseñopor la Banda de los Cuatro, deberías preferir
composición sobre herencia donde puedas. Hay muchas buenas razones para
utiliza la herencia y muchas buenas razones para usar la composición. El punto principal para
esta máxima es que si tu mente va instintivamente hacia la herencia, intenta pensar
si la composición pudiera modelar mejor tu problema. En algunos casos puede.
Entonces podrías estar preguntándote: “¿cuándo debería usar la herencia?” Depende de
tu problema actual, pero esta es una lista decente de cuándo la herencia tiene más sentido
sentido que composición:
Tu herencia representa una relación de "es-un" y no una relación de "tiene-un".
relación (Humano->Animal vs. Usuario->DetallesUsuario).
2. Puedes reutilizar el código de las clases base (los humanos pueden moverse como todos los ani-
mals).
3. Quieres hacer cambios globales en las clases derivadas al cambiar una base
clase. (Cambia el gasto calórico de todos los animales cuando se mueven).
Malo
claseEmpleado {
constructor(nombre, correo) {
[Link]=name;
[Link]=email;
}
// ...
}
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
25
claseDatosImpositivosDelEmpleadoextiendeEmployee {
constructor(ssn,salario) {
super();
[Link]=ssn;
[Link]=salary;
}
// ...
}
Bueno:
claseEmployeeTaxData {
constructor(ssn,sueldo) {
[Link]=ssn;
[Link]=salary;
}
// ...
}
claseEmpleado {
constructor(nombre, correo) {
[Link]=name;
[Link]=email;
}
setTaxData(ssn,salario) {
[Link]=nuevoEmployeeTaxData(ssn,salary);
}
// ...
}
volver arriba
SÓLIDO
Principio de Responsabilidad Única (SRP)
Como se indica en Clean Code, “no debería haber más de una razón para un
clase para cambiar”. Es tentador llenar una clase con mucha funcionalidad,
como cuando solo puedes llevar una maleta en tu vuelo. El problema con esto es
que tu clase no será conceptualmente cohesiva y le dará muchas razones para
cambio. Minimizar la cantidad de veces que necesitas cambiar de clase es importante.
Es importante porque si hay demasiada funcionalidad en una clase y modificas un
parte de ello, puede ser difícil entender cómo eso afectará a otros dependientes
módulos en tu base de código.
26
Malo:
claseUserSettings {
constructor(usuario) {
[Link]=user;
}
changeSettings(settings) {
si([Link]()) {
// ...
}
}
verifyCredentials() {
// ...
}
}
Bueno:
claseUserAuth {
constructor(usuario) {
[Link]=user;
}
verificarCredenciales() {
// ...
}
}
claseUserSettings {
constructor(usuario) {
[Link]=user;
[Link]=nuevoUserAuth(usuario);
}
changeSettings(settings) {
si([Link]()) {
// ...
}
}
}
volver arriba
27
Principio Abierto/Cerrado (OCP)
claseAdaptador de NodoextiendeAdaptador {
constructor() {
súper();
[Link]="nodeAdapter";
}
}
claseHttpRequester {
constructor(adapter) {
[Link]=adaptador;
}
fetch(url) {
si([Link]=== "ajaxAdapter") {
devolvermakeAjaxCall(url).then(response=> {
// transformar la respuesta y devolver
});
} sino si([Link]==="nodeAdapter") {
devolverhacerLlamadaHttp(url).entonces(respuesta)=> {
// transformar respuesta y retornar
});
}
}
}
funciónmakeAjaxCall(url) {
// solicitar y devolver promesa
}
funciónhacerLlamadaHttp(url) {
// solicitud y devolución de promesa
28
}
Bueno:
claseAdaptadorAjaxextiendeAdaptador {
constructor() {
super();
[Link]="ajaxAdapter";
}
solicitar(url) {
// solicitar y devolver promesa
}
}
claseAdaptadorDeNodose extiendeAdaptador {
constructor() {
super();
[Link]="nodeAdapter";
}
solicitud(url) {
// request and return promise
}
}
claseHttpRequester {
constructor(adapter) {
[Link]=adapter;
}
fetch(url) {
return [Link](url).then(response=> {
// transformar respuesta y devolver
});
}
}
back to top
29
La mejor explicación para esto es si tienes una clase padre y una clase hijo,
entonces la clase base y la clase hija se pueden usar de manera intercambiable sin obtener
resultados incorrectos. Esto podría seguir siendo confuso, así que echemos un vistazo a la
ejemplo clásico de cuadrado-rectángulo. Matemáticamente, un cuadrado es un rectángulo, pero
si lo modelas usando la relación 'es un' a través de la herencia, rápidamente te adentras en
problema.
Malo
claseRectángulo {
constructor() {
[Link]=0;
[Link]=0;
}
setColor(color) {
// ...
}
renderizar(area) {
// ...
}
setWidth(width) {
[Link]=width;
}
setHeight(height) {
[Link]=height;
}
obtenerÁrea() {
devuelve [Link]*[Link];
}
}
claseCuadradoextiendeRectángulo {
setWidth(width) {
[Link]=width;
[Link]=anchura;
}
setHeight(altura) {
[Link]=height;
[Link]=height;
}
}
30
funciónrenderizarRectángulosGrandes(rectángulos) {
[Link](rectangle=> {
rectá[Link](4);
[Link](5);
constanteárea=rectá[Link]Área();// MALO: Devuelve 25 para Cuadrado. Debería ser 20.
rectá[Link](area);
});
}
constrectangles=[nuevoRectángulo()nuevoRectángulo()nuevoCuadrado()];
renderizarRectángulosGrandes(rectángulos);
Bueno:
claseForma {
setColor(color) {
// ...
}
renderizar(area) {
// ...
}
}
claseRectánguloextiendeForma {
constructor(ancho,alto) {
súper();
[Link]=width;
[Link]=height;
}
obtenerÁrea() {
devuelve [Link]*[Link];
}
}
claseCuadradoextiendeForma {
constructor(length) {
súper();
[Link]=length;
}
obtenerÁrea() {
devuelve [Link]*[Link];
}
}
31
funciónrenderLargeShapes(shapes) {
[Link](forma=> {
const área=[Link]Área();
[Link](area);
});
}
constanteshapes=[nuevoRectángulo(4,5)nuevoRectángulo(4,5)nuevoCuadrado(5)];
dibujarGrandesFormas(formas);
volver arriba
JavaScript no tiene interfaces, por lo que este principio no se aplica tan estrictamente como
otros. Sin embargo, es importante y relevante incluso con la falta de tipos de JavaScript.
sistema.
El ISP afirma que "los clientes no deberían verse obligados a depender de interfaces que
no utilizan. Las interfaces son contratos implícitos en JavaScript debido al pato
escribiendo.
Un buen ejemplo para observar que demuestra este principio en JavaScript es para
classes that require large settings objects. Not requiring clients to setup huge
las cantidades de opciones son beneficiosas, porque la mayor parte del tiempo no necesitarán todas
de la configuración. Hacerlas opcionales ayuda a prevenir tener una “interfaz engorrosa”.
Malo:
claseDOMTraverser {
constructor(configuración) {
[Link]=configuraciones;
[Link]();
}
configuración() {
[Link]=[Link];
[Link]();
}
traverse() {
// ...
}
}
const$=nuevoDOMTraverser({
rootNode:[Link]("body"),
32
animationModule() {}// La mayoría de las veces, no necesitaremos animar al atravesar.
// ...
});
Bueno:
claseDOMTraverser {
constructor(configuración) {
[Link]=settings;
[Link]=[Link];
[Link]();
}
configuración() {
[Link]=[Link];
[Link]();
}
setupOptions() {
if ([Link]) {
// ...
}
}
traverse() {
// ...
}
}
const$=nuevoDOMTraverser({
rootNode:[Link]("body"),
options:{
animationModule() {}
}
});
volver arriba
33
Has visto una implementación de este principio en forma de Inyección de Dependencias.
La inversión de dependencias (DI). Si bien no son conceptos idénticos, la DIP mantiene altos módulos de nivel
Como se mencionó anteriormente, JavaScript no tiene interfaces, por lo que las abstracciones que
depende de contratos implícitos. Es decir, los métodos y la prop-
propiedades que un objeto/clase expone a otro objeto/clase. En el ejemplo a continuación,
el contrato implícito es que cualquier módulo de Solicitud para un TrackingInventario
tiene unamétododeartículodepetición.
Malo:
claseInventoryRequester {
constructor() {
esto.REQ_METHODS=["HTTP"];
}
requestItem(item) {
// ...
}
}
claseInventarioSeguimiento {
constructor(elementos) {
[Link]=items;
requestItems() {
[Link](item=> {
[Link](item);
});
}
}
constinventoryTracker=nuevoInventoryTracker(["apples","bananas"]);
[Link]();
Bueno:
claseInventoryTracker {
constructor(elementos, solicitante) {
34
[Link]=items;
[Link]=requester;
}
requestItems() {
[Link](item=> {
[Link](item);
});
}
}
claseInventoryRequesterV1 {
constructor() {
esto.REQ_METHODS=["HTTP"];
}
solicitarElemento(elemento) {
// ...
}
}
claseInventoryRequesterV2 {
constructor() {
esto.REQ_METHODS=["WS"];
}
requestItem(item) {
// ...
}
}
Prueba
Las pruebas son más importantes que el envío. Si no tienes pruebas o son inadecuadas
cantidad, entonces cada vez que envíes código no estarás seguro de que no lo rompiste.
cualquier cosa. Decidir qué constituye una cantidad adecuada depende de tu equipo,
35
but having 100% coverage (all statements and branches) is how you achieve very
alta confianza y tranquilidad para el desarrollador. Esto significa que además de
tener un gran marco de pruebas, también necesitas usar unbuena herramienta de cobertura.
No hay excusa para no escribir pruebas. Haymucha buena prueba de marco JS
funcionaasí que encuentra uno que tu equipo prefiera. Cuando encuentres uno que funcione para
tu equipo, entonces intenta siempre escribir pruebas para cada nueva función/módulo que
introducir. Si tu método preferido es el Desarrollo Guiado por Pruebas (TDD), eso
es genial, pero el punto principal es asegurarse de que está alcanzando su cobertura
metas antes de lanzar cualquier función o refactorizar una existente.
describir("MomentJS",()=> {
it("maneja los límites de fecha",()=> {
dejardate;
date=nuevoMomentJS("1/1/2015");
fecha.añadirDías(30);
[Link]("1/31/2015", fecha);
date=nuevoMomentJS("1/2/2016");
fecha.añadirDías(28);
[Link]("02/29/2016", fecha);
date=nuevoMomentJS("1/2/2015");
[Link](28);
[Link]("03/01/2015", fecha);
});
});
Bueno:
importar afirmación de "afirmación";
describe("MomentJS",()=> {
it("maneja meses de 30 días",()=> {
constantedate=nuevoMomentJS("1/1/2015");
[Link]ías(30);
[Link]("1/31/2015", fecha);
});
36
[Link]ías(28);
[Link]("02/29/2016", date);
});
Concurrencia
Usa Promesas, no callbacks
Las devoluciones de llamada no son limpias y causan excesivas cantidades de anidamiento. Con
ES2015/ES6, las Promesas son un tipo global incorporado. ¡Úsalas!
Malo:
importar { obtener } de "solicitud";
importar { escribirArchivo } de "fs";
obtener(
"[Link]
(requestErr,response,body)=> {
si(errorDeSolicitud) {
[Link](requestErr);
} sino{
writeFile("[Link]",body,writeErr=> {
si(escribirErr) {
[Link](writeErr);
} otro{
[Link]("Archivo escrito");
}
});
}
}
);
Bueno:
importar { obtener } de "request-promise";
importar { escribirArchivo } de "fs-extra";
get("[Link]
.entonces(cuerpo=> {
37
devolverescribirArchivo("[Link]",cuerpo);
})
.entonces(()=> {
[Link]("File written");
})
.catch(err=> {
[Link](err);
});
_volver arriba_
Malo:
importar { obtener } de "request-promise";
importar { writeFile } de "fs-extra";
get("[Link]
.entonces(cuerpo=> {
devolverescribirArchivo("[Link]",cuerpo);
})
.entonces(()=> {
[Link]("Archivo escrito");
})
.catch(err=> {
[Link](err);
});
Bueno:
importar { obtener } de "request-promise";
importar { escribirArchivo } de "fs-extra";
función asíncronagetCleanCodeArticle() {
intenta{
constantebody=esperarobtener(
[Link]
);
esperarescribirArchivo("artí[Link]",cuerpo);
[Link]("Archivo escrito");
} atrapar(err) {
38
[Link](err);
}
}
getCleanCodeArticle()
volver arriba
Manejo de errores
¡Los errores lanzados son algo bueno! Significan que el tiempo de ejecución ha identificado con éxito...
notificado cuando algo en tu programa ha salido mal y te está dejando
saber deteniendo la ejecución de la función en la pila actual, finalizando el proceso
(en Node), y notificándote en la consola con un rastro de pila.
39
Malo:
obtenerDatos()
.entonces(data=> {
functionThatMightThrow(data);
})
.atrapar(error=> {
[Link](error);
});
Bueno:
obtenerDatos()
.entonces(datos=> {
functionThatMightThrow(data);
})
.catch(error=> {
// Una opción (más ruidosa que [Link]):
[Link](error);
// Otra opción:
notifyUserOfError(error);
// Otra opción:
reportErrorToService(error);
// ¡O haz las tres cosas!
});
↑ volver arriba
Formato
Formatting is subjective. Like many rules herein, there is no hard and fast rule
que debes seguir. El punto principal es NO DISCUTAS sobre el formato.
Haymontones de herramientaspara automatizar esto. ¡Usa uno! Es una pérdida de tiempo y
money for engineers to argue over formatting.
Para cosas que no caen bajo la esfera del formato automático (indenta-
tion, tabulaciones vs. espacios, comillas dobles vs. comillas simples, etc.) mira aquí para algunas pautas.
40
constsongs=["Back In Black","Stairway to Heaven","Hey Jude"];
constanteArtists=["ACDC","Led Zeppelin","The Beatles"];
funciónborrarBaseDeDatos() {}
funciónrestaurar_base_de_datos() {}
claseanimal {}
claseAlpaca {}
Bueno:
constanteDAYS_IN_WEEK=7;
constDAYS_IN_MONTH=30;
function borrarBaseDeDatos() {}
funciónrestaurarBaseDeDatos() {}
claseAnimal {}
claseAlpaca {}
volver arriba
buscarCompaneros() {
[Link]([Link],"compañeros";
}
lookupManager() {
[Link]([Link],"gerente");
}
getPeerReviews() {
41
constpeers=[Link]();
// ...
}
perfReview() {
[Link]();
[Link]();
[Link]ón();
}
obtenerRevisionDelGerente() {
constantemanager=[Link]();
}
obtenerAutoevaluación() {
// ...
}
}
constantereview=nuevoRevisiónDeDesempeño(empleado);
revisió[Link]ónDeRendimiento();
Bueno:
clasePerformanceReview {
constructor(empleado) {
[Link]=employee;
}
evaluaciónDeDesempeño() {
[Link]ñasDeCompañeros();
[Link]ónDelGerente();
[Link]ón();
}
obtenerRevisionesDeCompañeros() {
constpeers=[Link]();
// ...
}
lookupPeers() {
[Link]([Link],"compañeros");
}
obtenerRevisiónDelGerente() {
constmanager=[Link]();
}
42
lookupManager() {
[Link]([Link],"gerente");
}
getSelfReview() {
// ...
}
}
constantereview=nuevoPerformanceReview(employee);
reseñ[Link]ónDeRendimiento();
volver arriba
Comentarios
Solo comenta cosas que tengan complejidad en la lógica empresarial.
Los comentarios son una disculpa, no un requisito. Buen código, sobre todo documentos.
sí mismo.
Malo:
funciónhashIt(data) {
El hash
dejarhash=0;
Longitud de la cadena
constlongitud=[Link];
para(dejari=0;i<length;i++) {
43
constantechar=[Link](i);
hash=(hash<<5)-hash+char;
Malo:
hacerCosas();
// hacerOtrasCosas();
// hacerAlgunCosaMas();
// doSoMuchStuff();
Bueno:
hacerCosas();
volver arriba
Malo:
/**
* 2016-12-20: Se eliminaron los monads, no los entendía (RM)
2016-10-01: Mejorado utilizando monadas especiales (JP)
2016-02-03: Se eliminó la comprobación de tipo (LI)
* 2015-03-14: Se añadió combinar con verificación de tipo (JR)
*/
funcióncombinar(a,b) {
regresara+b;
}
Bueno:
funcióncombinar(a,b) {
devolvera+b;
}
volver arriba
44
Evitalosmarcadoresdeposición
Generalmente solo añaden ruido. Deja las funciones y los nombres de las variables junto con
la indentación y el formato adecuados dan la estructura visual a tu código.
Malo:
////////////////////////////////////////////////////////////////////////////////
// Instanciación del Modelo de Alcance
////////////////////////////////////////////////////////////////////////////////
$[Link]={
menu:"foo",
nav:"bar"
};
////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
const actions=función() {
// ...
};
Bueno:
$[Link]={
menu:"foo",
nav:"bar"
};
constactions=función() {
// ...
};
↑ volver arriba
Traducción
Esto también está disponible en otros idiomas:
• Armenio:hanumanum/clean-code-javascript/
• BanglaInsomniacSabbir/código-limpio-javascript/
• Portugués brasileño:fesnt/código-limpio-javascript
• Chino simplificado
– alivebao/código-limpio-js
45
– beginor/código-limpio-javascript
• Chino tradicional:AllJointTW/clean-code-javascript
• Francés:eugene-augier/code-limpio-javascript-fr
• Alemán:marcbruederlin/clean-code-javascript
• Indonesia:andirkh/código-limpio-javascript/
• Italiano:frappacchio/código-limpio-javascript/
• Japonésmitsuruog/código-limpio-javascript/
• Coreano:qkraudghgh/clean-code-javascript-ko
• Pulir:greg-dev/codigo-limpio-javascript-pl
• Ruso
– BoryaMogila/clean-code-javascript-ru/
– maksugr/código-limpio-javascript
• Español:tureey/clean-code-javascript
• Spanish:andersontr15/código-limpio-javascript
• Serbiodoskovicmilos/código-limpio-javascript/
• Turco:bsonmez/código-limpio-javascript
• Ucraniano:mindfr1k/clean-code-javascript-ua
• Vietnamese:hienvd/código-limpio-javascript/
volver arriba
46