Brian Yao

【译】命名速查表

本文提供了一些建议来帮助编程者更好地命名变量和函数,包括使用英语、约定命名规则、S-I-D、避免缩写、避免上下文重复、反映预期结果和注意单复数等。希望其中的一些技巧能帮助大家更好的命名,从而提高代码的可读性。

本文翻译自:https://github.com/kettanaito/naming-cheatsheet#naming-cheatsheet

友情提醒:由于本文是翻译而来,所以可能会落后于原文。


给变量和函数命名是很难的,这张表试图让它变得更容易。

尽管这些建议可以适用于任何编程语言,但我将用 JavaScript 的例子来解释和说明。

使用英语

在命名变量和函数时要使用英语。

/* 不好的 */
const primerNombre = 'Gustavo'
const amigos = ['Kate', 'John']

/* 好的 */
const firstName = 'Gustavo'
const friends = ['Kate', 'John']

不管你喜欢与否,英语是编程中的主流语言:所有编程语言的语法都是用英语写的,无数的文档和教学资料也是如此。通过使用英语编写代码,你可以极大地提高代码的凝聚力。

约定命名规则

挑选一个命名规则并遵循它。它可以是 camelCasePascalCasesnake_case 或其他任何规则,只要代码中保持一致即可。许多编程语言在命名规则方面有着自己的约定,你可以查看编程语言文档或研究 Github 上的一些流行的存储库。

/* 不好的 */
const page_count = 5
const shouldUpdate = true

/* 好的 */
const pageCount = 5
const shouldUpdate = true

/* 同样好的 */
const page_count = 5
const should_update = true

S-I-D

名字必须 简短直观 和具有 描述性

  • 简短(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} />

函数命名

A/HC/LC 模式

在给函数命名时有一个可用的公式可以遵循:

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 告诉你组件会自动更新,而你只是控制更新的时间。 换句话说,强语境强调了变量的含义


动作

函数名的动词部分,它是函数名中最重要的部分,负责描述这个函数 了什么。

get

立即访问数据(例如:内部数据的速记器)。

function getFruitCount() {
  return this.fruits.length
}

另见 compose

在执行异步操作时,你也可以使用 get

async function getUser(id) {
  const user = await fetch(`/api/user/${id}`)
  return user
}

set

以声明的方式设置一个变量,将 A 设置为 B

let fruits = 0

function setFruits(nextFruits) {
  fruits = nextFruits
}

setFruits(5)
console.log(fruits) // 5

reset

将一个变量设置为它的初始值或状态。

const initialFruits = 5
let fruits = initialFruits
setFruits(10)
console.log(fruits) // 10

function resetFruits() {
  fruits = initialFruits
}

resetFruits()
console.log(fruits) // 5

remove

从某个地方移走某样东西。

举个例子,如果你在搜索页面上有一个所选过滤器的集合,从集合中移除其中一个过滤器就是 removeFilter,而不是 deleteFilter(这也是英语中的自然表达):

function removeFilter(filterName, filters) {
  return filters.filter((name) => name !== filterName)
}

const selectedFilters = ['price', 'availability', 'size']
removeFilter('price', selectedFilters)

另见 delete

delete

从真实的空间完全擦除某样东西。

想象一下,你是一个内容编辑者。那儿有一个臭名昭著的帖子,你想把它删掉。一旦你点击了一个闪亮的“删除帖子”按钮,CMS 执行的是 deletePost 动作,而不是 removePost

function deletePost(id) {
  return database.find({ id }).delete()
}

另见 remove

移除 还是 删除?

removedelete 之间的区别对你来说不是那么明显时,我建议你看看它们的相反动作—— addcreateaddcreate 的关键区别是 add 需要一个目的地,而 create 不需要目的地。你 add 某样东西 去某个地方,不是 “create去某个地方”。 你只需知道 removeadd 是一对,deletecreate 是一对。

这里有详细的解释。

compose

从现有的数据中创建新的数据。主要适用于字符串、对象或函数。

function composePageUrl(pageName, pageId) {
  return pageName.toLowerCase() + '-' + pageId
}

另见 get

handle

处理一个动作。通常在命名回调方法的时候使用。

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 就没有必要了。


前缀

前缀增强了变量的含义。它很少在函数名中使用。

is

描述了当前环境的特征或状态(通常是布尔值)。

const color = 'blue'
const isBlue = color === 'blue' // 特征
const isPresent = true // 状态

if (isBlue && isPresent) {
  console.log('Blue is present!')
}

has

描述了当前环境是否拥有某个值或某种状态(通常变量值是布尔类型)。

/* 不好的 */
const isProductsExist = productsCount > 0
const areProductsPresent = productsCount > 0

/* 好的 */
const hasProducts = productsCount > 0

should

反映了一个积极的条件语句与某种行动的结合(通常变量值是布尔类型)。

function shouldUpdateUrl(url, expectedUrl) {
  return url !== expectedUrl
}

min/max

代表最小值或最大值。在描述边界或限制时使用。

/**
 * 根据最小/最大的边界范围,渲染随机数量的帖子。
 */
function renderPosts(posts, minPosts, maxPosts) {
  return posts.slice(0, randomBetween(minPosts, maxPosts))
}

prev/next

表明变量在当前环境中的上一个或下一个状态。在描述状态转换时使用。

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']