camelCase 和 PascalCase 有什么区别?小驼峰与大驼峰命名详解

camelCase(小驼峰命名法)和 PascalCase(大驼峰命名法)唯一的区别就是首字母。camelCase 以小写字母开头,后面每个单词的首字母大写(userName、parseResponse)。PascalCase 则把每个单词的首字母都大写,包括第一个单词(UserName、ResponseParser)。按照惯例,PascalCase 用来标示类型(类、接口、枚举、React 组件),camelCase 用来标示值和操作(变量、函数、方法、属性)。
这一个大写字母承载的信息比看上去要多。下面会介绍它传达了什么,哪些语言把它变成了强制规则而不只是约定俗成,以及大家始终没有共识的部分:缩写。
camelCase 与 PascalCase 一览
| camelCase | PascalCase | |
|---|---|---|
| 首字母 | 小写 | 大写 |
| 其他名称 | lowerCamelCase、mixedCase(PEP 8)、camelCasing(Microsoft) | UpperCamelCase、CapWords(PEP 8)、StudlyCaps(PHP 的 PSR-1)、PascalCasing(Microsoft) |
| 通常用于命名 | 变量、函数、方法、属性、参数 | 类、接口、类型别名、枚举、组件 |
| 示例 | orderCount、sendInvoice() | OrderService、InvoiceStatus |
阅读风格指南时要留意用词。很多人把这两种风格都叫做“camel case”,PEP 8 甚至把 CamelCase 列为 CapWords 的别名,而 CapWords 正是它对 PascalCase 的称呼。如果某份指南的意思不够明确,就看看它的示例。
首字母为什么重要:类型与值
有了这个惯例,你读代码时不用查任何东西。在 const invoice = new Invoice(order) 中,你一眼就能看出 Invoice 是用来构造的东西,invoice 是拿到的结果。TypeScript 的函数签名也是同样的道理:在 function total(order: Order): Money 中,小写的名称是值,首字母大写的名称是类型。
它还让同一个单词既能命名类型又能命名值,而不会冲突,这种情况随处可见:user: User、config: Config、request: Request。在区分大小写的语言中,它们是不同的标识符,大写字母告诉读者哪个是哪个。
哪些语言强制执行这条规则
在 JavaScript、Java、C# 和 Python 中,类名的大小写只是惯例,没有什么能阻止你写 class invoice。在 JavaScript 中,你可以用 ESLint 的 new-cap 规则把这个惯例变成检查项:默认情况下,它会标出 new invoice(),也会标出不带 new 就调用首字母大写的函数(如 Invoice())的写法,这通常是个 bug。少数语言和框架更进一步,直接把这条规则内置其中。
Go:首字母就是可见性规则
Go 没有 public 或 private 关键字。包级别的名称、结构体字段或方法,首字母大写就会被导出,首字母小写则只在包内可见。选择 PascalCase 还是 camelCase,就是在选择可见性。
package shapes
import "math"
// Circle is exported, so other packages can use it.
type Circle struct {
Radius float64 // exported field
label string // unexported field
}
// Area is exported.
func Area(c Circle) float64 { return math.Pi * square(c.Radius) }
// square is unexported: only code inside package shapes can call it.
func square(x float64) float64 { return x * x }c := shapes.Circle{Radius: 2}
fmt.Println(shapes.Area(c)) // fine
fmt.Println(shapes.square(2)) // compile error: square is not exported这也是为什么 Effective Go 说,未导出字段 owner 的 getter 应该叫 Owner,而不是 GetOwner。大写字母已经告诉你,这是对外公开的访问方式。
React:组件必须首字母大写
React 的文档说得很直白:组件名称必须以大写字母开头,否则无法正常工作。JSX 会把 <section> 这样的小写标签当作 HTML 元素,把 <OrderSummary /> 这样首字母大写的标签当作你的组件。
Ruby:类名就是常量
在 Ruby 中,任何以大写字母开头的名称都是常量,而类名必须是常量。class Invoice 没问题;class invoice 则是语法错误(“class/module name must be CONSTANT”)。
各语言该用哪一种
- JavaScript 和 TypeScript:变量、函数、方法和属性用 camelCase;类、接口、类型别名、枚举和组件用 PascalCase。
- Java 和 Kotlin:同样的分工,另外真正的常量用大写字母加下划线(
MAX_RETRIES)。 - C#:PascalCase 的使用比其他语言普遍得多。方法、属性、事件、公共字段、命名空间甚至常量都用 PascalCase;camelCase 用于参数和局部变量,私有字段则用
_camelCase。典型的方法签名是这样的:public decimal CalculateTotal(decimal taxRate)。 - Swift:类型和协议用 PascalCase,其他一律用 camelCase。
- Go:看你需要哪种可见性。
- Python:只有类用 PascalCase。函数和变量用 snake_case,PEP 8 只在旧代码中 camelCase 已经是主流风格时才允许使用它。
- PHP(PSR-1):类用 PascalCase,方法用 camelCase。
关于常量、文件名等其他内容,请参阅我们的开发者命名风格速查表。
难点:缩写词与首字母缩略词
到底是 userId 还是 userID?是 HttpClient 还是 HTTPClient?各大风格指南给出的答案并不相同:
| 指南 | 规则 | 示例 |
|---|---|---|
| Google(JavaScript、TypeScript、Java) | 把缩写当作普通单词 | xmlHttpRequest、customerId、loadHttpUrl |
| Microsoft .NET | 两个字母的缩写保留两个大写字母;更长的缩写只大写首字母 | IOStream、ioStream、HtmlTag |
| Go | 每个缩写内部保持统一的大小写 | ServeHTTP、appID、xmlHTTPRequest |
| Swift | 通常以大写书写的缩写,要么全部大写,要么全部小写 | utf8Bytes、userSMTPServer |
| Rust | 把缩写当作一个单词 | Uuid、Stdin |
| Python(PEP 8) | 在 CapWords 中把整个缩写大写 | HTTPServerError |
| Airbnb JavaScript | 全部大写或全部小写 | SMSContainer、httpRequests |
我们的看法是:如果你所用语言的指南有相关规则,就照着做,因为标准库和同事都默认如此。如果没有,就把缩写当作单词(HttpClient、userId)。这样两个缩写相邻时,单词边界依然清晰(比较一下 XMLHTTPRequest 和 XmlHttpRequest),名称在不同命名风格之间自动转换时也不会出错。就连浏览器自带的 XMLHttpRequest,也在一个名称里混用了两种做法。
想了解更多背景,Microsoft 的大写约定是一个很好的例子,在一个页面里写下了一整套完整的规则。
camelCase 与 PascalCase 的相互转换
对于简单的名称,转换只涉及一个字符:改变首字母的大小写。
const toPascal = (s) => s.charAt(0).toUpperCase() + s.slice(1);
const toCamel = (s) => s.charAt(0).toLowerCase() + s.slice(1);
toPascal("orderTotal"); // "OrderTotal"
toCamel("OrderTotal"); // "orderTotal"
toCamel("HTMLParser"); // "hTMLParser" (acronyms need a smarter split)最后一行说明了为什么工具要先把名称拆分成单词。下面是 ConvertCaseHQ 大小写转换器对一些棘手输入的处理结果:
| 输入 | camelCase | PascalCase |
|---|---|---|
user id | userId | UserId |
XMLHttpRequest | xmlHttpRequest | XmlHttpRequest |
getUserID | getUserId | GetUserId |
IOError | ioError | IoError |
iPhone | iPhone | IPhone |
它会在已有的大写字母处拆分,并把缩写当作单词来写,这与 Google 的规则一致。如果你的项目遵循 Go、.NET 或 PEP 8 的缩写规则,之后要手动修正这些名称(在 Go 中,getUserId 要改成 getUserID)。品牌名是另一个陷阱:没有哪个算法知道 IPhone 看起来不对。
两个容易犯的小错误
第一个是把实例名首字母大写:const Cart = new ShoppingCart() 会让 Cart 看起来像另一个类,下一个读代码的人会去找它的定义。第二个是在 JavaScript 中用 PascalCase 命名辅助函数,比如 function FormatPrice()。除非它是构造函数或 React 组件,否则读者会以为它需要配合 new 或 JSX 使用,如果两者都不用就直接调用它,new-cap 就会报错。
一条经验法则
如果它是类型,或者是要用 new 构造的东西,就用 PascalCase。如果它是值或操作,就用 camelCase。缩写问题交给你所用语言的指南来决定,其余的交给 linter 去记。关于 JavaScript 的具体规则,包括常量和私有字段,请参阅我们的 JavaScript 命名指南。


