1. 概述
本规范中关于各个规则有 必须,应该,可以,不应该,不能 五种叙述。
必须 和 不能 为强制规定
应该 和 不应该 表达了强烈建议,如果没有其他强烈的原因,则应当遵守
可以 表达了轻微建议,开发者选择性遵守
在公司所有 TypeScript/JavaScript 项目中,各种变量和函数名称 应该 使用 驼峰命名
和 大驼峰命名
(包括它们的变体),在一些特殊情况下 可以 使用 下划线大写命名
。 其他命名法(匈牙利,下划线小写等)皆 不能 使用。
注意:在一些应用场景下,前置下划线是允许使用的,参见 #3.3 使用前置下划线加强私有性
1.1 命名法定义
1.1.1 驼峰命名 camelCase
驼峰命名法有如下规则:
- 满足正则表达式
/[a-z$][a-zA-Z0-9$]*/
- 第一个单词全部小写
- 第二个及以后
单词
的第一个字母大写,其余字母小写 - 数字看作独立单词
- 变体:在一些场景下可在名称前加下划线,表明其私有性。即
/_[a-z$][a-zA-Z0-9$]*/
正确举例:
var dialog, mailBox, user1, user1Data
var htmlData, getUrl, someHttpApi, oauthComplete // 不论是否缩写单词,都满足上述规定
var _someLocalVariable // 额外加上下划线表明私有性
错误举例:
var Data, 1user // 错误,应以小写字母开头
var _User // 错误, 在变体规则中,下划线不影响首字母规则,应改为 _user
1.1.2 大驼峰命名(帕斯卡命名)PascalCase
与驼峰命名类似,有如下规则:
- 满足正则表达式
/[A-Z$][a-zA-Z0-9$]*/
- 所有
单词
的第一个字母大写,其余字母小写 - 数字看作独立单词
- 变体:(比较罕见)在一些场景下可以在名称前加下划线,表明其私有性。即
/_[A-Z$][a-zA-Z0-9$]*/
正确举例:
var MailBox, Dialog, UserInfo
var HtmlData, GetUrl, SomeApi, DomHtmlApi // 不论是否缩写单词,都满足上述规定
var _MailBox, _HTMLData // 额外加上下划线表明私有性(比较罕见)
错误举例略去,可参考 #1.1.1 驼峰命名部分
1.1.3 下划线大写命名 UNDERLINE_ALL_UPPERCASE
下划线大写命名有如下规则:
- 满足正则表达式
/[A-Z$][A-Z0-9$_]*/
- 所有字母大写
- 所有
单词
使用下划线分割 - 数字看作独立单词
- 变体:(比较罕见)在一些场景下可以在名称前加下划线,表明其私有性。即
/_[A-Z$][A-Z0-9$_]*/
正确举例:
const PAGE_SIZE, API_HOST, CONTENT_MAX_LEN
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
变量指:
- 使用
let
和var
关键字声明的 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 等
正确命名举例:
function someFunctionScope(){
// 普通变量
var item = 'a'
let clickCnt = 1
let userNameInput = document.getElementById('input')
const alertDialog = getDialog()
const targetURL = 'https://xxx.com' + '?a=1'
const htmlStr = '<div></div>'
const contentHTML = '<div></div>'
const book = {
name: 'SomeBook' // 对象成员遵从变量命名规则
}
class Book {
name = 'SomeBook' // 类属性遵从变量命名规则
get age () {return 1} // getter 遵从变量命名规则
set age () {} //setter 遵从变量命名规则
}
// 布尔变量:状态标志
var disabled = true
let isActive = true
var requestCompleted = true
let valueIsEmail = true
const userCreatedAt = Date.now()
const willDeleteUser = true
const canFly = true
// 布尔变量:二元数据
let submitFlag = true
let clickLock = true
let lazyMode = true
let autoPlaySwitch = true
// 临时变量
list.forEach((i)=>{}) //使用单字母变量,因为结合上下文很容易知道其意义
for (const key in obj) { //使用抽象名词key,因为结合上下文很容易知道其意义
const val = obj[key] //使用抽象名词key,因为结合上下文很容易知道其意义
}
}
错误命名举例:
function someFunctionScope(){
var ITEM, user_name, contentHtml // 错误,未使用驼峰命名或大驼峰命名
var user = true // 错误,名称没有正确表达含义
var btnEl = '<button></button>' // 错误,名称没有正确表达含义,可改为 btnHTML
var disable = true // 错误,不可以使用动词,可以改为 disabled
var sendEmail = true // 错误,不可以使用动词开头短句
// 可以改为 emailSent 或 sendEmailCompleted
var createAt = Date.now() // 错误,语义有歧义,且不可以动词开头
// 如果描述过去已创建的时间,应该改为 createdAt
// 如果描述未来计划创建的时间,应该改为 willCreateAt
}
2.2 常量 Constants
常量指:
- 使用
const
关键字声明的,在较大定义域中用于指代一个固定值的 非函数 TypeScript/JavaScript 常量 - 在
类
中以readonly
关键字修饰的,用于指代一个固定值的 非函数属性
- 在
枚举
中使用的,用于指代一个固定值的 非函数枚举成员
常量
应该区分 #2.1 中使用 const 来声明的常变量
常量绝大多数存储了数字和字符串等具有高度不变性的数据。 惯例上,多处复用的正则表达式也可以使用常量存储。 开发者应理解他们的差异。
常量命名应遵守如下约定:
- 必须 使用
下划线大写命名
- 必须 是一个名词
正确举例:
// 普通常量
const PAGE_SIZE = 30;
const API_GET_USER = '/user/get'
const REG_URL = /https?:\/\/.*/
// 类常量属性
class PageLoader {
public readonly STATUS_SUCCESS = 'success'
public readonly STATUS_LOADING = 'loading'
public status: typeof PageLoader['STATUS_SUCCESS'] | typeof PageLoader['STATUS_LOADING']
}
// 枚举成员
enum TabType {
HOME: 'home',
MESSAGES: 'messages',
ME: 'me'
}
错误举例略
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非函数),上述两种情况使用相同的规则并不太会对可读性造成影响。
- 注意:检查函数和布尔变量都会使用 描述性肯定短句 ,比如 isElement 既可以是布尔类型变量的名称
数据型函数 名词应遵守如下约定:
- 必须 使用 驼峰命名
- 必须 是 名词
- 应该 以 数据型函数惯例名称 结尾。惯例名称包括 callback, listener, generator, iteritor, handler 等等
正确举例:
/* 普通函数 */
function reload() {}
function parseHTML(html:string) {}
function genURL() {}
const doSend = () => {}
class SomeDialog {
init() {}
sendAPI() {}
postData() {}
showFooterBtns() {}
}
const obj = {
toString: () => {}
}
/* 处理器函数 */
function onPageReady () {}
class SomeComponent {
handleClick() {}
handleCloseClick() {}
}
/* 检查函数 */
function nodeIsElement(node: Node): Node is Element {}
function isNull(target: any): boolean {}
function stringIsTooLong(str: string): boolean {}
/* 数据型函数 */
const cb = () => {} // callback 的略写
const callback = () => {}
const iter = () => {} // 迭代器 iteritor 的略写
const userGenerator = () => {return new User}
const pageReadyListener = () => {}
const clickHandler = () => {}
错误举例:
/* 错误的普通函数 */
function started () {} //错误,未使用动词,且表意不明,不知道是 start() 还是 handleStarted()
class SomeDialog {
SendAPI() {} //错误,未使用驼峰命名(使用了大驼峰)
name() {} //错误,必须是动词或动词开头短句
}
/* 错误的处理器函数 */
function handleClickItem() {} //错误,应改为 handleItemClick
function onClickItem() {} //错误,应改为 onItemClick
function handleItem() {} //错误,表意不明,没说清楚是 handle item 的什么事件
// 一个有多个输入框的组件
class SomeComponentWithManyInputs {
handleFocus() {} //如果这是处理特定input的focus,则错误。因为没有说明具体对象
//如果这是处理所有input的focus,则可以算正确,但是最好改成
//handleAnyInputFocus
}
/* 错误的检查函数 */
function strTooLong(str:string): boolean //错误,没有使用 "主语 + be动词/情态动词 + 动词/名词"
//的结构。应该改为 strIsTooLong
function checkIfElement(node:Node): Node is Element {} //严格来说这个函数满足普通函数的命名规范
//但是还是建议改成 isElement()
//或 nodeIsElement()
/* 错误的数据型函数 */
const clickHandle = () => {} //错误,应改为 clickHandler 或 handleClick
const userGen = () => {} //错误,gen 这个缩写可以代表 generate 和 generator 故有歧义
//应改成 genUser(gen放在前面则只能代表generate,无歧义)
//或 userGenerator
const callbackForTimeout = () => {} //错误,callback 应写在最后。应改为 timeoutCallback
2.4 类和构造函数 Classes & Constructor
类名称指:
- 使用 class 关键字声明的 TypeScript/JavaScript 类
构造函数指:
- 使用 原型链 方式构建的,用于模拟类功能的构造函数。包括使用 function 关键字声明的构造函数和存储了构造函数的 TypeScript/JavaScript 变量
类和构造函数命名规定一致,应遵守如下约定:
- 必须 使用 大驼峰命名(PascalCase)
- 必须 是 名词
正确举例:
class User {}
const UserConstructor = User //如果将类赋值给变量,此变量也遵从类的命名规范
错误举例:
class user {} //错误,必须使用大驼峰
class WatchDOM {} //错误,必须使用名词,可以改为 class DOMWatcher
class ParseHTML {} //错误,理由同上,可以改为 HTMLParser
2.5 接口和类型别名 Interfaces & Type Aliases
接口和类型别名指:
- 使用 interface 关键字声明的 TypeScript 接口
- 使用 type 关键词声明的 TypeScript 类型别名
接口名称和类型别名要求:
- 必须 使用大驼峰命名
- 不应该 使用前置下划线变体,除非仅在局部定义域中使用
- 必须 是 名词
正确举例:
interface UserListProps {}
interface User {}
type TabType = 'home' | 'me'
type UserArray = Array<User>
type ClickHandler = () => void
错误举例:
type TAB_TYPE = 'home' | 'me' //错误,必须使用大驼峰
interface user {} //错误,必须使用大驼峰
type HandleClick = () => void //错误,必须使用名词,可以改为 ClickHandler
2.6 文件名 Filenames
文件名根据内容的不同,有如下三种区分
- 类文件:主要作用是实现一个会导出的 主类 ,且在文件内部不对其进行实例化的文件
- 单例或对象文件:主要作用是实现一个会导出的 主对象 ,这个主对象可以是一个单例、对象或命名空间。比如常用的 utils.ts 就属于此类。
- 定义文件:只包含TypeScript定义,不包含实际实现的文件
- 普通文件:其他的所有文件
类文件 应使用 大驼峰命名 且文件名和主类名词一致。如 DOMWatcher.ts
单例或对象文件 应使用 驼峰命名 且文件名和单例或主对象名词一致,如 utils.ts 或 apis.ts
定义文件 应使用 驼峰命名 和 .d.ts 后缀。如 models.d.ts
普通文件 应使用 驼峰命名
所有文件名都 应该 是 名词
正确举例:
# 类文件
DOMWatcher.ts # export class DOMWatcher
# 单例或对象文件
apiService.ts # export const apiService = new APIService()
utils.ts # export namespace utils {}
# 定义文件
models.d.ts # declare namespace Models {}
# 普通文件
home.ts # $.ready(() => { new HomePage() })
错误举例:
# 错误类文件
userService.ts # export class UserService | 错误,文件名要和主类名称一致
# 错误单例或对象文件
api.ts # export namespace apis {} | 错误,文件名要和导出的对象,单例,命名空间名称一致
# 错误的定义文件
Models.d.ts # declare namespace Models {} | 错误,定义文件应使用驼峰命名
# 错误的普通文件
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 声明的,仅用于包裹 枚举 的命名空间。
实命名空间 要求:
- 必须 使用 驼峰命名
- 必须 是 名词
定义命名空间 要求:
- 必须 使用 大驼峰命名
- 必须 使用 名词
补充命名空间 要求:
- 必须 使用 大驼峰命名
- 必须 和被补充的类同名
枚举命名空间 要求:
- 必须 和其中包含的枚举名称采用相同命名规则。因为枚举可以使用大驼峰命名,也可使用下划线大写命名,因此枚举命名空间也可以使用大驼峰命名或下划线大写命名
正确举例:
/* 实命名空间 */
// 在 utils.ts 文件中
export namespace utils {
export function parseTime() {}
}
// 在 apis.ts 文件中
export namespace apis {
export function getUser() {}
}
/* 定义命名空间 */
// 在 models.d.ts 文件中
declare namespace Models {
interface Book {
title: string
auhtor: string
}
}
/* 补充命名空间 */
// 在 User.ts 文件中
export class User {
avatar: User.Avatar
type: User.Type
}
// 此命名空间用于为 User 类补充一些定义,因为类中不支持直接使用 interface 或 type 关键字
export declare namespace User {
interface Avatar {
src: string
width: number
height: number
}
type Type = 'normal'|'vip'
}
/* 枚举命名空间 */
// 如果枚举使用下划线大写,则命名空间也使用下划线大写
export namespace SERVER_API {
export enum GET {
USER_INFO = '/get_user_info'
}
export enum POST {
MODIFY_USER = '/modify_user'
}
}
// 如果枚举使用大驼峰,则命名空间也使用大驼峰
export namespace ServerApiStatus {
export enum Common {
SUCCESS: 0
}
}
2.8 枚举 Enums
枚举指:
- 使用 Enum 关键字声明的 TypeScript 枚举对象
枚举对象通常用于指定某个类型变量中的可选值,因此和接口/类型别名有类似之处。同时,由于枚举值具备一些常量的特点,因此他和常量也有类似之处。
在使用枚举时,其命名有如下两种选择:
- 类似接口/类型别名,名称使用 大驼峰命名
- 类似常量,名称使用 下划线大写命名
上述两种情况中,都要满足如下条件:
- 枚举名称 必须 使用 名词
- 值名称 必须 使用 下划线大写命名。词性也不做限制,根据场景不同,各种词性都可使用。
正确举例:
// 下划线大写命名
enum NodeKind {
LINK, TEXT, IMG // 使用名词
}
enum API_PATH {
GET_USER = '/user/get', // 使用动词/动作命令短句
DELTE_USER = '/user/delete', // 使用动词/动作命令短句
LOGIN = '/login' // 使用动词/动作命令短句
}
enum USER_STATUS {
ACTIVE, SUSPENDED // 使用形容词
}
// 驼峰命名
enum DashboardTab {
HOME = 'home',
ME = 'me'
}
错误举例:
enum dashboardTab { //错误,必须使用大驼峰或者下划线大写
HOME = 'home'
}
enum API_STATUS {
Succcess, // 错误,值必须是下划线大写命名
error // 错误,值必须是下划线大写命名
}
2.9 装饰器 Decorators
装饰器指:
- 使用 @xxx 语法声明的 TypeScript 装饰器
装饰器从应用场景上来说,往往是给特定的函数/属性/方法添加特定的修饰和限定,因此多数时候使用 形容词 或 描述性肯定短句,并采用 驼峰命名
但是,由于装饰器可能配合一些非主流抽象使用,因此对词性不做规定,命名法也允许使用 大驼峰命名
举例:
class SomeClass {
@private // 驼峰命名 + 形容词
someMethod() {}
@AuthRequired // 大驼峰命名 + 描述性肯定句
anotherMethod() {}
@state //名词也可以使用,代表「归属于某分类」的意思
name: string
}
2.10 外部依赖
2.10.1 引用依赖
引用依赖指:
- 引用自项目外(第三方)的包/类库/script(下面统称为第三方包)。如 jQuery,ReactJS等
与引用依赖有关的命名约定分为如下几类:
- 使用 import * as [名称] from ‘path/to/library’ 语法时使用的变量名称。此变量称为 导入变量
- 注意,不包括使用 import { [名称] } from ‘path/to/libary’ 语法时使用的命名。此时命名应该遵守第三方包的命名
- 针对没有包括 ts 定义的第三方包编写的 ts 定义
情况1 中,导入变量名称应满足如下条件:
- 如果导入变量是一个 类 (即可以使用 new 操作),那么 应该 使用 大驼峰命名,否则 应该 使用 驼峰命名
- 假如导入的是 loadash 这种使用了特殊命名风格,且无法转化成 驼峰命名 的库,则可以不使用 驼峰命名
- 导入变量 应该 尽量和库的名称一致,没有使用驼峰/大驼峰命名的库,应该 被转化成驼峰/大驼峰命名后使用
- 如果库的名字过分冗长,也可以使用简写或略写。比如第三方库 js-sha256 可以简写成 sha256
情况2 中,为没有定义的第三方包辨析定义时,按照如下规则:
- 不论库名称如何,其定义应该放在名为 [转化成驼峰命名后的库名].d.ts 的定义文件中
- 定义文件中的所有定义应该包裹在名为 [转化成大驼峰命名后的库名] 的命名空间中
- 命名空间中的接口,类型别名都因该依照此文档内对应的规范来命名
以 jQuery 库举例:
// 使用 jQuery 的文件
import * as jQuery from 'jQuery' // 可以直接用jquery的大名
import * as $ from 'jQuery' // 也可以使用 jQuery 推荐的命名,只要它满足「驼峰命名」
// 定义 jQuery 的文件 (jQuery.d.ts)
/** 命名空间必须是大驼峰形式的库名称 */
declare namespace JQuery {
// ...
}
2.10.2 外部数据
外部数据指:
- 使用 http 请求(api)或其他方式获取的外部数据。这些数据有可能使用了与此文档中的叙述不同的命名风格。
由于外部数据可能使用和我们不一样的命名习惯,为保证系统内部的命名一致性,我们 应该 在收到数据时进行一次转化,将其转化为符合我们命名风格的数据结构。
假如完全的转化的工作量过大,则至少应该确保其遵守 驼峰命名
3 典型场景
下面给出一些容易出错的典型场景
3.1 DOM事件处理函数 DOM Event Handler
假设有如下组件
<section id="some-component">
<button @bind:click="[事件处理函数]">Some Button</button>
</section>
类似上文中写的函数就叫做 事件处理函数 。
在给事件处理函数命名时,我们有两种选择:
上述两种情况都是允许的,但是如果绑定到 普通函数 ,此函数必须 不接受参数 。下面在举例中解释。
使用处理器函数风格
<section id="some-component">
<input @bind:keydown="handleInputKeydown" />
<button @bind:click="handleSubmitClick">Submit Button</button>
</section>
/*
如果需要处理事件的 event 对象,则「必须」使用「处理器函数」的命名约定
如果不需要处理事件的 event 对象,也更推荐使用「处理器函数」的命名风格,这样以后可以方便的添加事件参数
而不用在修改参数时同时修改函数名称
注意,采用「处理器函数」约定来命名的事件处理函数「不可以」被其他函数调用
*/
class SomeComponent {
handleItemClick (event: MouseEvent) {
event.stopPropagation()
}
handleInputKeydown (event: KeyboardEvent) {
if (event.key == 'Enter') {
// do something.
}
}
someOtherMethod () {
// 错误! 不可以直接调用一个「处理器函数」命名风格的事件处理函数
// 除非有什么「非常特殊」的原因(比如就是为了模拟点击操作或劫持事件)
this.handleItemClick({} as any)
}
}
直接触发普通函数
<section id="some-component">
<input @bind:keydown="updateAutoCompleteList" />
<button @bind:click="updateAutoCompleteList"> 刷新自动补全列表 </button>
<button @bind:click="doSubmit">提交</button>
<button @bind:click="doSubmit">另一个提交</button>
</section>
/*
在此例子中,由于同一个操作可能会被多个 DOM 元素触发(可被其他函数触发),
如果使用 handleXXXClick 这样的命名,不同的元素就需要不同的处理器函数,提高了代码复用的成本
此时就可以选择直接由DOM触发普通函数。
但是要注意,普通函数是不能(也不应该)接收DOM事件对象作为参数的。
*/
class SomeComponent {
updateAutoCompleteList() {
// ...
}
doSubmit(){
// do submit...
this.updateAutoCompleteList()
}
// 错误举例
doSubmit(event: MouseEvent){
event.preventDefault() //错误!普通函数不能接收DOM事件对象
}
}
3.2 标志值 Flags
TODO
3.3 使用前置下划线加强私有性
TODO
3.4 类 + 补充命名空间
TODO
3.5 单例模式
TODO
3.6 单数和复数的取舍
在命名时如果有用到复数的场景,应该遵守如下约定
- 对同一个概念的描述应该 只使用一个 复数表达
- 命名上的复数应对应数据上的复数
上述概念可以这样解释: 假如我们要叙述「获取多个用户id」这个操作,其中有「用户」和「id」两个概念,因此有如下几种处理思路
- 在 user 上使用复数 getUsersId
- 在 id 上使用复数 getUserIds
- 两个都使用复数 getUsersIds
依照规则一,第三种处理应该被废弃,仅剩
- getUsersId
- 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)