基于语法的命名约定
基本规则
语义数据类型
总而言之...
我最近读了@rachelsoderberg的一篇关于如何写好变量名的文章。在文章中,她讨论了编写描述性变量名所涉及的许多策略和注意事项。
这绝对是一篇很棒的文章,但读完之后,我突然意识到,在编程中完成这个看似平凡的任务是多么困难。作为程序员,我们经常为变量命名而苦恼,这并不是因为它本身很难,而是因为我们必须确保我们选择的名称清晰、描述性强,并且易于维护,以便下一个阅读我们代码的人(可能是我们自己,也可能不是)能够理解。
为了节省时间和精力,我在所有项目中都使用了一个简单的命名约定。今天,我想把它分享给社区,这样我们就可以少花点时间思考变量名了。
注意:我在文章中使用的代码示例是用 JavaScript 编写的,但它们适用于任何编程语言,因为它们毕竟只是命名约定。
基本规则
所有变量、函数、参数和标识符都以camelCase
除非你是一个Rustacean。常量以 写入SCREAMING_CASE
。做出这种区分很重要,这样我们才能判断哪些变量在本质上和设计上是不可变的和只读的。
在强烈鼓励(甚至强制)使用不可变变量的编程语言中,我们必须区分不可变变量和真正的常量。
任何不依赖于运行时变量(例如用户输入和其他动态值)的静态值都可以归类为真常量。例如, 的值PI
被视为真常量,因此必须将其写为SCREAMING_CASE
。否则,camelCase
用来表示可变变量和不可变变量,它们存储临时变量、别名、计算结果以及运行时变量的输出。
// Immutable Variables
const userInput = document.getElementsByTagName('input')[0].value;
const hasDevto = /dev\.to/g.test(userInput);
// True Constants
const WEBSITE_NAME = 'dev.to';
const TAU = 2 * Math.PI;
不过,需要注意的是,上下文很重要。区分不可变变量和真正常量的标准可能会根据具体情况而变化。例如,如果要在整个程序中将其视为静态值(即使它在不同的设备上每次运行时可能会有所不同),则可以使用SCREAMING_CASE
for userInput
。归根结底,作为程序员,我们需要自行决定将哪些变量作为不可变变量或真正常量进行通信。
语义数据类型
数据类型传达了可以对某个变量执行哪些方法和操作。因此,我们最好在命名变量时考虑其类型系统,尤其是在弱类型语言中。这样做有助于我们暗示变量可能具有的数据类型及其相应的方法、属性和操作。反过来,这会使代码更具可读性。
数字、字符串和对象
大多数情况下,数字、字符串和单个对象都用最合适的单数名词来命名。
const usernameInputField = document.getElementById('username-field');
const username = nameInputField.value;
const hypotenuse = Math.sqrt(a**2 + b**2);
const profileData = {
name: 'Presto',
type: 'Dog'
};
布尔值
布尔值的名称通常采用是或否问题的形式,就好像我们亲自询问布尔变量本身的状态一样。
// Yes-or-no questions
const isDog = true;
const hasJavaScriptEnabled = false;
const canSupportSafari = false;
const isAdmin = false;
const hasPremium = true;
// Functions or methods that return booleans
// are also named in a similar fashion
function isOdd(num) { return Boolean(num % 2); }
数组和集合
数组和其他类似集合的数据结构(例如Map
和)使用 中最合适的复数名词Set
命名。如果名词的复数和单数形式过于相似,我们可以用合适的集合名词替代复数形式。这样,这些名词对应的单数形式就可以在迭代过程中用作变量名。camelCase
// We use plural or collective nouns for arrays.
const dogs = [ 'Presto', 'Lucky', 'Sparkles' ];
// We can use the singular form of the
// variable name of the array
// in callback functions.
dogs.forEach(dog => console.log(dog));
// We can also use it in `for...of` loops.
for (const dog of dogs)
console.log(dog);
// Here, we can use collective nouns
// for better readability.
const herdOfCows = [ 'Bessie', 'Bertha', 'Boris' ];
herdOfCows.forEach(cow => console.log(cow));
for (const cow of herdOfCows)
console.log(cow);
功能
函数的编写旨在将其与操作关联起来。因此,它们通常由两部分组成:及物动词和直接宾语。换句话说,函数的名称通常采用 的形式。这告诉我们,该名称是一个命令verb + noun
,或者更确切地说是一个函数,我们可以随时调用它。
function getSum(a, b) { return a + b; }
function findBanana(str) { return str.indexOf('banana'); }
function getAverage(numbers) {
const total = numbers.reduce((prev, curr) => prev + curr);
return total / numbers.length;
}
PowerShell是 Linux 中Bash的 Windows 对应版本,它是强制执行函数(或语言中称之为cmdlet
s )命名约定的语言的一个很好的例子。
下面的脚本计算当前正在运行的所有 Chrome 进程分配的总内存。语法可能不太友好,但 PowerShellverb + noun
对cmdlet
s 的命名约定显而易见。下面的示例仅使用了Get-Process
、Where-Object
和s,但请放心,PowerShell 提供的其他 s 也遵循命名约定。此网站列出了所有 s 以供参考。Measure-Object
cmdlet
cmdlet
# Get all processes currently running
$processes = Get-Process;
# Filter to retrive all Chrome processes
$chromeProcesses = $processes | Where-Object { $_.ProcessName -eq 'chrome' }
# Sum up all of the memory collectively
# allocated for the Chrome processes
$memoryUsage = $chromeProcesses | Measure-Object WorkingSet64 -Sum;
# Log the result to the console
"{0:F2} MB used by Chrome processes." -f ($memoryUsage.Sum / 1mb);
课程
类的命名采用适当的专有名词PascalCase
。这告诉我们,该变量与程序中遵循命名约定的任何其他变量不同camelCase
;相反,它是一个特殊的变量,用于存储具有特殊属性和方法的用户定义类型。
class User { }
class Admin extends User { }
class Moderator extends Admin { }
class Player extends User { }
类字段和方法
类字段根据前面讨论过的不变性和数据类型约定来命名。
另一方面,类方法的命名方式与函数类似。它们仍然遵循verb + noun
约定,但在某些情况下,可以省略名称中的直接宾语(名词)部分。因此,及物动词(动作)的执行者被隐含为拥有该对象方法的类的对象实例。
// Class
class Player {
constructor(name) {
// String
this.username = name;
// Number
this.level = 100;
// Boolean
this.isAdmin = false;
// Array
this.weapons = [
'bow',
'sword',
'spear'
];
}
// Class Method (with noun)
initiateBattle() { }
// Class Method (without noun)
attack() { }
}
总而言之...
const TRUE_CONSTANT = Math.PI;
const stringName = '';
const numberName = 0;
const isBooleanName = true;
const objName = { };
const arrayNames = [ ].map(name => name);
function getFunctionName() { }
class ClassName { }
上面的代码片段简洁地概括了我的整个命名约定。很明显,英语的语法规则和语义极大地影响了这一约定。接受这些约定并以某种方式将它们与编程联系起来,使得命名变量和暗示其数据类型的行为比以往任何时候都更加直观。
如果我们愿意,可以简单地在所有变量前加上其数据类型的缩写——类似于用形容词来描述名词——但这样做会导致变量名变得冗长,如下例所示。我们最好使用 TypeScript 来进行显式类型注解。
// This is... eww. ❌
const NUM_TAU = 2 * Math.PI;
const str_Username = 'Some Dood';
const num_Hypotenuse = Math.sqrt(num_A**2 + num_B**2);
const boo_AdminStatus = false;
const obj_ProfileData = { };
const arr_Articles = [ ];
function fun_GetUser() { }
class Cls_Class { }
例如,命名变量是编程中最令人沮丧的方面之一
调试旁边遵循基于语法的命名约定无疑会让命名过程不那么令人不快。得益于这种命名约定的语言学渊源,我们默认能够编写更直观、更易读的代码,这始终是一件好事。当然,我们仍然需要确保软件的设计和架构本身并非“糟糕”,但至少我们可以放心,我们的代码对于接下来使用它的人来说确实是直观且易读的。
编程语言之所以被称为语言是有原因的......
文章来源:https://dev.to/somedood/a-grammar-based-naming-convention-13jf