Javascript 忍者代碼

2023-02-17 10:38 更新
學(xué)而不思則罔,思而不學(xué)則殆??鬃印墩撜Z(yǔ)》

過(guò)去的程序員忍者使用這些技巧,來(lái)使代碼維護(hù)者的頭腦更加敏銳。

代碼審查大師在測(cè)試任務(wù)中尋找它們。

一些新入門的開發(fā)者有時(shí)候甚至比忍者程序員能夠更好地使用它們。

仔細(xì)閱讀本文,找出你是誰(shuí) —— 一個(gè)忍者、一個(gè)新手、或者一個(gè)代碼審查者?

檢測(cè)到諷刺意味
許多人試圖追隨忍者的腳步。只有極少數(shù)成功了。

簡(jiǎn)潔是智慧的靈魂

把代碼盡可能寫得短。展示出你是多么的聰明啊。

在編程中,多使用一些巧妙的編程語(yǔ)言特性。

例如,看一下這個(gè)三元運(yùn)算符 ?'?'?:

// 從一個(gè)著名的 JavaScript 庫(kù)中截取的代碼
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

很酷,對(duì)嗎?如果你這樣寫了,那些看到這一行代碼并嘗試去理解 ?i? 的值是什么的開發(fā)者們,就會(huì)有一個(gè)“快活的”的時(shí)光了。然后會(huì)來(lái)找你尋求答案。

告訴他短一點(diǎn)總是更好的。引導(dǎo)他進(jìn)入忍者之路。

一個(gè)字母的變量

道隱無(wú)名。夫唯道善貸且成。老子(道德經(jīng))

另一個(gè)縮減代碼量的方法是,到處使用單字母的變量名。例如 ?a?、??或 ?c?。

短變量就像森林中真正的忍者一樣,一下就找不到了。沒有人能夠通過(guò)編輯器的“搜索”功能找到它。即使有人做到了,他也不能“破譯”出變量名 ?a? 或 ?b? 到底是什么意思。

……但是有一個(gè)例外情況。一個(gè)真正的忍者絕不會(huì)在 ?"for"? 循環(huán)中使用 ??作為計(jì)數(shù)器。在任何地方都可以,但是這里不會(huì)用。你隨便一找,就能找到很多不尋常的字母。例如 ??或 ?y?。

使用一個(gè)不尋常的變量多酷啊,尤其是在長(zhǎng)達(dá) 1-2 頁(yè)(如果可以的話,你可以寫得更長(zhǎng))的循環(huán)體中使用的時(shí)候。如果某人要研究循環(huán)內(nèi)部實(shí)現(xiàn)的時(shí)候,他就很難很快地找出變量 ??其實(shí)是循環(huán)計(jì)數(shù)器啦。

使用縮寫

如果團(tuán)隊(duì)規(guī)則中禁止使用一個(gè)字母和模糊的命名 — 那就縮短命名,使用縮寫吧。

像這樣:

  • ?list ?→ ?lst?
  • ?userAgent ?→ ?ua?
  • ?browser ?→ ?brsr?
  • ……等

只有具有真正良好直覺的人,才能夠理解這樣的命名。盡可能縮短一切。只有真正有價(jià)值的人,才能夠維護(hù)這種代碼的開發(fā)。

Soar high,抽象化。

大方無(wú)隅,大器晚成,大音希聲,大象無(wú)形。老子(道德經(jīng))

當(dāng)選擇一個(gè)名字時(shí),盡可能嘗試使用最抽象的詞語(yǔ)。例如 ?obj?、?data?、?value?、?item ?和 ?elem ?等。

  • 一個(gè)變量的理想名稱是 ?data?。 在任何能用的地方都使用它。的確,每個(gè)變量都持有 數(shù)據(jù)(data),對(duì)吧?……但是 ?data ?已經(jīng)用過(guò)了怎么辦?可以嘗試一下 ?value?,它也很普遍。畢竟,一個(gè)變量總會(huì)有一個(gè) 值(value),對(duì)吧?
  • 根據(jù)變量的類型為變量命名:?str?、?num?……嘗試一下吧。新手可能會(huì)詫異 — 這些名字對(duì)于忍者來(lái)說(shuō)真的有用嗎?事實(shí)上,有用的!一方面,變量名仍然有著一些含義。它說(shuō)明了變量?jī)?nèi)是什么:一個(gè)字符串、一個(gè)數(shù)字或是其他的東西。但是當(dāng)一個(gè)局外人試圖理解代碼時(shí),他會(huì)驚訝地發(fā)現(xiàn)實(shí)際上沒有任何有效信息!最終就無(wú)法修改你精心思考過(guò)的代碼。我們可以通過(guò)代碼調(diào)試,很容易地看出值的類型。但是變量名的含義呢?它存了哪一個(gè)字符串或數(shù)字?如果思考的深度不夠,是沒有辦法搞明白的。
  • ……但是如果找不到更多這樣的名字呢? 可以加一個(gè)數(shù)字:?data1?, ?item2?, ?elem5?……

