这里是 Naming cheatsheet 的中文翻译版本。
给变量和函数命名是很难的,这张表试图让它变得更容易。
尽管这些建议可以适用于任何编程语言,但我将用 JavaScript 的例子来解释和说明。
在命名变量和函数时要使用英语。
/* 不好的 */
const primerNombre = 'Gustavo'
const amigos = ['Kate', 'John']
/* 好的 */
const firstName = 'Gustavo'
const friends = ['Kate', 'John']
不管你喜欢与否,英语是编程中的主流语言:所有编程语言的语法都是用英语写的,无数的文档和教学资料也是如此。通过使用英语编写代码,你可以极大地提高代码的凝聚力。
挑选一个命名规则并遵循它。它可以是 camelCase
、PascalCase
、snake_case
或其他任何规则,只要代码中保持一致即可。许多编程语言在命名规则方面有着自己的约定,你可以查看编程语言文档或研究 GitHub 上的一些流行的存储库。
/* 不好的 */
const page_count = 5
const shouldUpdate = true
/* 好的 */
const pageCount = 5
const shouldUpdate = true
/* 同样好的 */
const page_count = 5
const should_update = true
名字必须 简短、 直观 和具有 描述性。
- 简短(Short):名字必须不需要花很长时间来输入,因此也不需要被记住。
- 直观(Intuitive):名字必须读起来自然,尽可能地接近常用说法。
- 描述性(Descriptive):名字必须以最有效的方式反映它所做的/所拥有的。
/* 不好的 */
const a = 5 // "a" 可以指代任何东西
const isPaginatable = a > 10 // "Paginatable" 听起来不自然
const shouldPaginatize = a > 10 // paginatize 是自创的动词
/* 好的 */
const postCount = 5
const hasPagination = postCount > 10
const shouldPaginate = postCount > 10 // 和 hasPagination 二选一
避免使用缩写,它们只会降低代码的可读性。找到一个简短的、具有描述性的名字可能很难,但这不是使用缩写的借口。
/* 不好的 */
const onItmClk = () => {}
/* 好的 */
const onItemClick = () => {}
名字不应该与它所定义的上下文重复。如果删除上下文不降低名字的可读性,就一定要把它从名字中删除。
class MenuItem {
/* 方法名与上下文("MenuItem")重复 */
handleMenuItemClick = (event) => { ... }
/* `MenuItem.handleClick()` 看起来舒服很多 */
handleClick = (event) => { ... }
}
名字应该反映出预期的结果。
/* 不好的 */
const isEnabled = itemCount > 3
return <Button disabled={!isEnabled} />
/* 好的 */
const isDisabled = itemCount <= 3
return <Button disabled={isDisabled} />
在给函数命名时有一个可用的公式可以遵循:
prefix? + action (A) + high context (HC) + low context? (LC)
在下面的表格中看看这个模式如何被应用。
名字 | 前缀 | 动作 (A) | 强语境 (HC) | 弱语境 (LC) |
---|---|---|---|---|
getUser |
get |
User |
||
getUserMessages |
get |
User |
Messages |
|
handleClickOutside |
handle |
Click |
Outside |
|
shouldDisplayMessage |
should |
Display |
Message |
注意:上下文的顺序会影响变量的含义。例如,
shouldUpdateComponent
意味着你要手动更新组件,而shouldComponentUpdate
告诉你组件会自动更新,而你只是控制更新的时间。
换句话说,强语境强调了变量的含义。
函数名的动词部分,它是函数名中最重要的部分,负责描述这个函数 做 了什么。
立即访问数据(例如:内部数据的速记器)。
function getFruitCount() {
return this.fruits.length
}
另见 compose。
在执行异步操作时,你也可以使用 get。
async function getUser(id) {
const user = await fetch(`/api/user/${id}`)
return user
}
以声明的方式设置一个变量,将 A 设置为 B。
let fruits = 0
function setFruits(nextFruits) {
fruits = nextFruits
}
setFruits(5)
console.log(fruits) // 5
将一个变量设置为它的初始值或状态。
const initialFruits = 5
let fruits = initialFruits
setFruits(10)
console.log(fruits) // 10
function resetFruits() {
fruits = initialFruits
}
resetFruits()
console.log(fruits) // 5
从某个地方移走某样东西。
举个例子,如果你在搜索页面上有一个所选过滤器的集合,从集合中移除其中一个过滤器就是 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()
}
另见 remove。
移除
还是删除
?当
remove
和delete
之间的区别对你来说不是那么明显时,我建议你看看它们的相反动作——add
和create
。add
和create
的关键区别是add
需要一个目的地,而create
不需要目的地。你add
某样东西 去某个地方,不是 “create
它 去某个地方”。 你只需知道remove
与add
是一对,delete
与create
是一对。这里有详细的解释。
从现有的数据中创建新的数据。主要适用于字符串、对象或函数。
function composePageUrl(pageName, pageId) {
return pageName.toLowerCase() + '-' + pageId
}
另见 get。
处理一个动作。通常在命名回调方法的时候使用。
function handleLinkClick() {
console.log('Clicked a link!')
}
link.addEventListener('click', handleLinkClick)
函数所操作的域。
函数通常是对 某物 的操作。重要的是要说明它可操作的域,或者至少说明预期的数据类型是什么。
/* 使用原语操作的纯函数 */
function filter(list, predicate) {
return list.filter(predicate)
}
/* 对文章精确处理的函数 */
function getRecentPosts(posts) {
return filter(posts, (post) => post.date === Date.now())
}
在一些特定的语言中可能允许省略上下文。例如,在 JavaScript 中,
filter
通常是对 Array 的操作。添加明确的filterArray
就没有必要了。
前缀增强了变量的含义。它很少在函数名中使用。
描述了当前环境的特征或状态(通常是布尔值)。
const color = 'blue'
const isBlue = color === 'blue' // 特征
const isPresent = true // 状态
if (isBlue && isPresent) {
console.log('Blue is present!')
}
描述了当前环境是否拥有某个值或某种状态(通常变量值是布尔类型)。
/* 不好的 */
const isProductsExist = productsCount > 0
const areProductsPresent = productsCount > 0
/* 好的 */
const hasProducts = productsCount > 0
反映了一个积极的条件语句与某种行动的结合(通常变量值是布尔类型)。
function shouldUpdateUrl(url, expectedUrl) {
return url !== expectedUrl
}
代表最小值或最大值。在描述边界或限制时使用。
/**
* 根据最小/最大的边界范围,渲染随机数量的帖子。
*/
function renderPosts(posts, minPosts, maxPosts) {
return posts.slice(0, randomBetween(minPosts, maxPosts))
}
表明变量在当前环境中的上一个或下一个状态。在描述状态转换时使用。
async function getPosts() {
const prevPosts = this.state.posts
const latestPosts = await fetch('...')
const nextPosts = concat(prevPosts, latestPosts)
this.setState({ posts: nextPosts })
}
像前缀一样,变量名可以是单数或是复数,这取决于变量是有一个值还是多个值。
/* 不好的 */
const friends = 'Bob'
const friend = ['Bob', 'Tony', 'Tanya']
/* 好的 */
const friend = 'Bob'
const friends = ['Bob', 'Tony', 'Tanya']