Escribir código JavaScript limpio y escalable: 12 consejos

JavaScript es de la primera web. Al principio, se escribieron scripts simples que "animaron" las páginas de los sitios. Ahora JS se ha convertido en un lenguaje de programación completo que incluso se puede utilizar para desarrollar proyectos del lado del servidor.

Las aplicaciones web modernas dependen en gran medida de JavaScript. Esto es especialmente cierto para aplicaciones de una sola página (Aplicación de una sola página, SPA). Con el advenimiento de bibliotecas y marcos como React, Angular y Vue, JavaScript se ha convertido en uno de los principales bloques de construcción de aplicaciones web.



Escalar tales aplicaciones, ya sea sobre sus partes cliente o servidor, puede ser una tarea muy difícil. Si tales aplicaciones se basan en una arquitectura mal pensada, sus desarrolladores, tarde o temprano, enfrentarán ciertas limitaciones. Se están ahogando en un mar de sorpresas desagradables.

El autor del artículo, que publicamos hoy, quiere compartir consejos sobre cómo escribir código JavaScript puro. Él dice que el artículo es para programadores de JS de cualquier nivel de habilidad. Pero será especialmente útil para aquellos que están familiarizados con JavaScript, al menos en un nivel intermedio.

1. Código de aislamiento


Para mantener limpio el código base del proyecto de modo que el código pueda leerse fácilmente, se recomienda que los fragmentos de código se separen en bloques separados según su propósito. Estos bloques suelen ser funciones. Considero que esta recomendación es la más importante que puedo dar. Si está escribiendo una función, debe enfocarse inmediatamente en el hecho de que esta función estaría dirigida a resolver un solo problema. La función no debe estar diseñada para resolver varios problemas.

Además, debe esforzarse por asegurarse de que las llamadas a funciones no provoquen efectos secundarios. En la mayoría de los casos, esto significa que la función no debe cambiar algo que se declara fuera de ella. Los datos se reciben a través de parámetros. Ella no debería trabajar con nada más. Debe return cualquiera de las funciones con la palabra clave return .

2. Desglose del código en módulos.


Las funciones que se utilizan de manera similar o realizan acciones similares se pueden agrupar en un módulo (o, si lo desea, en una clase separada). Suponga que necesita hacer varios cálculos en su proyecto. En tal situación, tiene sentido expresar las diferentes etapas de dichos cálculos en forma de funciones separadas (bloques aislados), cuyas llamadas se pueden combinar en cadenas. Sin embargo, todas estas funciones se pueden declarar en un solo archivo (es decir, en un módulo). Aquí hay un ejemplo del módulo calculation.js que contiene funciones similares:

 function add(a, b) {   return a + b  } function subtract(a, b) {   return a - b  } module.exports = {   add,   subtract } 