注意測(cè)試

只有一個(gè)真正細(xì)心的程序員才能理解你的代碼。但是怎么檢驗(yàn)?zāi)兀?/p>

方式之一 —— 使用相似的變量名,像 ?date ?和 ?data?。

盡你所能地將它們混合在一起。

想快速閱讀這種代碼是不可能的。并且如果有一個(gè)錯(cuò)別字時(shí)……額……我們卡在這兒好長(zhǎng)時(shí)間了,到飯點(diǎn)了 (⊙v⊙)。

智能同義詞

道,可道,非常道。名,可名,非常名。老子《道德經(jīng)》

對(duì) 同一個(gè) 東西使用 類似 的命名,可以使生活更有趣,并且能夠展現(xiàn)你的創(chuàng)造力。

例如,函數(shù)前綴。如果一個(gè)函數(shù)的功能是在屏幕上展示一個(gè)消息 — 名稱可以以 ?display…? 開頭,例如 ?displayMessage?。如果另一個(gè)函數(shù)展示別的東西,比如一個(gè)用戶名,名稱可以以 ?show…? 開始(例如 ?showName?)。

暗示這些函數(shù)之間有微妙的差異,實(shí)際上并沒有。

與團(tuán)隊(duì)中的其他忍者們達(dá)成一個(gè)協(xié)議:如果張三在他的代碼中以 ?display...? 來(lái)開始一個(gè)“顯示”函數(shù),那么李四可以用 ?render..?,王二可以使用 ?paint...?。你可以發(fā)現(xiàn)代碼變得多么地有趣多樣呀。

……現(xiàn)在是帽子戲法!

對(duì)于有非常重要的差異的兩個(gè)函數(shù) — 使用相同的前綴。

例如,?printPage(page)? 函數(shù)會(huì)使用一個(gè)打印機(jī)(printer)。?printText(text)? 函數(shù)會(huì)將文字顯示到屏幕上。讓一個(gè)不熟悉的讀者來(lái)思考一下:“名字為 ?printMessage(message)? 的函數(shù)會(huì)將消息放到哪里呢?打印機(jī)還是屏幕上?”。為了讓代碼真正耀眼,?printMessage(message)? 應(yīng)該將消息輸出到新窗口中!

重用名字

始制有名,名亦既有,夫亦將知止,知止可以不殆。老子(道德經(jīng))

僅在絕對(duì)必要時(shí)才添加新變量。

否則,重用已經(jīng)存在的名字。直接把新值寫進(jìn)變量即可。

在一個(gè)函數(shù)中,嘗試僅使用作為參數(shù)傳遞的變量。

這樣就很難確定這個(gè)變量的值現(xiàn)在是什么了。也不知道它是從哪里來(lái)的。目的是提高閱讀代碼的人的直覺和記憶力。一個(gè)直覺較弱的人必須逐行分析代碼,跟蹤每個(gè)代碼分支中的更改。

這個(gè)方法的一個(gè)進(jìn)階方案是,在循環(huán)或函數(shù)中偷偷地替換掉它的值。

例如:

function ninjaFunction(elem) {
  // 基于變量 elem 進(jìn)行工作的 20 行代碼

  elem = clone(elem);

  // 又 20 行代碼,現(xiàn)在使用的是 clone 后的 elem 變量。
}

想要在后半部分中使用 ?elem ?的程序員會(huì)感到很詫異……只有在調(diào)試期間,檢查代碼之后,他才會(huì)發(fā)現(xiàn)他正在使用克隆過(guò)的變量!

經(jīng)??吹竭@樣的代碼,即使對(duì)經(jīng)驗(yàn)豐富的忍者來(lái)說(shuō)也是致命的。

下劃線的樂趣

在變量名前加上下劃線 ?_? 和 ?__?。例如 ?_name? 和 ?__value?。如果只有你知道它們的含義,那就非常棒了?;蛘撸舆@些下劃線只是為了好玩兒,沒有任何含義,那就更棒了!

加下劃線可謂是一箭雙雕。首先,代碼變得更長(zhǎng),可讀性更低;并且,你的開發(fā)者小伙伴可能會(huì)花費(fèi)很長(zhǎng)時(shí)間,來(lái)弄清楚下劃線是什么意思。

