编写干净且可扩展的JavaScript代码:12个技巧

JavaScript来自早期的网络。 首先,在其上编写了简单的脚本,以“激活”网站页面。 现在,JS已成为一种成熟的编程语言,甚至可以用于开发服务器端项目。

现代Web应用程序严重依赖JavaScript。 对于单页应用程序(单页应用程序,SPA)尤其如此。 随着诸如React,Angular和Vue之类的库和框架的出现,JavaScript已成为Web应用程序的主要构建块之一。



扩展此类应用程序,无论是有关其客户端还是服务器部分,都是一项非常困难的任务。 如果这样的应用程序是基于一个考虑周全的体系结构,那么它们的开发人员迟早会面临某些限制。 他们淹没在令人不愉快的惊喜中。

我们今天翻译的文章的作者希望分享有关编写纯JavaScript代码的技巧。 他说,本文适用于任何技能水平的JS程序员。 但对于至少在中级水平上熟悉JavaScript的人来说,它将特别有用。

1.代码隔离


为了使项目的代码库保持整洁以使代码易于阅读,建议根据目的将代码片段分成单独的块。 这些块通常是功能。 我认为这项建议是我可以提供的最重要的建议。 如果要编写函数,则必须立即关注该函数旨在解决单个问题的事实。 该功能不应设计为解决几个问题。

此外,您应努力确保函数调用不会导致副作用。 在大多数情况下,这意味着该函数不应更改在其外部声明的内容。 通过参数接收数据。 她不应该和其他人一起工作。 您需要使用return关键字return任何函数。

2.将代码分解为模块


可以将以类似方式使用或执行类似操作的功能归为一个模块(或者,如果需要,可以在单独的类中)。 假设您需要在项目中进行各种计算。 在这种情况下,以单独的函数(隔离的块)的形式表示这种计算的不同阶段是有意义的,这些函数的调用可以组合成链。 但是,所有这些功能都可以在单个文件(即模块)中声明。 这是包含类似功能的calculation.js模块的示例:

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

这是在另一个文件中使用此模块的方式(我们将其称为index.js ):

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

可以为前端应用程序开发人员提供以下建议。 要导出模块中声明的最重要的实体,请使用默认的导出选项。 对于辅助实体,可以使用命名的导出。

3.使用多个功能参数,而不要使用带有参数的单个对象


声明函数时,应努力使用多个参数,而不要使用带有参数的单个对象。 以下是几个示例:

 //  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.`) } 

具有多个参数的函数的存在使您可以通过查看其声明的第一行立即查看需要传递给它的内容。 这正是我给出此建议的原因。

尽管事实上在开发函数时,您仍需要努力确保每个函数只能解决一个问题,但是函数代码的大小可能会很大。 如果一个函数接受带有参数的单个对象,那么为了准确地找到它的期望,您可能需要查看所有代码,并花大量时间在上面。 有时似乎在使用函数时,使用带有参数的单个对象要容易得多。 但是,如果编写函数,则考虑到将来可能会扩展应用程序,最好使用几个参数。

应该注意的是,在某些限制之后,使用单个参数将失去其意义。 就我而言,这些是4到5个参数。 如果一个函数需要这么多的输入,那么程序员应该考虑使用带有参数的对象。

提出此建议的主要原因是,必须以特定顺序将函数期望的各个参数传递给它。 如果某些参数是可选的,那么您需要传递undefinednull类的函数。 当使用带有参数的对象时,对象中参数的顺序无关紧要。 使用这种方法,您无需将可选参数设置为undefined

4.重组


重组是ES6中出现的一种有用的机制。 它允许您从对象中提取指定的字段,然后立即将它们写入变量。 在处理对象和模块时可以使用它:

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

特别是在使用模块时,有意义的是将文件(而不是整个模块)导入文件,而仅将必需的功能导入文件,使它们易于理解。 否则,您将必须使用符号化模块的变量来访问函数。

类似的方法适用于将单个对象用作功能参数的情况。 这使您可以查看函数的第一行,以立即找出它希望作为带有参数的对象确切接收的内容:

 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', }) 

如您所见,尽管该函数接受带有参数的单个对象,但通过对其进行分解,您可以找出调用该函数时确切需要放置在其中的内容。 下一个技巧是如何更准确地告诉用户该功能的期望。

顺便说一下,在使用React的功能组件时也可以使用重组。

5.设置功能参数的默认值


函数参数的标准值,参数的默认值,在用参数解构对象时,以及在函数接受参数列表的情况下,使用它是有意义的。 首先,它为程序员提供了可以传递哪些功能的示例。 其次,它允许您找出哪些参数是必需的,哪些是可选的。 我们用标准参数值补充上一个示例中的函数声明:

 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', }) 

显然,在某些情况下,如果在调用函数时未将函数参数传递给它,则有必要给出错误,而不是使用该参数的标准值。 但是,通常,这里描述的技术很有帮助。

6.不要将不必要的数据传递给函数


先前的建议使我们得出了一个有趣的结论。 它包含以下事实:函数不需要传输不需要的数据。 如果遵循此规则,则功能开发可能需要更多时间。 但是从长远来看,这种方法将导致形成以良好可读性为特征的代码库。 另外,知道程序的每个特定位置使用了哪种数据非常有用。