Y así es como puede usar este módulo en otro archivo (llamémoslo index.js ):

 const { add, subtract } = require('./calculations') console.log(subtract(5, add(3, 2)) 

Los desarrolladores de aplicaciones frontend pueden recibir las siguientes recomendaciones. Para exportar las entidades más importantes declaradas en el módulo, use las opciones de exportación predeterminadas. Para entidades secundarias, puede usar la exportación con nombre.

3. Use múltiples parámetros de función en lugar de un solo objeto con parámetros


Al declarar una función, debe esforzarse por usar varios parámetros, en lugar de un solo objeto con parámetros. Aquí hay un par de ejemplos:

 //  function displayUser(firstName, lastName, age) {   console.log(`This is ${firstName} ${lastName}. She is ${age} years old.`) } //  function displayUser(user) {   console.log(`This is ${user.firstName} ${user.lastName}. She is ${user.age} years old.`) } 

La presencia de una función con varios parámetros le permite ver de inmediato a qué se debe pasar mirando la primera línea de su declaración. Esta es precisamente la razón por la que doy esta recomendación.

A pesar de que al desarrollar funciones, debe esforzarse para asegurarse de que cada una de ellas resuelva solo un problema, el tamaño del código de la función puede ser bastante grande. Si una función acepta un solo objeto con parámetros, entonces para saber exactamente lo que espera, es posible que deba mirar todo su código, pasando mucho tiempo en él. A veces puede parecer que cuando se trabaja con funciones es mucho más fácil usar un solo objeto con parámetros. Pero si escribe funciones, dada la posible escala futura de la aplicación, es mejor usar varios parámetros.

Cabe señalar que hay un cierto límite después del cual el uso de parámetros individuales pierde su significado. En mi caso, estos son cuatro o cinco parámetros. Si una función necesita tanta entrada, entonces un programador debería considerar usar un objeto con parámetros.

La razón principal de esta recomendación es que los parámetros individuales esperados por la función deben pasarse a ella en un orden específico. Si algunos de los parámetros son opcionales, debe pasar funciones como undefined o null . Cuando se usa un objeto con parámetros, el orden de los parámetros en el objeto no importa. Con este enfoque, puede hacerlo sin establecer los parámetros opcionales en undefined .

4. Reestructuración


La reestructuración es un mecanismo útil que apareció en ES6. Le permite extraer los campos especificados de los objetos e inmediatamente escribirlos en las variables. Se puede usar cuando se trabaja con objetos y módulos:

 //    const { add, subtract } = require('./calculations') 

En particular, cuando se trabaja con módulos, tiene sentido importar en un archivo no todo el módulo, sino solo las funciones necesarias, dándoles nombres comprensibles. De lo contrario, tendrá que acceder a las funciones utilizando una variable que simbolice el módulo.

Un enfoque similar es aplicable a aquellos casos en los que se utiliza un solo objeto como parámetro de función. Esto permite mirar la primera línea de la función para descubrir de inmediato qué es exactamente lo que espera recibir como un objeto con parámetros:

 function logCountry({name, code, language, currency, population, continent}) {   let msg = `The official language of ${name} `   if(code) msg += `(${code}) `   msg += `is ${language}. ${population} inhabitants pay in ${currency}.`   if(contintent) msg += ` The country is located in ${continent}` } logCountry({   name: 'Germany',   code: 'DE',   language 'german',   currency: 'Euro',   population: '82 Million', }) logCountry({   name: 'China',   language 'mandarin',   currency: 'Renminbi',   population: '1.4 Billion',   continent: 'Asia', }) 

Como puede ver, a pesar del hecho de que la función acepta un solo objeto con parámetros, su desestructuración le permite descubrir qué es exactamente lo que debe colocarse cuando se llama a la función. El siguiente consejo será sobre cómo decirle con mayor precisión al usuario qué espera la función.

Por cierto, la reestructuración también se puede utilizar cuando se trabaja con componentes funcionales de React.

5. Establecer valores predeterminados para parámetros de función


Los valores estándar de los parámetros de las funciones, los valores predeterminados de los parámetros, tiene sentido usarlos al desestructurar objetos con parámetros, y en esos casos cuando las funciones aceptan listas de parámetros. En primer lugar, le da al programador un ejemplo de qué funciones se pueden pasar. En segundo lugar, le permite averiguar qué parámetros son obligatorios y cuáles son opcionales. Complementamos la declaración de función del ejemplo anterior con valores de parámetros estándar:

 function logCountry({   name = 'United States',   code,   language = 'English',   currency = 'USD',   population = '327 Million',   continent, }) {   let msg = `The official language of ${name} `   if(code) msg += `(${code}) `   msg += `is ${language}. ${population} inhabitants pay in ${currency}.`   if(contintent) msg += ` The country is located in ${continent}` } logCountry({   name: 'Germany',   code: 'DE',   language 'german',   currency: 'Euro',   population: '82 Million', }) logCountry({   name: 'China',   language 'mandarin',   currency: 'Renminbi',   population: '1.4 Billion',   continent: 'Asia', }) 

Es obvio que en algunos casos, si no se le pasó un parámetro de función al llamar a una función, es necesario dar un error, en lugar de usar el valor estándar de este parámetro. Pero a menudo, sin embargo, la técnica descrita aquí es muy útil.

6. No pasar datos innecesarios a funciones


La recomendación anterior nos lleva a una conclusión interesante. Consiste en el hecho de que las funciones no necesitan transferir los datos que no necesitan. Si sigue esta regla, el desarrollo de funciones puede requerir tiempo adicional. Pero a la larga, este enfoque conducirá a la formación de una base de código que se distingue por una buena legibilidad. Además, es increíblemente útil saber qué tipo de datos se utilizan en cada lugar particular del programa.

7. Limitar el número de líneas en los archivos y el nivel máximo de anidación de código


He visto archivos grandes con código de programa. Muy grande Algunos tenían más de 3,000 líneas. En tales archivos es muy difícil navegar.

Como resultado, se recomienda limitar el tamaño de los archivos medidos en líneas de código. Por lo general, me esfuerzo por garantizar que el tamaño de mis archivos no supere las 100 líneas. A veces, cuando es difícil dividir cierta lógica en pequeños fragmentos, el tamaño de mis archivos alcanza 200-300 líneas. Y muy raramente, su tamaño alcanza las 400 líneas. Los archivos que exceden este límite son difíciles de leer y mantener.

En el curso del trabajo en sus proyectos, cree audazmente nuevos módulos y carpetas. La estructura del proyecto debe parecerse a un bosque formado por árboles (grupos de módulos y archivos de módulos) y ramas (secciones de módulos). Esfuércese por asegurarse de que sus proyectos no se vean como cadenas montañosas.

Si hablamos de la apariencia de los propios archivos con el código, entonces deberían ser similares al terreno con colinas bajas. Se trata de evitar grandes niveles de anidación de código. Vale la pena esforzarse por garantizar que el anidamiento del código no supere los cuatro niveles.

Quizás observar estas recomendaciones ayudará a aplicar las reglas apropiadas de ESLint linter.

8. Use herramientas para formatear código automáticamente


Cuando trabaje en proyectos de JavaScript en un equipo, debe desarrollar una guía clara sobre el estilo y el formato del código. Puede automatizar el formateo de código con ESLint. Este linter ofrece al desarrollador un gran conjunto de reglas personalizables. Hay un eslint --fix que puede corregir algunos errores.

Sin embargo, recomiendo usar Prettier en lugar de ESLint para automatizar el formateo del código. Con este enfoque, el desarrollador puede no tener que preocuparse por el formato del código. Solo necesita escribir programas de calidad. Todo el código que se formatea automáticamente usando un solo conjunto de reglas se verá consistente.

9. Use nombres de variables bien diseñados


El nombre de la variable idealmente debería reflejar su contenido. Aquí hay algunas pautas para seleccionar nombres informativos de variables.

▍ Funciones


Por lo general, las funciones realizan algún tipo de acción. Las personas, cuando hablan de acciones, usan verbos. Por ejemplo: convertir (convertir) o mostrar (mostrar). Se recomienda que se formen nombres de funciones para que comiencen con un verbo. Por ejemplo, convertCurrency o displayUser .

▍ Arreglos


Las matrices generalmente contienen conjuntos de algunos valores. Como resultado, tiene sentido agregar la letra s al nombre de la variable que almacena la matriz. Por ejemplo:

 const students = ['Eddie', 'Julia', 'Nathan', 'Theresa'] 

▍ Valores lógicos


Los nombres de variables booleanas tienen sentido para comenzar con is o has . Esto los acerca a las construcciones que están disponibles en lenguaje ordinario. Por ejemplo, aquí está la pregunta: "¿Esa persona es maestra?". La respuesta a esto puede ser "Sí" o "No". Puede hacer lo mismo seleccionando nombres para variables lógicas:

 const isTeacher = true //  false 

▍ Parámetros de funciones pasados ​​a métodos de matriz estándar


Estos son algunos métodos estándar de matriz de JavaScript: para forEach , map , reduce , filter . Le permiten realizar ciertas acciones con matrices. Son funciones pasadas que describen operaciones en matrices. Vi cuántos programadores simplemente pasan parámetros con nombres como el o element a tales funciones. Aunque este enfoque libera al programador de pensar en nombrar tales parámetros, es mejor llamarlos en función de los datos que aparecen en ellos. Por ejemplo:

 const cities = ['Berlin', 'San Francisco', 'Tel Aviv', 'Seoul'] cities.forEach(function(city) { ... }) 

▍ Identificadores


A menudo sucede que un programador necesita trabajar con los identificadores de ciertos conjuntos de datos u objetos. Si tales identificadores están anidados, no es necesario hacer nada especial con ellos. Por ejemplo, cuando trabajo con MongoDB, generalmente convierto _id a id antes de devolver el objeto a la aplicación front-end. Al extraer identificadores de objetos, se recomienda formar sus nombres configurando el tipo de objeto antes de la id . Por ejemplo:

 const studentId = student.id //  const { id: studentId } = student //    

Una excepción a esta regla es trabajar con referencias MongoDB en modelos. En tales casos, se recomienda nombrar los campos de acuerdo con los modelos a los que se hace referencia en ellos. Esto, al completar documentos para los que hay enlaces en los campos, permitirá mantener el código limpio y uniforme:

 const StudentSchema = new Schema({   teacher: {       type: Schema.Types.ObjectId,       ref: 'Teacher',       required: true,   },   name: String,   ... }) 

10. Use la construcción async / await donde sea posible.


El uso de devoluciones de llamada degrada la legibilidad del código. Esto es especialmente cierto para las devoluciones de llamada anidadas. Las promesas aclararon un poco las cosas, pero creo que es mejor leer el código que usa la construcción async / await. Incluso los principiantes y desarrolladores que cambiaron a JavaScript desde otros idiomas pueden entender fácilmente este código. Lo más importante aquí es dominar los conceptos subyacentes async / wait. No use este diseño en todas partes debido a su novedad.

11. El procedimiento para importar módulos


Las recomendaciones 1 y 2 demostraron la importancia de elegir el lugar correcto para almacenar el código para garantizar que sea compatible. Ideas similares se aplican al orden de importación de módulos. Es decir, estamos hablando del hecho de que el orden lógico de importación de módulos aclara el código. Al importar módulos, sigo el siguiente esquema simple:

 //    import React from 'react' import styled from 'styled-components' //  import Store from '~/Store // ,    import Button from '~/components/Button' //   import { add, subtract } from '~/utils/calculate' //  import Intro from './Intro' import Selector from './Selector' 

Este ejemplo está basado en React. La misma idea no será difícil de transferir a ningún otro entorno de desarrollo.

12. Evite usar console.log


El comando console.log es una herramienta simple, rápida y conveniente para depurar programas. Hay, por supuesto, herramientas más avanzadas de este tipo, pero creo que casi todos los programadores todavía usan console.log . Si, usando console.log para la depuración, si no elimina las llamadas de este comando que se vuelven innecesarias a tiempo, la consola pronto se desordenará por completo. Cabe señalar que tiene sentido dejar algunos equipos de registro incluso en el código de proyectos que están completamente listos para trabajar. Por ejemplo, comandos que muestran mensajes de error y advertencias.

Como resultado, podemos decir que es bastante posible usar console.log para propósitos de depuración, y en los casos en que los comandos de registro están planeados para ser utilizados en proyectos de trabajo, tiene sentido recurrir a bibliotecas especializadas. Entre ellos se encuentran loglevel y winston . Además, ESLint se puede usar para combatir comandos de registro innecesarios. Esto permite una búsqueda y eliminación global de dichos comandos.

Resumen


El autor de este material dice que todo lo que habló aquí lo ayuda mucho a mantener la limpieza y la escalabilidad de la base del código de sus proyectos. Esperamos que encuentre útiles estos consejos.

Estimados lectores! ¿Qué podría agregar a los 12 consejos aquí para escribir código JS limpio y escalable?

Source: https://habr.com/ru/post/452562/


All Articles