【译】12个编写干净且可扩展的JavaScript技巧

JavaScript起源于早期的网络。 从做为脚本语言开始,到如今它已经发展成为一种彻底成熟的编程语言,而且支持服务器端执行。javascript

现代Web应用程序严重依赖JavaScript,尤为是单页应用程序(SPA)。借助于React,AngularJS和Vue.js等新兴框架,Web应用程序主要使用JavaScript构建。前端

扩展这些应用程序有时候会比较棘手,经过简单的设置,您最终可能会遇到限制并迷失在混乱的海洋中。我想分享一些小技巧,这些技巧将帮助您以有效的方式编写干净的代码。java

本文面向任何技能水平的JavaScript开发人员。 可是,至少具备JavaScript中级知识的开发人员将从这些技巧中获益最多。react

原文连接:blog.logrocket.com/12-tips-for…git

分隔您的代码

我建议保持代码库清洁和可读的最重要的事情是具备按主题分隔的特定逻辑块(一般是函数)。若是你编写一个函数,该函数应该默认只有一个目的,不该该一次作多个事情。github

此外,您应避免引发反作用,这意味着在大多数状况下,您不该更改在函数外声明的任何内容。 您将数据接收到带参数的函数中;其余一切都不该该被访问。若是您但愿从函数中获取某些内容,请返回新值。npm

模块化

固然,若是以相似的方式使用这些函数或执行相似的操做,您能够将多个函数分组到一个模块(and/or 的类中)。例如,若是要进行许多不一样的计算,请将它们拆分为能够连接的独立步骤(函数)。可是,这些函数均可以在一个文件(模块)中声明。 如下是JavaScript中的示例:编程

function add(a, b) {
    return a + b   
}

function subtract(a, b) {
    return a - b   
}

module.exports = {
    add,
    subtract
}

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

console.log(subtract(5, add(3, 2))
复制代码

若是您正在编写前端JavaScript,请务必使用默认导出做为最重要的项目,并为次要项目命名导出。数组

多个参数优先于单个对象参数

声明一个函数时,您应该老是喜欢多个参数而不是一个指望对象的参数:服务器

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

// BAD
function displayUser(user) {
    console.log(`This is ${user.firstName} ${user.lastName}. She is ${user.age} years old.`)
}
复制代码

这背后的缘由是,当您查看函数声明的第一行时,您能确切知道须要传递给函数的内容。

尽管函数应该受到限制 - 只作一项工做 - 可是它可能会变得更大。在函数体中扫描须要传递的变量(嵌套在对象中)将花费更多时间。有时,使用整个对象并将其传递给函数彷佛更容易,但为了扩展应用程序,此设置确定会有所帮助。

在某种程度上,声明特定参数没有意义。对我来讲,它超过四个或五个功能参数。若是你的函数变大,你应该转向使用对象参数。

这里的主要缘由是参数须要以特定顺序传递。 若是您有可选参数,则须要传递undefined或null。 使用对象参数,您能够简单地传递整个对象,其中顺序和未定义的值可有可无。

解构(Destructuring)

解构是ES6引入的一个很好的工具。它容许您从对象中获取特定字段并当即将其分配给变量。 您能够将它用于任何类型的对象或模块。

// EXAMPLE FOR MODULES
const { add, subtract } = require('./calculations')
复制代码

只导入您须要在文件中使用的函数而不是整个模块,而后从中访问特定的函数。 一样,当您肯定您确实须要一个对象做为函数参数时,也可使用destructuring。 这仍将为您提供函数内所需内容的概述:

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功能组件。)

使用默认值

解构的默认值甚至基本函数参数都很是有用。首先,它们为您提供了一个能够传递给函数的值的示例。其次,您能够指出哪些值是必需的,哪些值不是。使用前面的示例,该函数的完整设置以下所示:

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',
})
复制代码

显然,有时您可能不想使用默认值,而是在未传递值时抛出错误。 然而,这一般是一个方便的技巧。

数据稀缺性

前面的技巧引出了一个结论:最好不要传递您不须要的数据。一样,在设置函数时,这可能意味着更多的工做。可是,从长远来看,它确定会为您提供更具可读性的代码库。确切地知道在特定位置使用哪些值是很是有价值的。

行数和缩进限制

我见过大文件 - 很是大的文件。实际上,超过3,000行代码。在这些文件中查找逻辑块是很是困难的。

