我先说个真实经历。上个月接手一个内部系统,代码里有个变量叫data2_final_true_v3,我顺着引用找了二十分钟,最后发现它根本不存“最终数据”,只是某个筛选条件下的临时结果。那一刻我才彻底意识到,变量名从来不是小事。它不只是给电脑看的符号,更是给下一个人——包括三周后的你自己——留下的线索。这篇要整理的是我这些年攒下来的常用变量名组合和命名思路,覆盖后端、前端、算法题和日常脚本,你看完可以直接抄。与其收藏一堆装不下也记不住的规范,不如先把这套高频命名用熟。
1. 接手别人代码时,真正让人崩溃的往往不是算法,而是变量名
1.1 变量名是三层信息的承载体
一个合格的变量名至少要回答三个问题:这个东西是什么、它的类型大概是什么、它的作用是什么。比如userList一看就是用户对象的集合;isLoggedIn一看就是布尔状态;createdAt一看就是时间字段。而不合格的变量名往往三样都不沾边——temp、data、flag,乍一看没什么毛病,但跟着代码读下去,你会一直问自己:它到底在表达什么?
看下面这段 Python 小逻辑:
# 反例:读到 d、n 的时候,大脑要额外做一层翻译 d = get_user_by_id(uid) if d: n = d["name"] show(n) # 正例:名字把上下文讲清楚了 user = get_user_by_id(user_id) if user: user_name = user["name"] show(user_name)两种写法都能跑,但维护成本完全不一样。真实项目里,同一个变量可能被十几个函数引用,如果名字是d,你每次看到它都得往上翻定义;如果名字是user,你几乎不需要思考。变量名是“最便宜的文档”,设计模式、架构文档可能要写几周,但一个变量名只花你几秒,这几秒的回报率高得吓人。
1.2 上下文过期的文档 vs 永远在更新的变量名
很多团队会维护接口文档、业务说明,但文档最大的问题是滞后。代码改了三轮,文档还是第一版。变量名则不同,它跟着代码一起改,永远描述“当前状态”。所以我一直觉得,变量名是项目里最不容易过期的线索。
打个比方,变量名就像冰箱上的标签。你贴“剩菜”可能当天知道,三天后打开冰箱,你完全不知道那盒东西还能不能吃。如果标签写成“红烧肉-0512-可冷冻”,情况就不一样了。代码里类似的“温馨标签”,就是cachedResult、pendingRequest、originalConfig这种名字。它们不需要额外文档,只要存在,就已经在给后来的维护者交代前因后果。
1.3 可搜索性:命名对 debug 的直接影响
还有一个很容易被忽略的价值:变量名能直接决定一段逻辑好不好被定位。假设一个页面状态叫state,你在全局搜state会搜出来几千条结果;如果叫isOrderModalOpen,一个 grep 下去,相关代码全浮出来了。
这其实就是“面向编辑器的命名法”。好的变量名不一定长,但要在全局搜索、IDE 跳转时提供足够精确的入口。从工具角度去看,currentUser、targetUser、selectedUser三个名字虽然都在描述“某个用户”,但搜索结果是分开的、语义定位是清晰的。如果全都叫user,一旦线上问题需要排查,你就只能逐行翻。
// 反例:整个文件到处是 state,搜都搜不过来 const state = { page: 1, size: 20 }; const state2 = { sort: "desc", filter: "paid" }; // 正例:每个变量名都是独立的搜索关键词 const listQuery = { page: 1, size: 20 }; const sortAndFilter = { sort: "desc", filter: "paid" };2. snake_case、camelCase、UPPER_CASE:命名风格不是个人审美,是项目语境
2.1 各种风格的“势力范围”
不同语言社区用不同风格,不是闲得没事,而是为了让阅读者在看到变量的第一眼就判断出它在代码里的身份。我梳理了几种最常见的风格和它们的典型场景:
| 命名风格 | 示例 | 常见使用场景 |
|---|---|---|
| snake_case | user_name | Python、Ruby、PHP、Rust 的变量与函数 |
| camelCase | userName | JavaScript、TypeScript、Java、Kotlin 的变量与函数 |
| PascalCase | UserProfile | 类名、组件名、接口类型名 |
| UPPER_SNAKE_CASE | MAX_RETRY_COUNT | 常量、环境变量、全局配置 |
| kebab-case | user-card | CSS 类名、文件路径、NPM 包名 |
snake_case跟散文一样,用下划线把词隔开,读起来非常接近自然语言;camelCase紧凑,适合在 Java、JavaScript 这类方法名频繁拼接的生态里保持视觉整洁;PascalCase的首字母大写,天然地把“类型”和“实例”区分开,所以类名、组件名基本都归它。
# Python:snake_case 才是社区的主流表达 user_name = "alice" MAX_RETRY_COUNT = 3 def get_user_list(): ...// JavaScript:camelCase 用于变量和函数,PascalCase 用于构造函数和组件 const userName = "alice"; const MAX_RETRY_COUNT = 3; function getUserList() {} class UserProfile {}2.2 选错风格的代价比想象中更大
语言解释器不会因为你用 camelCase 写 Python 就报错,ESLint 也不会因为某个变量不是 camelCase 就崩溃,但团队协作时,混着写会让每个人打开文件都得先反应一下。这种“认知摩擦”看起来很小,累积到一个上千文件的工程里,就是肉眼可见的效率损失。
所以我一直主张:进入一个老项目,先别急着安利你自己的风格,看看这个项目里既有代码怎么命名。如果项目历史遗留全是user_id,你就老老实实跟user_id;如果一个 React 组件的局部状态都是isLoading,你新写的is_loading会显得特别像外星球来的。风格一致性带来的好处,远大于“用了我自己喜欢的风格”带来的那点舒适感。
2.3 常量、布尔值、事件处理函数也有约定俗成
除了变量,还有几类特殊命名的约定,我这里单独列一下,因为它们比普通变量更容易被忽视:
- 常量:用
UPPER_SNAKE_CASE,比如DEFAULT_TIMEOUT、API_BASE_URL。但注意,JavaScript 里的const不一定代表常量语义,const data = fetchData()只是“绑定不可变”,不代表它像配置项一样全局固定,不要动不动就大写。 - 布尔变量:用
is、has、can、should开头,比如isReady、hasPermission、canSubmit、shouldShowTip。它们读出来就是一句判断句,逻辑一目了然。 - 事件处理函数:用
handle或on开头,比如handleClick、onSubmit。在 React 里,onXxx通常表示 props 对外暴露的事件,handleXxx表示内部处理函数,这个区分能让事件流非常清楚。
2.4 命名风格的本质是给大脑做视觉预分组
为什么团队要为了“大小写”这种小事吵架?因为人类阅读代码时,眼球扫到的 token 会先被大脑做一次快速分类。全是PascalCase的地方,你会默认它们是类型;全是UPPER_SNAKE_CASE的地方,你会默认它们是常量;全是camelCase的地方,你会默认它们是普通值。
这种预判速度,是命名风格带来的实打实红利。如果一套代码里风格随心情换,你的大脑就要反复“纠错”,读起来自然又累又慢。所以选风格不是选美,是选一种降低认知负担的编码方案。
3. 高频变量名清单:后端、前端、算法、日常脚本都能直接抄
这一章是整篇的核心。我按功能场景分类,给出一套我个人反复使用、几乎没有翻过车的高频变量名。抄的时候不用记全部,先挑你工作里最常见的三个场景用起来。
3.1 集合与列表:items、records、rows 到底怎么选
处理集合时的命名,最容易犯的错就是一律用list或arr。list只能说明“这是一个列表”,完全没说明里面装的是什么。更好用的方案是直接告诉读者“这里的集合是什么业务对象”。
| 推荐变量名 | 适用场景 |
|---|---|
items | 通用列表,尤其在组件内部、商品列表、文件列表这种不确定具体业务的场景 |
users、orders、products | 具体业务实体列表,后端和前端都通用 |
records | 数据库查询结果、表格数据源,暗示“一行一条记录” |
rows | 数据表格的行集合,尤其适合 CSV、Excel、关系型数据库结果集 |
results | 搜索或计算后得到的输出列表 |
entries | 表单条目、字典条目、日志条目 |
举个例子:在 React 里写一个通用下拉组件,props 里的数据叫items最合适;但如果这个组件专门渲染用户列表,业务组件里应该用users。前者的通用性让它能在任意地方复用,后者的具体性让代码读起来没有歧义。
// 通用组件用 items function Select({ items, selectedValue }) { return items.map((item) => ...); } // 业务页面用具体集合名 const users = await fetchUsers(); const adminUsers = users.filter((user) => user.role === "admin");3.2 请求与响应:request/response、payload、body 的分工
写接口或对接接口时,变量名直接体现了你对 HTTP 模型的理解。request和response是完整的词,适合作用域比较大的场景,比如中间件、拦截器、服务层;req和res是 Express 生态里根深蒂固的缩写,适合短函数参数场景,新人也能一眼看懂,但不要到处用。
// Express 风格:req、res 约定俗成 router.post("/users", (req, res) => { const { name, email } = req.body; const userId = req.params.id; const page = req.query.page; ... });更值得留意的是payload、body、data三兄弟的区别。payload一般指提交给后端的业务负载,比如创建订单时的订单内容;body强调 HTTP 请求体;data太泛,我建议在接口层尽量别用。你可以用userPayload、orderBody、rawResponse、normalizedData来代替。
3.3 状态与开关:isLoading、hasError、canSubmit
前端状态管理里,命名是不是清楚,直接决定一个页面好不好维护。我的默认组合是:
const [isLoading, setIsLoading] = useState(false); const [isSaving, setIsSaving] = useState(false); const [hasError, setHasError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const canSubmit = isLoggedIn && !hasError;这里的规律是:is描述当前是什么状态,has描述有没有什么东西,can描述允不允许做某件事。这组前缀看起来简单,但能避掉大量loading、error混用的混乱。如果状态不止一个,建议用联合类型:
type SubmitStatus = "idle" | "submitting" | "success" | "error"; const [status, setStatus] = useState<SubmitStatus>("idle");status在这里是一个“当前所处的阶段”,比同时维护三四个布尔值好控制得多。
3.4 计算与累计:total、count、sum、average
写统计逻辑时,命名最容易犯的错是不区分“总数”“求和”“平均值”。count是整数个数,total通常指总计值或金额,sum是累加结果,average是均值。把它们混成一个total,很容易在后续维护时踩雷。
total_count = 0 sum_price = 0 for item in items: total_count += 1 sum_price += item.price average_price = sum_price / total_count if total_count else 0要注意单复数:total_count和count是单数概念;counts通常用于统计多个分类各自的数量,比如daily_order_counts。同样,sum是单数,但如果你有多个维度的求和,应该写category_sums,而不是让所有结果都用一个sum反复赋值。
3.5 算法与索引:left、right、mid、index、offset
算法题或者通用算法代码里,命名可以短,但必须语义稳定。二分查找的固定组合是left、right、mid;循环用i、j、k没问题,但循环体一超过五六行,就建议替换成有业务含义的索引名。
int left = 0; int right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; }index和offset我经常会放在一起说,但它们不是一回事。index表示位置,offset表示偏移量。比如翻页场景里,offset是“从第几条开始取”,与limit搭配;数组切片时,startIndex和endIndex更好懂。
3.6 时间、错误与其他通用组合
时间变量名建议统一采用统一的时态后缀,既符合直觉,也方便排序和搜索:
| 业务含义 | 推荐变量名 |
|---|---|
| 创建时间 | createdAt/created_at |
| 更新时间 | updatedAt/updated_at |
| 开始时间 | startTime/startedAt |
| 结束时间 | endTime/endedAt |
| 过期时间 | expireAt/expiresAt |
错误处理相关的命名,我一般这样区分:error是错误对象;errorMessage是展示给用户的文本;errorCode是程序要判断的错误码;failureReason是业务层面的失败原因,比如库存不足、余额不够。这组名字一旦固定下来,错误处理逻辑会清爽很多。
4. 这些变量名一开始觉得挺好,后来基本都改掉了
4.1 data、temp、flag 的滥用
我见过最多的问题变量名就是data、temp、flag。它们不是不能用,而是大部分人都把它们当成了“思想懒惰的挡箭牌”。data描述了一个事实:这是数据。但任何变量都是数据,这句话等于没说。
// 看似格式正确,实际什么都没说 const data = await fetchData(); const filtered = data.filter(...); // 把角色点破 const orders = await fetchOrders(); const paidOrders = orders.filter((order) => order.status === "paid");temp在“交换两个变量”这种极短作用域里可以用,比如temp = a; a = b; b = temp,因为它的生命周期只有三行。但如果一个temp出现在几十行的函数里,还被多次赋值,它基本就是一颗定时炸弹。更好的做法是把它改名为swapBuffer或currentRow,告诉读者“这只是个临时载体,不是业务实体”。
4.2 缩写要有度,别让缩写成为第二门外语
团队协作中,缩写是必要的,但不能缩到只有创始人才懂。usr、pwd、cfg、cnt、idx、mgr这种,如果是局部短作用域倒还好,一旦是全局变量、文件级变量,后面的人看着就头疼。
// 缩写过度 const usr = await getUsr(); const pwd = usr.pwd; // 可读性优先 const user = await getUser(); const password = user.password;我的底线是:如果缩写不能达到“短代码块里闭眼懂”的程度,就写全。req、res、i、id这类属于社区公认知名缩写,可以直接用;自己发明的usr_cfg、tmp_arr这类,最好尽早消灭。
4.3 数字后缀和 Final 系列是“放弃思考”的标志
data1、data2、listFinal、finalFinal这种名字,通常意味着作者已经不再区分变量的真实含义,只想用版本号硬扛。问题在于,数字后缀没有语义,过两个星期你根本分不清data2和data3哪个才是真正要用的。
# 反例 data = load_data() data2 = process(data) data3 = finalize(data2) # 正例 raw_data = load_data() processed_data = process(raw_data) final_data = finalize(processed_data)如果一堆变量共用同一个业务名,最好的办法不是加后缀,而是拆开它们的职责。rawData、filteredData、displayData,每个名字都在告诉读者“我经过了哪一步处理”,这比data2_final有价值多了。
4.4 一个变量复用两种含义:省了命名,亏了可读性
有些代码里,一个变量先被赋值为 A 含义,又被赋值为 B 含义。如果 A 和 B 在业务上有关联,很多人觉得“不换变量名也没什么”。但下一个人读代码时,会默认同一个变量在同一段逻辑里含义是稳定的。
total_price = 0 for price in prices: total_price += price # 反例:total_price 还没用,又被改成平均价 total_price = total_price / len(prices) # 正例:用两个各司其职的变量 sum_price = sum(prices) average_price = sum_price / len(prices) if prices else 0这种“省变量”的写法短期能少敲几个字,长期维护时会造成困惑。特别是排查问题的时候,你可能得在脑子里追踪这个变量每一次赋值的上下文,这比多起一个名字累多了。
5. 把命名方法论从变量延伸到函数、文件和数据库字段
5.1 函数名:动词开头 + 明确宾语
变量名叫好了,函数命名的逻辑可以一脉相承。函数是在“执行一个动作”,所以名字由“动词 + 宾语”组成,比如fetchUserList、saveOrder、calculateTotalPrice。如果是返回布尔值的函数,用is、has、can开头;如果是事件处理函数,用handle或on开头。
def fetch_user_list(): ... def is_valid_email(email): ... def handle_submit(): ...函数名最怕变成process这种“万能动作”。process、handle、do单独用,读者不知道函数在干吗。你至少得说清楚处理的对象,比如processOrder、handleSubmit、renderUserCard。
5.2 组件与文件名:从变量名升级到模块边界
项目后期的维护很多时候不是找变量,而是找文件。文件名的命名一致性能让人更快定位。组件文件用 PascalCase,比如UserList.jsx、OrderDetail.jsx;服务模块用具体功能 + 类型后缀,比如userService.js、orderRepository.ts。
src ├── components │ ├── UserList/ │ └── OrderDetail/ ├── services │ ├── userService.js │ └── orderService.jsCSS 里也有一套经典命名——BEM,块、元素、修饰符用清晰的层级表达关系:
.user-card__title {} .user-card--active {}这套方法的本质和变量名一样:通过命名的结构和前缀,让读者不用看内容也知道“这个类属于谁、是块还是元素、处于什么状态”。
5.3 数据库字段:变量命名和表结构要遥相呼应
数据库字段名在命名上常被忽略,但它和程序变量之间的映射关系非常关键。如果后端用created_at,前端又用createdAt,ORM 转换时就要反复做映射和解释,还容易出 bug。选定一种风格后,尽量让数据库字段名和代码字段名保持对应。
| 场景 | 推荐 |
|---|---|
| 数据表主键 | id |
| 逻辑删除标记 | is_deleted/isDeleted |
| 创建/更新时间 | created_at/updated_at |
| 状态字段 | status,值用active、pending、disabled |
| 金额字段 | total_amount,避免用money |
5.4 提交代码前,用一张自查清单过滤“危险变量名”
命名习惯不是靠一次两篇文章就能养成的,我在实际操作中会用一个最简单的清单来兜底:搜索一下当前改动文件里的data、temp、flag、info,凡是能改成更具体名字的,一律改掉。再把可疑的data1、data2单独看一眼;如果它们同时出现,说明这段逻辑的职责梳理还不够清楚。最后把代码默读一遍,遇到需要停顿猜测的地方,就是需要改名的信号。
这篇合集如果你觉得有用,与其把它放进收藏夹吃灰,不如马上打开自己最近维护的项目,搜一遍data和temp。改掉三五个被滥用的变量名,比看任何规范都更能理解好命名的重要性。
最后说点个人体会。真正好用的变量名,往往是“朴素但具体”的词。你不需要多华丽的英文,只要把业务里最高频的词固定下来,比如user、order、config、error、status,然后配上前后缀和动词,就能应付绝大多数场景。我现在写代码时有个习惯:凡是连续两次需要在文件里搜索一个变量才能搞懂含义,我就停下来把它改名。经验多了之后,这套命名习惯会自动长在身上。希望这份常用变量名合集,能让你少走一段弯路,也让你以后读代码时少翻几次白眼。