原文链接:
"在编程界, 命名规范是一个大的集合, 包含了众多的规则. 所谓的命名规范, 就是将一系列的字符排列组合为标识符, 用来表示
变量
、类型
、函数
以及源码和文档中的其它实体
." —— Wikipedia
给一个东西命名确实有难度!
这篇文章将致力于介绍 A/HC/LC 命名法, 希望这可以增强代码的可读性.
并且我将使用 JavaScript 来举例说明, 但实际上这种命名方式可以被应用到任何编程语言.
(P)A/HC/LC是什么?
实际上, (P)A/HC/LC
指的是使用下面的模式来命名一个函数.
prefix? + action (A) + high context (HC) + low context? (LC)
Prefix是什么?
prefix(前缀
)增强函数的可读性.
- is
描述当前 context(语境
) 的特征或状态, 通常是一个布尔值.
const color = 'blue'
const isBlue = (color === 'blue') // characteristic
const isPresent = true // state
if (isBlue && isPresent) {
console.log('Blue is present!')
}
- has
描述当前的 context(语境
) 是否具有某个特定的值或者状态. 通常也是一个布尔值.
/* Bad */
const isProductsExist = (productsCount > 0)
const areProductsPresent = (productsCount > 0)
/* Good */
const hasProducts = (productsCount > 0)
- should
映射一个具有明确动作的条件语句, 通常也是一个布尔值.
function shouldUpdateUrl(url, expectedUrl) {
return (url !== expectedUrl)
}
函数的核心是Actions
Actions(动作
) 是函数名的动词部分. 描述这个函数要做什么, 同样非常重要.
- get
获取数据
比如快捷的获取内部数据.
function getFruitsCount() {
return this.fruits.length;
}
- set
设置数据
比如将某个变量的值由 A
变为 B
.
let fruits = 0
function setFruits(nextFruits) {
fruits = nextFruits
}
setFruits(5)
console.log(fruits) // 5
- reset
还原数据
比如将某个变量设置为其初始的值或状态
let initialFruits = 5
let fruits = initialFruits
setFruits(10)
console.log(fruits) // 10
function resetFruits() {
fruits = initialFruits
}
resetFruits()
console.log(fruits) // 5
- fetch
请求数据
通常是一个比较耗时的操作(比如: 异步请求).
function fetchPosts(postCount) {
return fetch('https://api.dev/posts', {...})
}
- remove
移除数据
逻辑上的删除, 某个数据可能只是被移动到了别的地方.
假设在搜索页面有一个筛选器, 可以通过 removeFilter
来移除筛选器中的某个值, 而不是 deleteFilter
, 因为英语的表达方式更倾向于前者.
function removeFilter(filterName, filters) {
return filters.filter(name => name !== filterName)
}
const selectedFilters = ['price', 'availability', 'size']
removeFilter('price', selectedFilters)
- delete
删除数据
将某个数据彻底删除, 是物理上的删除.
可以想象如果你是一个文章审核员, 看到一篇很辣鸡的文章, 当你想彻底删除这篇文章并点击 删除文章
按钮的时候, CMS 会执行一个 deletePost
的删除动作, 而不是 removePost
.
function deletePost(id) {
return database.find({ id }).delete()
}
- compose
组合数据
基于现有的数据来创建一个新的数据, 适用于字符串、对象和函数.
function composePageUrl(pageName, pageId) {
return `${pageName.toLowerCase()}-${pageId}`
}
- handle
处理数据
处理某个动作, 通常用来声明一个回调函数.
function handleLinkClick() {
console.log('Clicked a link!')
}
link.addEventListener('click', handleLinkClick)
最后是Context
context 是一个域, 指代当前函数的执行环境.
函数通常是处理某件事的一个动作, 所以注明函数所属的域, 或者至少注明一个预期的数据类型是很重要的.
/* 基于内置方法的纯函数 */
function filter(predicate, list) {
return list.filter(predicate)
}
/* 确切的表明这个函数是处理 posts 的 */
function getRecentPosts(posts) {
return filter(posts, (post) => post.date === Date.now())
}
/*
一些编程语言允许你省略 context, 比如在 JavaScript 中 filter 方法只存在于数组上, 所以没必要给函数添加 context.
*/
综上所述
变量命名的5个参考
本区块将着重介绍一些可以提高代码可读性的变量命名技巧.
1. 遵循 S-I-D 原则
命名必须具备简洁(Short
)、直观(Intuitive
)、语义化(Descriptive
)这三个特点.
/* Bad */
const a = 5 // "a" 没有什么含义
const isPaginatable = (postsCount > 10) // "Paginatable" 听起来很不自然
const shouldPaginatize = (postsCount > 10) // "Paginatize" 编造一个动词更加荒谬!
建议:
/* Good */
const postsCount = 5
const hasPagination = (postsCount > 10)
const shouldDisplayPagination = (postsCount > 10) // alternatively
2. 避免缩写
不要使用缩写形式, 这会降低代码可读性. 起一个简短并且语义化的名称可能有点难度, 但是不要让这成为你使用缩写的借口, 举个例子:
/* Bad */
const onItmClk = () => {}
/* Good */
const onItemClick = () => {}
3. 避免重复的上下文
如果命名中的 context 存在与否不会降低其可读性的话, 最好还是移除它:
class MenuItem {
/* 命名中的 "MenuItem" 与类名的域重复 */
handleMenuItemClick = (event) => { ... }
/* 去掉 "MenuItem", 看起来更简洁 */
handleClick = (event) => { ... }
}
4. 应该映射预期结果
/* Bad */
const isEnabled = (itemsCount > 3)
return <Button disabled={!isEnabled} />
/* Good */
const isDisabled = (itemsCount <= 3)
return <Button disabled={isDisabled} />
5. 考虑单数和复数形式
为变量命名的时候也要考虑单数和复数形式, 这取决于当前的变量拥有单个还是多个值.
我写这篇文章是受到这个 Github 仓库的启发, 同时感谢大家的阅读, 编码愉快!