【譯】JavaScript 代碼整潔之道-變量篇
本文源于翻譯 Clean Code Applied to JavaScript — Part II. Variables
這篇文章是 “代碼整潔” 系列中的一篇,這一系列文章都在探討如何編寫(xiě) JavaScript 整潔代碼。在本系列的文章中,我們將討論程序員都應(yīng)知應(yīng)會(huì)的、應(yīng)用于 JavaScript/TypeScript 語(yǔ)言的代碼整潔之道。
簡(jiǎn)介
在這篇文章中,我們將介紹書(shū)寫(xiě)整潔代碼的基本技巧和建議,并重點(diǎn)關(guān)注于代碼中的最基本的元素 -- 變量。
本文中所有示例都是用 JavaScript 來(lái)實(shí)現(xiàn),但是這些良好的實(shí)踐適用于任何編程語(yǔ)言,包括 “最接近硬件”(CTM) 的編程語(yǔ)言。為什么要特別強(qiáng)調(diào)這一點(diǎn)呢?曾經(jīng)和同事討論過(guò),他們?cè)诠ぷ髦惺褂?C 或 Go 語(yǔ)言做開(kāi)發(fā),但是并不喜歡將這些實(shí)踐應(yīng)用于開(kāi)發(fā)中,他們甚至認(rèn)為在他們使用的編程語(yǔ)言中 “沒(méi)有人” 會(huì)這樣做。然后我的回答是,只有行動(dòng)起來(lái)才能有所改變。盡管如此,我們還是對(duì)這些實(shí)踐的優(yōu)缺點(diǎn)做了較長(zhǎng)時(shí)間的討論。
接下來(lái),我們開(kāi)始介紹如何將這些技巧應(yīng)用在程序中的變量上。
變量名要名副其實(shí)
變量的名稱(chēng)必須能夠描述出該變量的作用和用途。也就是說(shuō),我們不應(yīng)該用 x 或 y 這樣的名字去作為變量名,除非我們正在開(kāi)發(fā)數(shù)學(xué)軟件,在數(shù)學(xué)的語(yǔ)境中,這些名字是準(zhǔn)確的。在其他的情況下,應(yīng)該避免使用這種變量名,因?yàn)樗鼰o(wú)法描述出該變量的真實(shí)用途,使代碼難于理解。
首先,如果我們調(diào)用一個(gè)名字為 x 的變量,我們能否立刻知道它用來(lái)做什么事情嗎?不能!
其次,我們繼續(xù)保留名稱(chēng),并為它添加注釋。不過(guò)我們?yōu)槭裁匆o一個(gè)不規(guī)范的變量名去添加注釋呢? 很顯然,這個(gè)問(wèn)題有更簡(jiǎn)單的解決方案,那就是給變量取一個(gè)合適的名稱(chēng)。
現(xiàn)在有趣的事情來(lái)了,父母給孩子起個(gè)名字需要多久?與之類(lèi)似,要找到合適的變量名稱(chēng),我們同樣需要很長(zhǎng)時(shí)間。但最重要的是,在 IDE 的支持下,我們可以不斷地重命名變量,直到找到一個(gè)更恰當(dāng)?shù)拿Q(chēng)。選個(gè)好名字需要花時(shí)間,但磨刀不誤砍柴工,簡(jiǎn)單易懂的名字讓我們很輕易的知道發(fā)生了什么。
const x; // What it is?!
const x; // User information
const user;
變量名可以讀出來(lái)
如果一個(gè)變量的名字有意義,最好的一點(diǎn)就是我們能夠把它的發(fā)音讀出來(lái)。回到整潔代碼的重要實(shí)踐之一,即生成人類(lèi)可讀的代碼,我認(rèn)為能夠讀出變量的發(fā)音是必要的。因此,在這種場(chǎng)合不應(yīng)該使用首字母縮略詞,即使它們看起來(lái)非常的巧妙。
在選擇變量的名稱(chēng)時(shí),另一個(gè)錯(cuò)誤的行為是刪除一個(gè)詞中某些字母,使用這些縮略語(yǔ)讀起來(lái)很困難。首先,我們是在用英語(yǔ)編碼,而且不是所有的開(kāi)發(fā)者都是講英語(yǔ)的。因此,我們?yōu)槭裁匆阉拿譁p少 3 或 4 個(gè)字符?這有什么好處呢?代碼會(huì)被工具(轉(zhuǎn)譯器包括其他語(yǔ)言的編譯器)操作,最終正確地完成格式化(使用 Prettier)。因此,把不能發(fā)音的名字放在一起,只能讓我們更費(fèi)力地去推斷變量的用途。
請(qǐng)不要讓我思考那些不是業(yè)務(wù)邏輯重點(diǎn)的事情?。?/span>
看看下面的第一段代碼,你可能會(huì)推斷出該類(lèi)正在創(chuàng)建的數(shù)據(jù)類(lèi)型,但這很考驗(yàn)團(tuán)隊(duì)同事之間的默契度。在第二段代碼中,它定義了同樣的數(shù)據(jù),但不需要花費(fèi)任何腦力就能讓人知道變量的用途。
class DtaRcrd102 {
private Date genymdhms;
private Date modymdhms;
}
class Customer {
private Date generationTimestamp;
private Date modificationTimestamp;
}
不要在名稱(chēng)中使用變量的類(lèi)型
在變量名稱(chēng)中使用數(shù)據(jù)類(lèi)型作為前綴是一個(gè)很古老的做法,現(xiàn)在讓我們反思一下這個(gè)問(wèn)題。
變量名稱(chēng)中必須要用類(lèi)型作為前綴嗎?
每個(gè)公司和工程都有各自的前綴規(guī)范,那如何去學(xué)習(xí)和書(shū)寫(xiě)這種代碼呢?
如果我在變量的名稱(chēng)中使用一種編程語(yǔ)言的類(lèi)型系統(tǒng),為什么要用它呢?
當(dāng)變量的數(shù)據(jù)類(lèi)型發(fā)生變化時(shí),比如把 Array 修改為 Map,這種情況怎么處理?
這個(gè)前綴能給我們帶來(lái)什么?它是可以發(fā)音的嗎?
如果我們的語(yǔ)言是類(lèi)型化的,我們?yōu)槭裁匆羞@個(gè)前綴呢?但是,即使這個(gè)語(yǔ)言沒(méi)有被類(lèi)型化,就像發(fā)生在 JavaScript 中的那樣。我們?cè)谧兞康拿Q(chēng)中揭示了一個(gè)具體的實(shí)現(xiàn),它將我們與數(shù)據(jù)的類(lèi)型相聯(lián)系。
也就是說(shuō),我們正在將數(shù)據(jù)類(lèi)型與業(yè)務(wù)邏輯關(guān)聯(lián)到了一起。
這是毫無(wú)意義的!
恰恰相反,它使變量無(wú)法發(fā)音,如果我們進(jìn)行改進(jìn)(使我們的代碼適應(yīng)新的數(shù)據(jù)類(lèi)型),我們就必須重新命名所有的代碼。也就是說(shuō),這個(gè)前綴是噪音。
看一下變量定義中的兩個(gè)例子。作為一個(gè)開(kāi)發(fā)者,你真的有必要使用這個(gè)前綴來(lái)理解變量的內(nèi)容嗎?
const aCountries = [];
const sName = '';
const dAmount = 3.2;
const countries = [];
const name = '';
const amount = 3.2;
對(duì)同一類(lèi)型的變量使用相同的詞匯表
這個(gè)建議并不專(zhuān)門(mén)針對(duì)團(tuán)隊(duì)內(nèi)工作的時(shí)候,即使在單獨(dú)生成代碼的時(shí)候也會(huì)有類(lèi)似情況發(fā)生。尤其是在我們作為軟件開(kāi)發(fā)者的職業(yè)生涯的開(kāi)始階段。
對(duì)同一類(lèi)型的數(shù)據(jù)使用相同的詞匯表。如果我們需要檢索一個(gè)用戶(hù)或客戶(hù)的信息,我們不能以不同的方式稱(chēng)呼用戶(hù)或客戶(hù)。也就是說(shuō),不能有時(shí)稱(chēng)其為 user,有時(shí)稱(chēng)其為 customer,甚至是 client 這個(gè)詞。更不可取的是,在變量名稱(chēng)上額外再加一個(gè)后綴。
因此,在軟件開(kāi)發(fā)中,要對(duì)行業(yè)術(shù)語(yǔ)和名詞詞匯進(jìn)行統(tǒng)一的規(guī)范或定義。特別是在團(tuán)隊(duì)開(kāi)發(fā)時(shí),這個(gè)規(guī)范或定義尤為重要,避免讓一群開(kāi)發(fā)者用不同的名字來(lái)指代同一個(gè)概念。
下面的代碼就是很好的示例。同一個(gè)概念,有三個(gè)不同的定義。必須自始至終使用統(tǒng)一的命名,不管是 user、customer 還是 client,只能用同一個(gè)。
getUserInfo();
getClientData();
getCustomerRecord();
getUser();
不要添加不需要的上下文
在變量名稱(chēng)中沒(méi)有必要添加類(lèi)或包的相關(guān)上下文。
在變量名稱(chēng)中添加上下文是很常見(jiàn)的,這樣可以知道這個(gè)變量位于哪個(gè)工作區(qū)。事實(shí)上,當(dāng)我們閱讀代碼時(shí),會(huì)很快意識(shí)到這是完全沒(méi)有必要的。在理解代碼的過(guò)程中,這些不必要的冗余會(huì)帶來(lái)一些干擾。
在下面的例子中,定義了一個(gè)汽車(chē) Car,有三個(gè)基本屬性和一個(gè)方法。在變量包含了上下文的情況下,我們可以觀(guān)察到 car 這個(gè)詞不斷的重復(fù),并且沒(méi)有任何作用。如果我們?nèi)サ?nbsp;car 這個(gè)詞(上下文),代碼也完全可以被理解;事實(shí)上,由于我們?nèi)サ袅诉@些噪音,代碼會(huì)變得更容易理解。
const Car = {
carMake: 'Honda',
carModel: 'Accord',
carColor: 'Blue',
};
function paintCar(car) {
car.carColor = 'Red';
}
const Car = {
make: 'Honda',
model: 'Accord',
color: 'Blue',
};
function paint(car) {
car.color = 'Red';
}
不要使用魔法數(shù)字和字符串
在編寫(xiě)代碼時(shí),不應(yīng)該在源代碼中直接使用數(shù)字或文本字符串,這些通常也被稱(chēng)為魔法數(shù)字。這個(gè)數(shù)字是什么意思?必須要解釋這個(gè)數(shù)字嗎?這讓我們不得不思考業(yè)務(wù)邏輯之外的事情。
因此,這些魔法數(shù)字或字符串必須存儲(chǔ)在常量中,通過(guò)對(duì)常量的名稱(chēng)來(lái)表達(dá)出它們的用途。在業(yè)務(wù)邏輯層面上,對(duì)于那些有意義的數(shù)字或文本字符串,如果沒(méi)有一個(gè)確切的名字就會(huì)引起噪音。
下面的例子中,莫名其妙出現(xiàn)的數(shù)字 86400000,會(huì)讓人丈二和尚 -- 摸不著頭腦。此外,文本字符串也是危險(xiǎn)的,譬如在多次書(shū)寫(xiě) Administrator 過(guò)程中,如果某次將其誤寫(xiě)為 Adminitrator 導(dǎo)致代碼發(fā)生問(wèn)題,卻不易排出錯(cuò)誤。
// What the heck is 86400000 for?
setTimeout(blastOff, 86400000);
user.rol = 'Administrator';
const MILLISECONDS_IN_A_DAY = 86400000;
const ADMINISTRATOR_ROL = 'Administrator';
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
user.rol = ADMINISTRATOR_ROL;
結(jié)論
在我們剛開(kāi)始成為一名開(kāi)發(fā)人員的時(shí)候,我們可能不會(huì)注意到變量的名稱(chēng),因?yàn)橥ǔN覀儎傞_(kāi)始開(kāi)發(fā)腳本或代碼的時(shí)候都是在獨(dú)自工作。并且我們的關(guān)注點(diǎn)都是如何編寫(xiě)代碼,一旦項(xiàng)目被開(kāi)發(fā)出來(lái)之后,我們就不會(huì)再閱讀自己的源代碼。
然而,當(dāng)我們開(kāi)發(fā)一個(gè)需要花費(fèi)相當(dāng)長(zhǎng)時(shí)間維護(hù)的軟件時(shí),我們就需要閱讀和重新讀取源代碼。這時(shí),合理的命名變量將給我們帶來(lái)高質(zhì)量的、整潔的代碼。
在這篇文章中,我們回顧了給變量命名時(shí)的一些基本要點(diǎn)。如果你正在學(xué)習(xí)編程,把它們寫(xiě)下來(lái),因?yàn)樵谖磥?lái)它們將為你節(jié)省許多精力。如果你有一個(gè)漫長(zhǎng)的職業(yè)生涯,你會(huì)發(fā)現(xiàn),通過(guò)不斷地閱讀代碼,你自然而然地就會(huì)得出了這些結(jié)論。
請(qǐng)記住,我們討論的要點(diǎn)如下:
變量名要名副其實(shí)
變量名可以讀出來(lái)
不要在名稱(chēng)中使用變量的類(lèi)型
對(duì)同一變量的類(lèi)型使用相同的詞匯
不要添加不需要的上下文
不要使用魔法數(shù)字和字符串
翻譯:李想
