1. 概述

本规范中关于各个规则有 必须应该,可以,不应该,不能 五种叙述。

必须 不能 为强制规定
应该 不应该 表达了强烈建议,如果没有其他强烈的原因,则应当遵守
可以 表达了轻微建议,开发者选择性遵守

在公司所有 TypeScript/JavaScript 项目中,各种变量和函数名称 应该 使用 驼峰命名 大驼峰命名 (包括它们的变体),在一些特殊情况下 可以 使用 下划线大写命名。 其他命名法(匈牙利,下划线小写等)皆 不能 使用。

注意:在一些应用场景下,前置下划线是允许使用的,参见 #3.3 使用前置下划线加强私有性

1.1 命名法定义

1.1.1 驼峰命名 camelCase

驼峰命名法有如下规则:

  • 满足正则表达式 /[a-z$][a-zA-Z0-9$]*/
  • 第一个单词全部小写
  • 第二个及以后 单词 的第一个字母大写,其余字母小写
  • 数字看作独立单词
  • 变体:在一些场景下可在名称前加下划线,表明其私有性。即 /_[a-z$][a-zA-Z0-9$]*/

正确举例:

  1. var dialog, mailBox, user1, user1Data
  2. var htmlData, getUrl, someHttpApi, oauthComplete // 不论是否缩写单词,都满足上述规定
  3. var _someLocalVariable // 额外加上下划线表明私有性

错误举例:

  1. var Data, 1user // 错误,应以小写字母开头
  2. var _User // 错误, 在变体规则中,下划线不影响首字母规则,应改为 _user

1.1.2 大驼峰命名(帕斯卡命名)PascalCase

与驼峰命名类似,有如下规则:

  • 满足正则表达式 /[A-Z$][a-zA-Z0-9$]*/
  • 所有 单词 的第一个字母大写,其余字母小写
  • 数字看作独立单词
  • 变体:(比较罕见)在一些场景下可以在名称前加下划线,表明其私有性。即 /_[A-Z$][a-zA-Z0-9$]*/

正确举例:

  1. var MailBox, Dialog, UserInfo
  2. var HtmlData, GetUrl, SomeApi, DomHtmlApi // 不论是否缩写单词,都满足上述规定
  3. var _MailBox, _HTMLData // 额外加上下划线表明私有性(比较罕见)

错误举例略去,可参考 #1.1.1 驼峰命名部分

1.1.3 下划线大写命名 UNDERLINE_ALL_UPPERCASE

下划线大写命名有如下规则:

  • 满足正则表达式 /[A-Z$][A-Z0-9$_]*/
  • 所有字母大写
  • 所有 单词 使用下划线分割
  • 数字看作独立单词
  • 变体:(比较罕见)在一些场景下可以在名称前加下划线,表明其私有性。即 /_[A-Z$][A-Z0-9$_]*/

正确举例:

  1. const PAGE_SIZE, API_HOST, CONTENT_MAX_LEN
  2. const _SIZE // 额外加上下划线表明其私有性(比较罕见)

错误举例略。

1.2 词性定义

计算机程序命名领域所使用的词汇和日常使用的英语习惯上有差异,在各种场合下往往有某些特定的喜好。
下面列出的词性分类并非完全按照英语的分类,而是结合了计算机程序的命名习惯。

1.2.1 动词

特指一般动词而不包括限定动词。比如 go,send,generate,make,listen 等
动词多用作函数名称

1.2.2 名词

名词指代一个具体的对象的名称,可以是:

  • 单个名词(包括单复数,缩写名词算作一个名词):user, api, books, messages
  • 复合名词(多个词语直接组合而成的名词):aliCDN,vipUser,modifyUserAPI(尽管modify是动词,但是在这里它构成了名称的一部分,而不描述一个动作,因此看作复合名词的一个部分)
  • 带有前置或后置定语的单个/复合名词:deletedItem, redBook, bookAbove, bookWithCover 等

名词多用作变量名称

1.2.3 形容词

形容词描述对象的某个特征,比如所具备的属性(例如颜色)或所属的状态(是否激活),可以是:

  • 一般形容词:red, active, primary
  • 一般动词过去分词做形容词:disabled, faved, enabled, completed

形容词多用作布尔类型变量名称,可作为名词的定语,也可参与构成描述性肯定短句

1.2.4 限定动词