所以,您应该将文件大小限制为必定数量的行。我倾向于将个人文件保存在100行代码之下。 有时候,很难分解文件,它们会增加到200-300行,在极少数状况下会增长到400行。

超过此临界值,意味着文件太杂乱,难以维护。随意建立新的模块和文件夹。您的项目应该看起来像一个森林,由树(模块部分)和分支(模块和模块文件组)组成。避免试图模仿阿尔卑斯山,在密闭区域堆积代码。

相比之下,你的实际文件应该看起来像Shire,这里和那里都有一些山丘(小水平的缩进),但一切都相对平坦。 尽可能将压痕水平保持在四级如下。

也许为这些提示启用eslint规则是有帮助的!

使用prettier

在团队中工做须要清晰的样式指南和格式。ESLint提供了一个巨大的规则集,您能够根据本身的需求进行自定义。还有 eslint--fix,它能够纠正一些错误,但不是所有。

相反,我建议使用Prettier格式化代码。这样,开发人员没必要担忧代码格式化,而只需编写高质量的代码。 外观将一致而且格式自动化。

使用有意义的变量名

理想状况下,应根据其内容命名变量。 如下是一些有助于您声明有意义的变量名称的指南。

函数

函数一般执行某种操做。为了解释这一点,人类使用动词 - 转换或显示,例如。在开头用动词命名函数是个好主意,例如convertCurrencydisplayUserName

数组

这些一般会包含一系列项目; 所以,将s附加到变量名称。 例如:

const students = ['Eddie', 'Julia', 'Nathan', 'Theresa']
复制代码

布尔

简单地说就是尽可能多接近于天然语言,这样好理解。你会问“这我的是教师吗?”→“是”或“否”。一样:

const isTeacher = true // OR false
复制代码

数组函数

forEach, map, reduce, filter等是很好的原生JavaScript函数,用于处理数组和执行某些操做。 我看到不少人只是将elelement做为参数传递给回调函数。 虽然这很简单快捷,但您还应根据其值来命名。 例如:

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

标识

一般,您必须跟踪特定数据集和对象的ID。当嵌套id时,只需将其保留为id便可。在这里,我喜欢在将对象返回到前端以前将MongoDB _id映射到 id。从对象中提取id时,请预先添加对象的类型。例如:

const studentId = student.id
// OR
const { id: studentId } = student // destructuring with renaming
复制代码

该规则的一个例外是模型中的MongoDB引用。 在这里,只需在引用的模型以后命名字段便可。 这将在填充参考文档时保持清晰:

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

尽量使用async / await

在可读性方面,回调是最糟糕的 - 特别是在嵌套时。Promises是一个很好的改进,但在我看来,async / await具备最好的可读性。即便对于初学者或来自其余语言的人来讲,这也会有很大帮助。可是,请确保您了解其背后的概念,而且不要盲目地在任何地方使用它。

模块导入顺序

正如咱们在技巧1和2中看到的那样,将逻辑保持在正确的位置是可维护性的关键。 一样,导入不一样模块的方式能够减小文件中的混淆。 我在导入不一样模块时遵循一个简单的结构:

// 3rd party packages
import React from 'react'
import styled from 'styled-components'

// Stores
import Store from '~/Store

// reusable components
import Button from '~/components/Button'

// utility functions
import { add, subtract } from '~/utils/calculate'

// submodules
import Intro from './Intro'
import Selector from './Selector'
复制代码

我在这里使用了React组件做为示例,由于有更多类型的导入。 您应该可以根据您的具体用例进行调整。

摆脱控制台

console.log 是一种很好的调试方式 - 很是简单,快速,完成工做。显然,有更复杂的工具,但我认为每一个开发人员仍然使用它。若是您忘记清理日志,您的控制台最终将陷入巨大的混乱。而后,您确实要在代码库中保留日志; 例如,警告和错误。

要解决此问题,您仍然可使用console.log 进行调试,但对于持久日志,请使用loglevelwinston等库。此外,您可使用ESLint警告控制台语句。这样你就能够轻松地全局查找console... 并删除这些语句。

遵循这些指导原则确实帮助我保持代码库的清洁和可扩展性。 你以为还有什么提示特别有用的吗?请在评论中告诉我您在编码工做过程当中值得推荐的内容,并请分享您用于帮助代码结构的任何其余提示!谢谢~

若是以为文章对你有些许帮助,欢迎在个人GitHub博客点赞和关注,感激涕零!

相关文章
相关标签/搜索