7.限制文件中的行数和最大代码嵌套级别


我看过带有程序代码的大文件。 很大 有些拥有3,000多个线路。 在此类文件中,导航非常困难。

因此,建议限制以代码行为单位的文件大小。 我通常会努力确保文件大小不超过100行。 有时,当很难将某些逻辑分解成小片段时,我的文件大小达到200-300行。 而且,它们的大小很少达到400行。 超出此限制的文件很难读取和维护。

在项目工作过程中,大胆地创建新的模块和文件夹。 项目结构应类似于由树木(模块和模块文件的组)和分支(模块的部分)组成的森林。 努力确保您的项目看起来不像山脉。

如果我们用代码来讨论文件本身的外观,那么它们应该类似于低丘地形。 这是关于避免大量的代码嵌套。 值得努力确保代码的嵌套不超过四个级别。

也许遵守这些建议将有助于应用适当的ESLint linter规则。

8.使用工具自动格式化代码


在团队中处理JavaScript项目时,您需要为代码的样式和格式制定清晰的指南。 您可以使用ESLint自动执行代码格式化。 这套lint为开发人员提供了大量可定制的规则。 有一个eslint --fix可以修复某些错误。

但是,我建议使用Prettier而不是ESLint来自动执行代码格式化。 使用这种方法,开发人员可能不必担心代码格式。 他只需要编写高质量的程序。 使用一组规则自动格式化的所有代码将看起来一致。

9.使用精心设计的变量名


变量的名称在理想情况下应反映其内容。 这是一些选择信息性变量名称的准则。

▍功能


通常,函数执行某种动作。 人们在谈论动作时会使用动词。 例如-转换(转换)或显示(显示)。 建议形成函数名称,以便它们以动词开头。 例如, convertCurrencydisplayUser

▍数组


数组通常包含一些值的集合。 因此,将字母s添加到存储数组的变量的名称是有意义的。 例如:

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

▍逻辑值


布尔变量名称以ishas开头is有意义的。 这使它们更接近普通语言可用的结构。 例如,这是一个问题:“那个人是老师吗?”。 答案可以是“是”或“否”。 您可以通过为逻辑变量选择名称来做同样的事情:

 const isTeacher = true //  false 

passed传递给标准数组方法的函数参数


以下是一些标准的JavaScript数组方法: forEachmapreducefilter 。 它们允许您对数组执行某些操作。 它们是描述数组操作的传递函数。 我看到有多少程序员简单地将名称如elelement参数传递给此类函数。 尽管这种方法使程序员无需考虑命名此类参数,但最好根据其中显示的数据来调用它们。 例如:

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

▍标识符


通常,程序员需要使用某些数据集或对象的标识符。 如果嵌套了此类标识符,则无需对其进行任何特殊处理。 例如,在使用MongoDB时,通常在将对象返回到前端应用程序之前将_id转换为id 。 从对象提取标识符时,建议通过在id之前设置对象的类型来形成其名称。 例如:

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

该规则的一个例外是在模型中使用MongoDB引用。 在这种情况下,建议根据字段中引用的模型来命名字段。 在填写字段中有链接的文档时,这将使代码保持整洁统一:

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

10.尽可能使用async / await构造。


使用回调会降低代码的可读性。 对于嵌套回调尤其如此。 答应了一下,但我认为最好使用async / await构造的代码。 即使是从其他语言转向JavaScript的初学者和开发人员也可以轻松理解此代码。 这里最重要的是掌握异步/等待的基础概念。 由于其新颖性,请不要在任何地方使用此设计。

11.导入模块的过程


建议1和2证明了选择正确的代码存储位置以确保代码受支持的重要性。 类似的想法适用于模块的导入顺序。 即,我们正在谈论的是导入模块的逻辑顺序使代码更清晰的事实。 导入模块时,我遵循以下简单方案:

 //    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' 

这个例子基于React。 相同的想法将不会很难转移到任何其他开发环境中。

12.避免使用console.log


console.log命令是用于调试程序的简单,快速且方便的工具。 当然,有这种更高级的工具,但是我认为几乎所有程序员仍在使用console.log 。 如果使用console.log进行调试,而没有及时删除该命令的调用,那么控制台很快就会变得混乱。 应该指出的是,即使在完全准备工作的项目代码中,也保留一些日志记录团队是有意义的。 例如,显示错误消息和警告的命令。

结果,我们可以说可以将console.log用于调试目的,并且在计划将日志命令用于工作项目的情况下,诉诸专用库是有意义的。 其中有loglevelwinston 。 另外,ESLint可以用来对抗不必要的日志记录命令。 这允许全局搜索和删除此类命令。

总结


该材料的作者说,他在这里谈论的所有内容都对他保持项目代码库的整洁性和可伸缩性有很大帮助。 我们希望您发现这些技巧有用。

亲爱的读者们! 您可以在此处编写干净且可扩展的JS代码的12条提示中添加哪些内容?

Source: https://habr.com/ru/post/zh-CN452562/


All Articles