限定动词指不描述特定具体动作,而是表达“是否”,“能否”,“会否”等限定意味的动词,常见的包括:

  • is, am, are, be 等 be动词,以及他们的过去式(was, were)
  • not 和 no 等用于表达否定意味的词汇
  • will, may, can, would 等情态动词 用于表达“能够”,“将会” 等意思
  • has, being 等构成特定时态表达的词语

限定动词不会单独使用,而是和名词或形容词构成描述性肯定短句使用

1.2.5 描述性肯定短句

命名时常有单用名词或形容词难以表达的情况,此时可以使用描述性肯定短句。
常见的表达有:

  • [主语名词] + 限定动词 + 形容词 :常描述一个对象是否具有某个特征
    • 例如 isActive, btnIsDisabled, isEmpty, numbersAreAllowed
  • [主语名词] + 限定动词 + 名词 :常描述一个对象是否属于某个类型
    • 例如 nodeIsElement, isURL, notURL, noName
  • [主语名词] + 限定动词 + 动词 + [宾语名词] :常描述一个对象的行为是否处于某个状态
    • 例如 socketBeingUsed, isSending,reqHasFinished, dialogWillClose, taskCanStart, canAcceptNumber

描述性肯定短句多用做布尔类型变量名称,或检查函数名称。

1.2.6 动作命令短句

命名时常有一个动词无法描述的动作,此时可以使用动作命令短句。
常见的表达有:

  • 动词 + 名词 :如 createElement, addListener, sendFetchUserRequest
  • 动词 + [名词] + 形容词 :常表达将某个对象修改为某状态的操作,如 setBtnActive, markUsed

上述表达还可以进一步添加后置定语,比如 createElementForSend,sendRequestAsAdmin 等
动作命令短句多用作函数名称


2. 命名标准

下面给出各类名称的命名标准。

2.1 变量和常变量 Variables

