多编程技术文章,请查阅IOKKS - 专业编程技术分享平台
这并不是什么秘密,开发人员必须思考众多日常任务。他们的一部分思考总是专注于各种例行事务,比如“我应该把这个函数放在哪个模块?”,“我应该如何命名这个变量?”,或者“这个变量应该做什么?”尽管这些问题看起来简单而琐碎,但它们一直在消耗着心智资源。然而,如果有办法通过自动化一些例行任务来简化这个过程,从而将注意力集中在更为关键的事务上,那么利用这些方法无疑是明智的。
在这种情况下,命名约定起着至关重要的作用。了解并明智地应用这些约定有助于增强代码的可读性,简化理解,并减轻开发人员的认知负担。然而,并非所有开发人员都清楚并且有时会忘记如何正确应用这些约定,盲目地将他们的注意力转移到看似更为关键的事务上。这反过来使他们的代码的可读性和理解变得复杂,使表面上“更为关键”的任务比实际上可能更为复杂。
以下是我有机会参与的一个真实项目的截图。请注意每个导入。根据名称,尝试猜测 我们可以从每个导入中获得什么项目。
在本文中,我们将探讨其中大部分内容,学习或者提醒自己在编写构造时应该遵循哪些原则。这将有助于你和未来与你的代码一起工作的任何人,无论是你的团队还是你自己 在一段时间后,避免猜测 并增强你的代码的可读性。
本文将包括一系列命名建议。我不希望你将它们视为唯一的真理。首先,这并不完全正确,因为大多数建议都带有一项免责声明,暗示这些约定本身并不涵盖所有情况。其次,虽然大多数建议在 JavaScript 社区中得到了接受,但并非所有人都出于各种原因遵循它们,有些建议被部分或者显著修改以适应特定团队。
本文的主要观点是,每个项目都应该建立自己的命名约定,无论它们是否与广泛采用的约定一致,还是独特于你的团队。关键的一点是要有约定并且遵守它们。
在互联网上,有大量关于最佳命名实践的文章。众所周知的约定强调,名称应该是简短的,更重要的是,可理解的 。名称应该具体,与其使用的上下文相对应,并且一目了然地传达代码的含义。实际上,这些约定不仅可以而且应该不仅在使用 JavaScript 时,而且在使用任何编程语言时都应该被应用。在这些通用规则中,我想重点关注一些约定中的特定细节,这些约定是广为人知的,但经常被遗忘或者没有完全遵守。
统一性 。在 JavaScript 项目中,有许多约定,但最有价值的约定是在单个项目中保持一致的编码风格。参与不同项目的个人最清楚不过,编写风格在不同项目之间可能会有显著的差异。
通常会出现这样的情况,即应用的不同部分由不同团队开发,或者在其开发中使用不同的技术。根据所选择的编程语言或者开发团队的偏好,命名约定可能会有所不同。尽管存在这些差异,应用的不同部分需要相互交互,比如前端和后端之间的数据交换。例如,从后端请求的数据的命名风格可能与前端代码中使用的风格不同。
尽管 JavaScript 语言的约定是使用 camelCase,但值得注意的是,并没有阻止使用,例如,snake_case。正如前面提到的,这种表示法对许多其他编程语言和一些团队来说可能更为熟悉。可能会出现这样的情况,即与你的应用程序风格不同的命名风格开始渗入你的代码。
我们无权判断使用不同的命名风格是好是坏,因为各种原因可能推动这样的选择。然而,明确地混合两种或更多种命名风格可能会带来挑战。对于加入项目的新开发人员来说,理解正在发生的事情并决定使用哪种命名风格可能会变得非常具有挑战性。
// 不好
const hasAccess=checkHasUserAccess(session.current_user) && checkIsPageVisible(session.current_page)
// 好
const hasAccess=checkHasUserAccess(session.currentUser) && checkIsPageVisible(session.currentPage)
// 如果偏好这种命名风格,也是好的
const has_access=check_has_user_access(session.current_user) && check_is_page_visible(session.current_page)
有几种方法可以解决这个问题,从使用 解构赋值、导入重命名、使用映射函数(例如 Array.prototype.map()),到更高级的方法,比如利用 适配器 设计模式。
关键在于采用一致的方法,不仅在命名约定上,而且在任务完成方式上也是如此。在可能的情况下,最好为特定操作选择一种方法。避免强迫自己和其他开发人员花费时间和额外的心智资源来决定使用哪种方法。考虑委托这样的任务,特别是与编码风格相关的任务,给工具。
仅限英文 。尽管在 JavaScript 中写作时可以使用非拉丁字母,但不建议这样做。即使你为自己编写某些东西,每一行代码都有“长寿”的能力,可能会有一天你需要与其他开发人员分享这些代码。对于其他开发人员,尤其是来自不同国家的开发人员,理解代码中发生的事情将会很困难。
在强调仅使用英文时,人们通常只考虑键盘布局。然而,人们不应忘记缩写和名称的一般使用。它们应该只与常用词一起使用,比如 idx、err、evt、xhr、src 等社区中历史上被接受的词。在其他情况下,强烈不建议使用缩写和词语的简写,因为它们经常会导致混淆,并且解密它们可能需要很长时间。
// 不好
const 新規ユーザーのウェルカムメッセージ='こんにちは'
const usrInf={ fName: 'John', lName: 'Doe' }
const isAdult=a >=18
// 好
const newUserWelcomeMessage='こんにちは'
const userInfo={ firstName: 'John', lastName: 'Doe' }
const isAdult=userAge >=18
还建议在代码编辑器中启用拼写检查器,它将突出显示单词中的语法错误。在许多编辑器中,默认情况下已启用,对于某些编辑器,你可能需要安装一个扩展,比如 VS Code 的 Code Spell Checker。今天的拼写检查器足够智能,大多数被广泛接受的缩写和词语的简写不会被标记为错误。
不仅仅是 camelCase 。许多资源强调在 JavaScript 中写作时专门 使用 camelCase 表示法。然而,这并不完全准确。建议更多地是关于遵循语言本身使用的表示法 。虽然 JavaScript 语言的大部分 是使用 camelCase 编写的,但并非完全如此。以下是一些例子:
parseInt('18') // camelCase
new RegExp('[A-Z]', 'i') // PascalCase
Number.MAX_SAFE_INTEGER // PascalCase + UPPER_SNAKE_CASE
正如我们所看到的,JavaScript 不仅包括 camelCase 表示法,还包括其他一些表示法。JavaScript 中的所有类和构造函数都是使用 PascalCase 表示法编写的。在声明自定义类和构造函数时,习惯上要遵循与语言本身相同的命名约定。对于表示固定值的常量也是如此。无论是 JavaScript 内置的常量还是开发人员创建的常量,通常都是以 UPPER_SNAKE_CASE 命名的。
与一些编程语言不同,其 API 使用各种表示法而没有明确的分布不同,JavaScript 的 API 并不受此问题的困扰。因此,遵循语言本身中存在的约定是惯例。
遵循 readme.md 。假设你正在使用像 axios 这样的网络请求库,并且该库的 README 建议使用标准名称 axios 进行导入。
import httpRequester from 'npm:axios'
// 在文件的底部的某个地方
httpRequester('https://example.com')
将名称从 axios 更改为 httpRequester 可能会导致混淆和错误,因为其他在项目中工作并期望标准名称 axios 的开发人员可能会遇到问题。因此,遵循库的 README 中设置的建议是非常重要的,以确保兼容性和代码理解。
乍一看,使用 axios 库的示例可能看起来无害,但类似的重命名场景可能会出现在更深度集成到项目中的更复杂的库和框架的使用中。采用有意义的重命名实践的团队将需要创建自己的文档,因为新团队成员在官方文档中找不到足够的信息来理解技术的使用风格的变化。
通常情况下,将更抽象的重命名用于与 依赖反转原则 结合使用。然而,理解这里的微妙之处是很重要的。你和你的团队必须清楚地了解你正在做什么以及为什么,因为在采用这样的实践时需要保持平衡。
不包含数据类型 。在变量名称中包含数据类型可能是诱人的,但屈服于这种诱惑通常会给名称增加不必要的语义负荷。例如,对于包含数组的变量使用名称 arr 可能会导致冲突,如果在其他地方使用相同名称的变量。此外,名称 arr 并不传达有意义的信息。使名称更具体,比如 userArr,是一种改进,但它并不能解决所有问题。
// 不好
const userNameMinLengthConst=3
const userObj=getUser()
const getUniqueUserNames=(arr)=> Array.from(new Set(arr))
// 好
const USER_NAME_MIN_LENGTH_COUNT=3
const user=getUser()
const getUniqueUserNames=(names)=> Array.from(new Set(names))
对于每种语言构造,无论是内置的还是自定义的数据结构,都存在一种传统的方式来指示值的预期数据类型。这体现在构造的名称中,它应该与社区内部和团队内部的约定一致。建议优先考虑社区约定,因为解释一些被广泛接受的东西通常比引入完全独特的东西更容易。在社区约定的情况下,你可以将某人引用到一篇互联网文章,而对于团队特定的约定,你可能需要自己编写这篇文章。
在本节中,我们将讨论数据类型和数据结构的特定命名约定。虽然 JavaScript 在其内置数据类型和结构方面可能显得有限,但它提供了广泛的功能。这些功能包括各种方法,在其他编程语言中,这些方法通常被分隔成不同的内置类型或数据结构。
例如,在 JavaScript 中,对象可以用作 enum、map(dictionary)、graph 等。JavaScript 中的 number 数据类型包括处理整数和浮点数。根据具体要求,JavaScript 的数据类型和结构可以灵活地适应使用,为 JavaScript 提供了多样性和强大性。
正是在这种情况下,命名约定的重要性变得明显。它们不仅增强了代码的可读性,还在与代码库的其他部分交互时防止混淆和错误。良好的命名约定确保了代码的清晰性,并有助于其他开发人员理解数据结构及其目的,从而促进团队内更有效的协作。
布尔值 。布尔值的名称应以肯定前缀开头,即前缀应回答“是”的问题。虽然有几个肯定的词(should、can、will 等)可以回答“是”,但建议优先选择最常见的两个词——is 和 has。虽然使用其他肯定词不会被视为错误,但应将其视为例外情况,如果可能的话,最好避免使用它们。
这一约定的一个重要补充,经常被忽视的是,肯定前缀不应包括否定。其背后的原因是,否定运算符(!)最常用于布尔值。因此,一个命名为 isNotAllowed 的值,应用了否定 !isNotAllowed,可能会相当误导。不相信吗?那就试着快速弄清楚 !!isNotAllowed(双重否定)等于什么。即使你能迅速做到,想象一下在整个文件中散布着所有这些否定反转的真实代码中工作。跟踪这样的逻辑,尤其是当有很多逻辑时,可能会相当具有挑战性。在这种情况下,最好通过使用名称中的积极肯定来改变评估布尔变量的逻辑。
函数和方法 。函数/方法的名称应该是一个动词,并对应它执行的动作。
尽管函数/方法的命名约定乍看起来可能很简单,但它们的命名是最有例外和其他约定的。对于大多数函数和方法,主要约定是名称应该是一个动词,并对应它们执行的动作。
集合和迭代器 。命名约定如下——如果使用了迭代器或索引集合(例如 Array、NodeList、FileList 等),名称应该是一个复数名词。否则,如果使用了键控集合(Set、Map),并且我们只对可以通过键获得的值感兴趣(通常使用 Array.from() 或扩展语法),命名约定保持不变——名称应该是一个复数名词。但是,如果键对我们也很重要,那么这样的集合应该使用单数名词命名,并在名称末尾添加表示一组某物的前缀之一。例如,Collection、List、Group 等。
类 。在处理类时,遵循几个约定是很重要的。以下是列表:
常量 。用于描述在程序执行之前已知且在执行过程中不应更改的值。
关于命名约定,开发人员之间有一个重要的共识:常量的名称应使用 UPPER_SNAKE_CASE 表示法。
枚举 。在 JavaScript 中,这种数据结构用于枚举一组固定值。
与许多其他编程语言具有枚举的单独数据类型不同,JavaScript 没有这样的数据类型(至少目前没有)。因此,为了在 JavaScript 中模拟枚举,可以使用一个普通对象,但需要遵循特定的命名约定。以下是这些命名约定的列表:
由于 TypeScript 现在是 JavaScript 开发的重要组成部分,值得一提的是 TypeScript 具有用于枚举的 enum 关键字。如果决定与团队一起使用 TypeScript 的枚举,惯例是遵循相同的命名约定。
地图 。也被称为字典数据结构。这种数据结构用于将一个值映射到另一个值。
地图是任何编程语言中非常有用且经常使用的数据结构。在JavaScript世界中,地图有特定的命名约定。名称应遵循aToB的模式,其中a作为从地图中检索值的键,后跟介词To,暗示着两个事物的映射,然后是B,它是a的映射值。
// 不好的
const userRolesReadable={
[UserRole.ADMIN]: '管理员',
[UserRole.GUEST]: '访客',
}
const REDIRECTING={
'/groups': '/admin-login',
'/profile': '/login',
}
const UserPermissions={
[UserRole.ADMIN]: [Permission.MANAGE_USERS, Permission.MANAGE_GROUPS],
[UserRole.GUEST]: [Permission.EDIT_PROFILE],
}
// 好的
const userRoleToReadable={
[UserRole.ADMIN]: '管理员',
[UserRole.GUEST]: '访客',
}
const pagePathToRedirectPath={
'/groups': '/admin-login',
'/profile': '/login',
}
const userRoleToPermissions={
[UserRole.ADMIN]: [Permission.MANAGE_USERS, Permission.MANAGE_GROUPS],
[UserRole.GUEST]: [Permission.EDIT_PROFILE],
}
JavaScript中有一个内置的Map类。与常规对象的主要区别在于能够使用任何数据类型(甚至是对象)作为键。通常情况下,使用原生的Map类来创建地图作为数据结构是多余的。但是,如果您需要原生Map提供的功能,可以使用它来创建地图数据结构。在大多数情况下,常规对象就足够了。
类型和接口 。如前所述,TypeScript已成为当今JavaScript开发中不可或缺的一部分。一般来说,在大多数情况下,类型和接口是可以互换的。然而,由于已经有足够的讨论关于选择哪种,本文将专注于命名。对于类型和接口,存在以下命名约定:
// 不好的
type TUser={
firstName: string
lastName: string
}
interface user {
firstName: string
lastName: string
}
interface userServiceInterface {
findByEmail: (email: string)=> User
}
// 好的
type User={
firstName: string
lastName: string
}
interface User {
firstName: string
lastName: string
}
interface UserServiceContract {
findByEmail: (email: string)=> User
}
// 也可以
interface IUserService {
findByEmail: (email: string)=> User
}
重要的是要意识到,尽管本文对JavaScript命名约定进行了广泛的概述,但它无法涵盖所有可能的情况。即使在提出的观点中,也应考虑许多例外情况。命名过程虽然基础,但意味着在应用中灵活性,考虑到每个项目的独特特点和要求。
在开发过程中,我们经常深入复杂的技术细节,忘记了力量常常隐藏在细节中。多年的开发经验只能证实这样一个事实,即对细节的关注,比如命名,对于创建高效和可读的代码起着至关重要的作用。简洁和无需思考如何命名一个值以及如何快速理解它所包含的内容,为开发人员的日常工作带来了难以置信的轻松。
如果一个开发人员的变量没有被清晰命名,他们能否认为自己是强大的?清晰的命名不仅使代码对其他开发人员更易理解,也使程序员自己更易理解,从而使开发过程更高效,减少错误。
经常发生的情况是,寻找糟糕的命名会导致发现糟糕的代码。这只强调了命名约定作为代码质量指标的重要性。适当的命名反映了对细节的关注,进而表明了对开发的细致态度。
当应用本文讨论的约定时,从前言部分导入值的方式看起来更清晰了,不是吗?我相信现在理解每个导入的含义将需要您付出更少的努力。
在结论中,重要的是要记住,虽然命名标准很重要,但它们并不是绝对的。每个项目都有其特殊性,定义自己的约定至关重要。最重要的是它们应该存在,并且您应该遵循它们,以确保代码的一致性并提高集体生产力。此外,如果可能的话,尝试将代码风格问题委托给诸如ESLint之类的工具,以简化和增强开发过程。
SS命名规范:
喜欢的给作者点个关注哦,想要学习资料的私聊
TML 符号实体
HTML 符号是不呈现在标准的键盘上,比如数学运算符、箭头符号、技术符号和形状。
如需向 HTML 页面添加这些符号,您可以使用 HTML 实体名称。
如果不存在实体名称,您可以使用实体编号。
如果字符没有实体名称,您可以使用十进制(或十六进制)引用。
实例
<p>我将显示 €</p>
<p>我将显示 €</p>
<p>我将显示 €</p>
结果如下:
我将显示
我将显示
我将显示
HTML 支持的数学符号
如果您使用的是一个 HTML 实体名称或一个十六进制编号,字符总是能正确显示。
这是与您页面使用的字符集相互独立的!
字符 | 编号 | 实体 | 描述 |
---|---|---|---|
? | ∀ | ∀ | 所有(for all) |
? | ∂ | ∂ | 部分(part) |
? | ∃ | ∃ | 存在(exists) |
? | ∅ | ∅ | 空(empty) |
? | ∇ | ∇ | 倒三角(nabla) |
∈ | ∈ | ∈ | 属于(isin) |
? | ∉ | ∉ | 不属于(notin) |
? | ∋ | ∋ | 包含的成员(ni) |
∏ | ∏ | ∏ | 连乘(prod) |
∑ | ∑ | ∑ | 总和(sum) |
完整的数学(Math)参考手册
HTML 支持的希腊字母
字符 | 编号 | 实体 | 描述 |
---|---|---|---|
Α | Α | Α | Alpha(中文注音:阿耳法) |
Β | Β | Β | Beta(中文注音:贝塔) |
Γ | Γ | Γ | Gamma(中文注音:伽马) |
Δ | Δ | Δ | Delta(中文注音:德耳塔) |
Ε | Ε | Ε | Epsilon(中文注音:艾普西隆) |
Ζ | Ζ | Ζ | Zeta(中文注音:截塔) |
完整的希腊(Greek)参考手册
HTML 支持的其他实体
字符 | 编号 | 实体 | 描述 |
---|---|---|---|
? | © | © | 版权所有(REGISTERED SIGN) |
? | ® | ® | 注册商标(REGISTERED SIGN) |
| € | € | 欧元符号(EURO SIGN) |
? | ™ | ™ | 商标(trademark) |
← | ← | ← | 向左箭头(LEFTWARDS ARROW) |
↑ | ↑ | ↑ | 向上箭头(UPWARDS ARROW) |
→ | → | → | 向右箭头(RIGHTWARDS ARROW) |
↓ | ↓ | ↓ | 向下箭头(DOWNWARDS ARROW) |
? | ♠ | ♠ | 黑桃(BLACK SPADE SUIT) |
? | ♣ | ♣ | 黑梅花(BLACK CLUB SUIT) |
? | ♥ | ♥ | 黑心(BLACK HEART SUIT) |
? | ♦ | ♦ | 黑方块(BLACK DIAMOND SUIT) |
如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!
*请认真填写需求信息,我们会在24小时内与您取得联系。