聰明的忍者會(huì)在代碼的一個(gè)地方使用下劃線,然后在其他地方刻意避免使用它們。這會(huì)使代碼變得更加脆弱,并提高了代碼未來(lái)出現(xiàn)錯(cuò)誤的可能性。

展示你的愛

向大家展現(xiàn)一下你那豐富的情感!像 ?superElement?、?megaFrame ?和 ?niceItem ?這樣的名字一定會(huì)啟發(fā)讀者。

事實(shí)上,從一方面來(lái)說(shuō),看似寫了一些東西:?super..?、?mega..?、?nice..?。但從另一方面來(lái)說(shuō) — 并沒有提供任何細(xì)節(jié)。閱讀代碼的人可能需要耗費(fèi)一到兩個(gè)小時(shí)的帶薪工作時(shí)間,冥思苦想來(lái)尋找一個(gè)隱藏的含義。

重疊外部變量

處明者不見暗中一物,處暗者能見明中區(qū)事。關(guān)尹子

對(duì)函數(shù)內(nèi)部和外部的變量,使用相同的名稱。很簡(jiǎn)單,不用費(fèi)勁想新的名稱。

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...許多行代碼...
  ...
  ... // <-- 某個(gè)程序員想要在這里使用 user 變量……
  ...
}

在研究 ?render? 內(nèi)部代碼的程序員可能不會(huì)注意到,有一個(gè)內(nèi)部變量 ?user ?屏蔽了外部的 ?user ?變量。

然后他會(huì)假設(shè) ?user ?仍然是外部的變量然后使用它,?authenticateUser() ?的結(jié)果……陷阱出來(lái)啦!你好呀,調(diào)試器……

無(wú)處不在的副作用!

有些函數(shù)看起來(lái)它們不會(huì)改變?nèi)魏螙|西。例如 ?isReady()?,?checkPermission()?,?findTags()……?它們被假定用于執(zhí)行計(jì)算、查找和返回?cái)?shù)據(jù),而不會(huì)更改任何它們自身之外的數(shù)據(jù)。這被稱為“無(wú)副作用”。

一個(gè)非常驚喜的技巧就是,除了主要任務(wù)之外,給它們添加一個(gè)“有用的”行為。

當(dāng)你的同事看到被命名為? is..?、?check..? 或 ?find...? 的函數(shù)改變了某些東西的時(shí)候,他臉上肯定是一臉懵逼的表情 — 這會(huì)擴(kuò)大你的理性界限。

另一個(gè)驚喜的方式是,返回非標(biāo)準(zhǔn)的結(jié)果。

展示你原來(lái)的想法!讓調(diào)用 ?checkPermission ?時(shí)的返回值不是 ?true/false?,而是一個(gè)包含檢查結(jié)果的復(fù)雜對(duì)象。

那些嘗試寫 ?if (checkPermission(..))? 的開發(fā)者,會(huì)很疑惑為什么它不能工作。告訴他們:“去讀文檔吧”。然后給出這篇文章。

強(qiáng)大的函數(shù)!

大道泛兮,其左可右。老子(道德經(jīng))

不要讓函數(shù)受限于名字中寫的內(nèi)容。拓寬一些。

例如,函數(shù) ?validateEmail(email)? 可以(除了檢查郵件的正確性之外)顯示一個(gè)錯(cuò)誤消息并要求重新輸入郵件。

額外的行為在函數(shù)名稱中不應(yīng)該很明顯。一個(gè)真正的忍者會(huì)使它們?cè)诖a中也不明顯。

將多個(gè)行為合并到一起,可以保護(hù)你的代碼不被重用。

想象一下,另一個(gè)開發(fā)者只想檢查郵箱而不想輸出任何信息。你的函數(shù) ?validateEmail(email)? 對(duì)他而言就不合適啦。所以他不會(huì)找你問關(guān)于這些函數(shù)的任何事而打斷你的思考。

總結(jié)

上面的所有“建議”都是從真實(shí)的代碼中提煉而來(lái)的……有時(shí)候,這些代碼是由有經(jīng)驗(yàn)的開發(fā)者寫的。也許比你更有經(jīng)驗(yàn) ;)

  • 遵從其中的一丟丟,你的代碼就會(huì)變得充滿驚喜。
  • 遵從其中的一大部分,你的代碼將真正成為你的代碼,沒有人會(huì)想改變它。
  • 遵從所有,你的代碼將成為尋求啟發(fā)的年輕開發(fā)者的寶貴案例。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)