变量指:

  • 使用 letvar 关键字声明的 TypeScript/JavaScript 变量,但不包括:
    • 存储了函数引用的变量(其命名规范遵从 #2.3 函数 部分)
    • 存储了类引用的变量(其命名规范遵从 #2.4 类和构造函数 部分)
    • 存储了枚举引用的变量(其命名规范遵从 #2.8 枚举 部分)
    • 存储了外部依赖引用的变量 (其命名规范遵从 #2.10 外部依赖 部分)
  • 中的 非函数 属性
  • 中的 非函数 属性Getter/Setter
  • 对象 中的 非函数 成员

常变量值:

  • 使用 const 声明的,在局部作用域中用于替代 let 使用的,值为 非函数 的所有 TypeScript/JavaScript 常量。

常变量通常用于替代普通变量使用,以防止意外的修改,增加代码的鲁棒性,和 #2.2 中的 常量 并非同一概念。

变量和常变量遵守相同命名规则,再本章节中不再区分,统一称为变量

变量从命名偏好上来说,可以分为如下几类:

  • 普通变量:存储了特定类型(bool类型除外)数据,表达特定含义的变量
  • 布尔变量:存储了 bool 类型数据。标志变量又可以分为如下两类
    • 状态标志:描述特定对象的状态,如 isActive, disabled 等
    • 二值数据:存储了某个二元状态数据的变量,比如标志数据,锁数据等,如 clickLock, debounceFlag 等
  • 临时变量:在狭小定义域中使用,用于接收参数,或临时存储数据以简化代码编写的变量。比如 for 循环中的 i 变量,list.filter 处理函数中的参数 item 等

普通变量命名应遵守如下约定:

  • 必须 使用 驼峰命名
  • 必须 名词
  • 应该 正确表达内容含义,不能 挂羊头卖狗肉
  • 应该 尽量避免名称的歧义
  • 不应该 过度抽象或使用单字母名称,以避免歧义和命名冲突

布尔变量命名应遵守如下约定:

  • 必须 使用 驼峰命名
  • 状态标志 必须 使用 形容词 描述性肯定短句,如 active, isDisabled
  • 二值数据 必须 使用 名词 ,且 应该 使用 flag(标志),lock(锁), switch(开关), mode(模式) 等符合惯例的名词

临时变量命名应遵守如下约定:

  • 必须 使用 驼峰命名
  • 必须 用于小规模局部定义域中
  • 必须 名词 单字母,如 item, i, s
  • 可以 使用抽象名词,如 key, val, item, str 等

正确命名举例:

  1. function someFunctionScope(){
  2. // 普通变量
  3. var item = 'a'
  4. let clickCnt = 1
  5. let userNameInput = document.getElementById('input')
  6. const alertDialog = getDialog()
  7. const targetURL = 'https://xxx.com' + '?a=1'
  8. const htmlStr = '<div></div>'
  9. const contentHTML = '<div></div>'
  10. const book = {
  11. name: 'SomeBook' // 对象成员遵从变量命名规则
  12. }
  13. class Book {
  14. name = 'SomeBook' // 类属性遵从变量命名规则
  15. get age () {return 1} // getter 遵从变量命名规则
  16. set age () {} //setter 遵从变量命名规则
  17. }
  18. // 布尔变量:状态标志
  19. var disabled = true
  20. let isActive = true
  21. var requestCompleted = true
  22. let valueIsEmail = true
  23. const userCreatedAt = Date.now()
  24. const willDeleteUser = true
  25. const canFly = true
  26. // 布尔变量:二元数据
  27. let submitFlag = true
  28. let clickLock = true
  29. let lazyMode = true
  30. let autoPlaySwitch = true
  31. // 临时变量
  32. list.forEach((i)=>{}) //使用单字母变量,因为结合上下文很容易知道其意义
  33. for (const key in obj) { //使用抽象名词key,因为结合上下文很容易知道其意义
  34. const val = obj[key] //使用抽象名词key,因为结合上下文很容易知道其意义
  35. }
  36. }

错误命名举例:

  1. function someFunctionScope(){
  2. var ITEM, user_name, contentHtml // 错误,未使用驼峰命名或大驼峰命名
  3. var user = true // 错误,名称没有正确表达含义
  4. var btnEl = '<button></button>' // 错误,名称没有正确表达含义,可改为 btnHTML
  5. var disable = true // 错误,不可以使用动词,可以改为 disabled
  6. var sendEmail = true // 错误,不可以使用动词开头短句
  7. // 可以改为 emailSent 或 sendEmailCompleted
  8. var createAt = Date.now() // 错误,语义有歧义,且不可以动词开头
  9. // 如果描述过去已创建的时间,应该改为 createdAt
  10. // 如果描述未来计划创建的时间,应该改为 willCreateAt
  11. }

2.2 常量 Constants

常量指:

  • 使用 const 关键字声明的,在较大定义域中用于指代一个固定值的 非函数 TypeScript/JavaScript 常量
  • 中以 readonly 关键字修饰的,用于指代一个固定值的 非函数 属性
  • 枚举 中使用的,用于指代一个固定值的 非函数 枚举成员

常量 应该区分 #2.1 中使用 const 来声明的 常变量 常量绝大多数存储了数字和字符串等具有高度不变性的数据。 惯例上,多处复用的正则表达式也可以使用常量存储。 开发者应理解他们的差异。

常量命名应遵守如下约定:

  • 必须 使用 下划线大写命名
  • 必须 是一个名词

正确举例:

  1. // 普通常量
  2. const PAGE_SIZE = 30;
  3. const API_GET_USER = '/user/get'
  4. const REG_URL = /https?:\/\/.*/
  5. // 类常量属性
  6. class PageLoader {
  7. public readonly STATUS_SUCCESS = 'success'
  8. public readonly STATUS_LOADING = 'loading'
  9. public status: typeof PageLoader['STATUS_SUCCESS'] | typeof PageLoader['STATUS_LOADING']
  10. }
  11. // 枚举成员
  12. enum TabType {
  13. HOME: 'home',
  14. MESSAGES: 'messages',
  15. ME: 'me'
  16. }

错误举例略

2.3 函数 Functions

函数名称指:

  • 使用 function 关键字声明的 TypeScript/JavaScript 函数 (但不包括构造函数
  • 使用 var let const 声明的,值类型为函数(但不包括构造函数)的 TypeScript/JavaScript 变量和常量
  • 方法
  • 函数类型属性
  • 函数类型属性 Getter/Setter
  • 对象函数类型成员

函数从命名偏好上可分:

  • 普通函数:描述一个动作或行为的函数,可以是纯函数也可以是副作用函数。
  • 处理器函数(handler):用于事件的响应处理,通常是副作用函数。有时可以使用数据型函数替代。
  • 检查函数(checker):用于进行类型或状态的检查,通常是返回布尔值的纯函数。
  • 数据型函数:包括迭代器,生成器,回调函数在内的,概念上更接近变量的函数。这部分函数往往会被当做高阶函数的参数。

普通函数 名称应遵守如下约定:

处理器函数 名称应遵守如下约定:

  • 必须 使用 驼峰命名
  • 应该 handle/on + [对象] + 行为 结构的短句 (比如 handleItemClick 或 onReady)
  • 在没有歧义的情况下,可以 handle + 操作 结构的短句 (比如 handleSend 或 handleToggle)
  • 不能名词

检查函数 名称应遵守如下约定:

  • 必须 使用 驼峰命名
  • 必须 描述性肯定短句 (比如 isElement) 或者类似 check + xxx 的形式
    • 注意:检查函数和布尔变量都会使用 描述性肯定短句 ,比如 isElement 既可以是布尔类型变量的名称obj.isElement = true ,也可以是检查函数的名称 if (isElement(obj) == true){}
      实际上由于数据类型不同(函数vs非函数),上述两种情况使用相同的规则并不太会对可读性造成影响。

数据型函数 名词应遵守如下约定:

  • 必须 使用 驼峰命名
  • 必须 是 名词
  • 应该 以 数据型函数惯例名称 结尾。惯例名称包括 callback, listener, generator, iteritor, handler 等等

正确举例:

  1. /* 普通函数 */
  2. function reload() {}
  3. function parseHTML(html:string) {}
  4. function genURL() {}
  5. const doSend = () => {}
  6. class SomeDialog {
  7. init() {}
  8. sendAPI() {}
  9. postData() {}
  10. showFooterBtns() {}
  11. }
  12. const obj = {
  13. toString: () => {}
  14. }
  15. /* 处理器函数 */
  16. function onPageReady () {}
  17. class SomeComponent {
  18. handleClick() {}
  19. handleCloseClick() {}
  20. }
  21. /* 检查函数 */
  22. function nodeIsElement(node: Node): Node is Element {}
  23. function isNull(target: any): boolean {}
  24. function stringIsTooLong(str: string): boolean {}
  25. /* 数据型函数 */
  26. const cb = () => {} // callback 的略写
  27. const callback = () => {}
  28. const iter = () => {} // 迭代器 iteritor 的略写
  29. const userGenerator = () => {return new User}
  30. const pageReadyListener = () => {}
  31. const clickHandler = () => {}

错误举例:

  1. /* 错误的普通函数 */
  2. function started () {} //错误,未使用动词,且表意不明,不知道是 start() 还是 handleStarted()
  3. class SomeDialog {
  4. SendAPI() {} //错误,未使用驼峰命名(使用了大驼峰)
  5. name() {} //错误,必须是动词或动词开头短句
  6. }
  7. /* 错误的处理器函数 */
  8. function handleClickItem() {} //错误,应改为 handleItemClick
  9. function onClickItem() {} //错误,应改为 onItemClick
  10. function handleItem() {} //错误,表意不明,没说清楚是 handle item 的什么事件
  11. // 一个有多个输入框的组件
  12. class SomeComponentWithManyInputs {
  13. handleFocus() {} //如果这是处理特定input的focus,则错误。因为没有说明具体对象
  14. //如果这是处理所有input的focus,则可以算正确,但是最好改成
  15. //handleAnyInputFocus
  16. }
  17. /* 错误的检查函数 */
  18. function strTooLong(str:string): boolean //错误,没有使用 "主语 + be动词/情态动词 + 动词/名词"
  19. //的结构。应该改为 strIsTooLong
  20. function checkIfElement(node:Node): Node is Element {} //严格来说这个函数满足普通函数的命名规范
  21. //但是还是建议改成 isElement()
  22. //或 nodeIsElement()
  23. /* 错误的数据型函数 */
  24. const clickHandle = () => {} //错误,应改为 clickHandler 或 handleClick
  25. const userGen = () => {} //错误,gen 这个缩写可以代表 generate 和 generator 故有歧义
  26. //应改成 genUser(gen放在前面则只能代表generate,无歧义)
  27. //或 userGenerator
  28. const callbackForTimeout = () => {} //错误,callback 应写在最后。应改为 timeoutCallback

2.4 类和构造函数 Classes & Constructor

类名称指:

  • 使用 class 关键字声明的 TypeScript/JavaScript 类

构造函数指:

  • 使用 原型链 方式构建的,用于模拟类功能的构造函数。包括使用 function 关键字声明的构造函数和存储了构造函数的 TypeScript/JavaScript 变量

类和构造函数命名规定一致,应遵守如下约定:

  • 必须 使用 大驼峰命名(PascalCase)
  • 必须 是 名词

正确举例:

  1. class User {}
  2. const UserConstructor = User //如果将类赋值给变量,此变量也遵从类的命名规范

错误举例:

  1. class user {} //错误,必须使用大驼峰
  2. class WatchDOM {} //错误,必须使用名词,可以改为 class DOMWatcher
  3. class ParseHTML {} //错误,理由同上,可以改为 HTMLParser

2.5 接口和类型别名 Interfaces & Type Aliases

接口和类型别名指:

  • 使用 interface 关键字声明的 TypeScript 接口
  • 使用 type 关键词声明的 TypeScript 类型别名

接口名称和类型别名要求:

  • 必须 使用大驼峰命名
  • 不应该 使用前置下划线变体,除非仅在局部定义域中使用
  • 必须 是 名词

正确举例:

  1. interface UserListProps {}
  2. interface User {}
  3. type TabType = 'home' | 'me'
  4. type UserArray = Array<User>
  5. type ClickHandler = () => void

错误举例:

  1. type TAB_TYPE = 'home' | 'me' //错误,必须使用大驼峰
  2. interface user {} //错误,必须使用大驼峰
  3. type HandleClick = () => void //错误,必须使用名词,可以改为 ClickHandler

2.6 文件名 Filenames

文件名根据内容的不同,有如下三种区分

  • 类文件:主要作用是实现一个会导出的 主类 ,且在文件内部不对其进行实例化的文件
  • 单例或对象文件:主要作用是实现一个会导出的 主对象 ,这个主对象可以是一个单例、对象或命名空间。比如常用的 utils.ts 就属于此类。
  • 定义文件:只包含TypeScript定义,不包含实际实现的文件
  • 普通文件:其他的所有文件

类文件 应使用 大驼峰命名 且文件名和主类名词一致。如 DOMWatcher.ts
单例或对象文件 应使用 驼峰命名 且文件名和单例或主对象名词一致,如 utils.ts 或 apis.ts
定义文件 应使用 驼峰命名 和 .d.ts 后缀。如 models.d.ts
普通文件 应使用 驼峰命名

所有文件名都 应该 是 名词

正确举例:

  1. # 类文件
  2. DOMWatcher.ts # export class DOMWatcher
  3. # 单例或对象文件
  4. apiService.ts # export const apiService = new APIService()
  5. utils.ts # export namespace utils {}
  6. # 定义文件
  7. models.d.ts # declare namespace Models {}
  8. # 普通文件
  9. home.ts # $.ready(() => { new HomePage() })

错误举例:

  1. # 错误类文件
  2. userService.ts # export class UserService | 错误,文件名要和主类名称一致
  3. # 错误单例或对象文件
  4. api.ts # export namespace apis {} | 错误,文件名要和导出的对象,单例,命名空间名称一致
  5. # 错误的定义文件
  6. Models.d.ts # declare namespace Models {} | 错误,定义文件应使用驼峰命名
  7. # 错误的普通文件
  8. home-entry.ts # $.ready(() => { new HomePage() }) | 错误,普通文件应使用驼峰命名

2.7 命名空间 Namespaces

命名空间指:

  • 使用 namespace 关键字声明的 TypeScript 命名空间。
  • 使用 declare namespace 关键词声明的纯定义 TypeScript 命名空间。

注:此处说的命名空间 不包括 为了声明外部依赖的包而编写的 .d.ts 文件(比如 jquery.d.ts)中使用的命名空间。这部分将在 #2.10.1 引用依赖 中叙述

命名空间在使用上做如下区分:

  • 实命名空间:使用 namespace 声明的,用于包裹特定数据,操作和类型,以方便使用和防止命名冲突的命名空间。常见的有 utils, api 等。
  • 定义命名空间:使用 declare namespace 声明的,用于包裹部分类型定义,以方便别处引用和防止命名冲突的命名空间。多用于定义文件 d.ts 中。常见的有 Models 等。
  • 补充命名空间:使用 namespace 或 declare namespace 声明的,和一个已有的 类 重名的命名空间。这种命名空间会和 类 同时使用,用于暴露一些和类有关的私有类型和数据
  • 枚举命名空间:使用 namespace 声明的,仅用于包裹 枚举 的命名空间。

实命名空间 要求:

  • 必须 使用 驼峰命名
  • 必须 是 名词

定义命名空间 要求:

  • 必须 使用 大驼峰命名
  • 必须 使用 名词

补充命名空间 要求:

  • 必须 使用 大驼峰命名
  • 必须 和被补充的类同名

枚举命名空间 要求:

  • 必须 和其中包含的枚举名称采用相同命名规则。因为枚举可以使用大驼峰命名,也可使用下划线大写命名,因此枚举命名空间也可以使用大驼峰命名或下划线大写命名

正确举例:

  1. /* 实命名空间 */
  2. // 在 utils.ts 文件中
  3. export namespace utils {
  4. export function parseTime() {}
  5. }
  6. // 在 apis.ts 文件中
  7. export namespace apis {
  8. export function getUser() {}
  9. }
  10. /* 定义命名空间 */
  11. // 在 models.d.ts 文件中
  12. declare namespace Models {
  13. interface Book {
  14. title: string
  15. auhtor: string
  16. }
  17. }
  18. /* 补充命名空间 */
  19. // 在 User.ts 文件中
  20. export class User {
  21. avatar: User.Avatar
  22. type: User.Type
  23. }
  24. // 此命名空间用于为 User 类补充一些定义,因为类中不支持直接使用 interface 或 type 关键字
  25. export declare namespace User {
  26. interface Avatar {
  27. src: string
  28. width: number
  29. height: number
  30. }
  31. type Type = 'normal'|'vip'
  32. }
  33. /* 枚举命名空间 */
  34. // 如果枚举使用下划线大写,则命名空间也使用下划线大写
  35. export namespace SERVER_API {
  36. export enum GET {
  37. USER_INFO = '/get_user_info'
  38. }
  39. export enum POST {
  40. MODIFY_USER = '/modify_user'
  41. }
  42. }
  43. // 如果枚举使用大驼峰,则命名空间也使用大驼峰
  44. export namespace ServerApiStatus {
  45. export enum Common {
  46. SUCCESS: 0
  47. }
  48. }

2.8 枚举 Enums

枚举指:

  • 使用 Enum 关键字声明的 TypeScript 枚举对象

枚举对象通常用于指定某个类型变量中的可选值,因此和接口/类型别名有类似之处。同时,由于枚举值具备一些常量的特点,因此他和常量也有类似之处。

在使用枚举时,其命名有如下两种选择:

  • 类似接口/类型别名,名称使用 大驼峰命名
  • 类似常量,名称使用 下划线大写命名

上述两种情况中,都要满足如下条件:

  • 枚举名称 必须 使用 名词
  • 值名称 必须 使用 下划线大写命名。词性也不做限制,根据场景不同,各种词性都可使用。

正确举例:

  1. // 下划线大写命名
  2. enum NodeKind {
  3. LINK, TEXT, IMG // 使用名词
  4. }
  5. enum API_PATH {
  6. GET_USER = '/user/get', // 使用动词/动作命令短句
  7. DELTE_USER = '/user/delete', // 使用动词/动作命令短句
  8. LOGIN = '/login' // 使用动词/动作命令短句
  9. }
  10. enum USER_STATUS {
  11. ACTIVE, SUSPENDED // 使用形容词
  12. }
  13. // 驼峰命名
  14. enum DashboardTab {
  15. HOME = 'home',
  16. ME = 'me'
  17. }

错误举例:

  1. enum dashboardTab { //错误,必须使用大驼峰或者下划线大写
  2. HOME = 'home'
  3. }
  4. enum API_STATUS {
  5. Succcess, // 错误,值必须是下划线大写命名
  6. error // 错误,值必须是下划线大写命名
  7. }

2.9 装饰器 Decorators

装饰器指:

  • 使用 @xxx 语法声明的 TypeScript 装饰器

装饰器从应用场景上来说,往往是给特定的函数/属性/方法添加特定的修饰和限定,因此多数时候使用 形容词 或 描述性肯定短句,并采用 驼峰命名

但是,由于装饰器可能配合一些非主流抽象使用,因此对词性不做规定,命名法也允许使用 大驼峰命名

举例:

  1. class SomeClass {
  2. @private // 驼峰命名 + 形容词
  3. someMethod() {}
  4. @AuthRequired // 大驼峰命名 + 描述性肯定句
  5. anotherMethod() {}
  6. @state //名词也可以使用,代表「归属于某分类」的意思
  7. name: string
  8. }

2.10 外部依赖

2.10.1 引用依赖

引用依赖指:

  • 引用自项目外(第三方)的包/类库/script(下面统称为第三方包)。如 jQuery,ReactJS等

与引用依赖有关的命名约定分为如下几类:

  1. 使用 import * as [名称] from ‘path/to/library’ 语法时使用的变量名称。此变量称为 导入变量
    • 注意,不包括使用 import { [名称] } from ‘path/to/libary’ 语法时使用的命名。此时命名应该遵守第三方包的命名
  2. 针对没有包括 ts 定义的第三方包编写的 ts 定义

情况1 中,导入变量名称应满足如下条件:

  • 如果导入变量是一个 类 (即可以使用 new 操作),那么 应该 使用 大驼峰命名,否则 应该 使用 驼峰命名
    • 假如导入的是 loadash 这种使用了特殊命名风格,且无法转化成 驼峰命名 的库,则可以不使用 驼峰命名
  • 导入变量 应该 尽量和库的名称一致,没有使用驼峰/大驼峰命名的库,应该 被转化成驼峰/大驼峰命名后使用
    • 如果库的名字过分冗长,也可以使用简写或略写。比如第三方库 js-sha256 可以简写成 sha256

情况2 中,为没有定义的第三方包辨析定义时,按照如下规则:

  • 不论库名称如何,其定义应该放在名为 [转化成驼峰命名后的库名].d.ts 的定义文件中
  • 定义文件中的所有定义应该包裹在名为 [转化成大驼峰命名后的库名] 的命名空间中
  • 命名空间中的接口,类型别名都因该依照此文档内对应的规范来命名

以 jQuery 库举例:

  1. // 使用 jQuery 的文件
  2. import * as jQuery from 'jQuery' // 可以直接用jquery的大名
  3. import * as $ from 'jQuery' // 也可以使用 jQuery 推荐的命名,只要它满足「驼峰命名」
  4. // 定义 jQuery 的文件 (jQuery.d.ts)
  5. /** 命名空间必须是大驼峰形式的库名称 */
  6. declare namespace JQuery {
  7. // ...
  8. }

2.10.2 外部数据

外部数据指:

  • 使用 http 请求(api)或其他方式获取的外部数据。这些数据有可能使用了与此文档中的叙述不同的命名风格。

由于外部数据可能使用和我们不一样的命名习惯,为保证系统内部的命名一致性,我们 应该 在收到数据时进行一次转化,将其转化为符合我们命名风格的数据结构。

假如完全的转化的工作量过大,则至少应该确保其遵守 驼峰命名


3 典型场景

下面给出一些容易出错的典型场景

3.1 DOM事件处理函数 DOM Event Handler

假设有如下组件

  1. <section id="some-component">
  2. <button @bind:click="[事件处理函数]">Some Button</button>
  3. </section>

类似上文中写的函数就叫做 事件处理函数 。

在给事件处理函数命名时,我们有两种选择:

  • 按照 #2.3 函数 > 处理器函数 中的定义,将其命名成 handle + [对象] + 操作 的形式
  • 将事件绑定到一个 #2.3 函数 > 普通函数 上

上述两种情况都是允许的,但是如果绑定到 普通函数 ,此函数必须 不接受参数 。下面在举例中解释。

使用处理器函数风格

  1. <section id="some-component">
  2. <input @bind:keydown="handleInputKeydown" />
  3. <button @bind:click="handleSubmitClick">Submit Button</button>
  4. </section>
  1. /*
  2. 如果需要处理事件的 event 对象,则「必须」使用「处理器函数」的命名约定
  3. 如果不需要处理事件的 event 对象,也更推荐使用「处理器函数」的命名风格,这样以后可以方便的添加事件参数
  4. 而不用在修改参数时同时修改函数名称
  5. 注意,采用「处理器函数」约定来命名的事件处理函数「不可以」被其他函数调用
  6. */
  7. class SomeComponent {
  8. handleItemClick (event: MouseEvent) {
  9. event.stopPropagation()
  10. }
  11. handleInputKeydown (event: KeyboardEvent) {
  12. if (event.key == 'Enter') {
  13. // do something.
  14. }
  15. }
  16. someOtherMethod () {
  17. // 错误! 不可以直接调用一个「处理器函数」命名风格的事件处理函数
  18. // 除非有什么「非常特殊」的原因(比如就是为了模拟点击操作或劫持事件)
  19. this.handleItemClick({} as any)
  20. }
  21. }

直接触发普通函数

  1. <section id="some-component">
  2. <input @bind:keydown="updateAutoCompleteList" />
  3. <button @bind:click="updateAutoCompleteList"> 刷新自动补全列表 </button>
  4. <button @bind:click="doSubmit">提交</button>
  5. <button @bind:click="doSubmit">另一个提交</button>
  6. </section>
  1. /*
  2. 在此例子中,由于同一个操作可能会被多个 DOM 元素触发(可被其他函数触发),
  3. 如果使用 handleXXXClick 这样的命名,不同的元素就需要不同的处理器函数,提高了代码复用的成本
  4. 此时就可以选择直接由DOM触发普通函数。
  5. 但是要注意,普通函数是不能(也不应该)接收DOM事件对象作为参数的。
  6. */
  7. class SomeComponent {
  8. updateAutoCompleteList() {
  9. // ...
  10. }
  11. doSubmit(){
  12. // do submit...
  13. this.updateAutoCompleteList()
  14. }
  15. // 错误举例
  16. doSubmit(event: MouseEvent){
  17. event.preventDefault() //错误!普通函数不能接收DOM事件对象
  18. }
  19. }

3.2 标志值 Flags

TODO

3.3 使用前置下划线加强私有性

TODO

3.4 类 + 补充命名空间

TODO

3.5 单例模式

TODO

3.6 单数和复数的取舍

在命名时如果有用到复数的场景,应该遵守如下约定

  • 对同一个概念的描述应该 只使用一个 复数表达
  • 命名上的复数应对应数据上的复数

上述概念可以这样解释: 假如我们要叙述「获取多个用户id」这个操作,其中有「用户」和「id」两个概念,因此有如下几种处理思路

  1. 在 user 上使用复数 getUsersId
  2. 在 id 上使用复数 getUserIds
  3. 两个都使用复数 getUsersIds

依照规则一,第三种处理应该被废弃,仅剩

  1. getUsersId
  2. getUserIds

但是,由于此操作从名称上来讲,获取到的数据应该是 「多个id数据」而不是「多个用户数据」,因此复数应该用在「id」上。

因此,「获取多个用户id操作」 应该 被命名为 getUserIds

3.9 re, un 等带有前缀的动词

当遇到 re-start, re-send, re-load 等表达「再次进行某操作」单词,或 un-favorite, un-load 等表达「进行某操作的反操作」时,应该注意在英文中 un- 和 re- 都是用连字符前缀来表达的,这就意味着 un 和 re 不是独立的单词。

因此,当使用大小驼峰命名和下划线大写命名方式时,un 和 re 不应该看作的独立单词。比如

  • re-load 应该写为 reload 而不是 reLoad
  • un-star 应该写为 unstar 而不是 unStar
  • 以此类推

4 附录

4.1 常见首字母缩写词

  • API (Application Program Interface)
  • HTML (Hyper Text Markup Language)
  • DOM (Document Object Model)
  • URL (Uniform Resource Locator)
  • URI (Uniform Resource Indentifier)
  • CDN (Content Delivery Network)
  • IPC (Inter-Process Communication)
  • RPC (Remote Procedure Call)

4.2 常见略写词

常规的略写方法有如下两种:

  • 保留开头几个字符,略去剩下的。此种方式的常见略写词有:
    • dev (略自 development)
    • prod (略自 production)
    • str (略自 string)
    • fav (略自 Favorite)
    • req (略自 Request)
    • res 或 resp (略自 Response)
    • el (略自 Element)
    • val (略自 Value)
    • iter (略自 Iterator)
    • init (略自 Initialize)
    • prop (略自 Property)
    • el (略自 Element)
    • attr (略自 Attribute)
  • 保留全部或部分代表性辅音字母而略去元音字母。此种方式的常见略写词有:
    • img (略自 Image)
    • idx (略自 Index)
    • cnt (略自 Count)
    • cb (略自 Callback)
    • msg (略自 Message)
    • btn (略自 Button)
    • evt (略自 Event)