原文發佈與個人博客 lambdas.dev。html
接上文,在 JS 優雅指南 - 1 中咱們聊到了基礎的 JavaScript 編程指南,包括如何可靠的聲明變量、常量、函數、以及利用函數正確的構建邏輯,如今咱們思考改善代碼質量的第一步,如何命名。git
在閱讀代碼之初,編碼的風格與命名會比抽象方式、設計技巧更令咱們印象深入,不少時候也會爲項目的風格定下基調。好比當你閱讀一些算法工程師寫的代碼塊,經常會見到 單字母變量 、 反覆的聲明 、不知因此的賦值與拷貝 、累贅的條件判斷 等等,咱們不能說這裏有問題,由於他們的代碼大多的的確確是能夠正常工做的,甚至在性能或儲存空間上有一些優點, 但這在工程,特別是大型工程中是不值得稱道的。根據經驗咱們能夠這類代碼歸類爲 "糊屎",充其量是 "糊" 了一灘高性能的 "屎"。程序員
如今咱們作的是脫離 "編程只不過是工具" 的階段,脫離 "糊屎男孩",讓機器面有喜色、富有人性,使閱讀者在某個瞬間切實地感覺到創做者的思惟熱情與審美哲學。github
事實上,你徹底可使用 doSomeThing
代替全部的函數,畢竟它們真的只是提供某些微不足道的功能,但當你有了多個甚至是成百上千的函數時,這是一個災難。 這是一個淺顯易懂的道理,即使是毫無經驗的開發人員也會意識到命名爆炸的問題,他們隱約明白了什麼是好的編程風格,但最後,甚至是大多數都止步於 doSomeThing
到 "優美" 的 某一站上。正則表達式
"這就夠了" —— 他們這樣告訴本身。算法
例 1, 命名只須要有必要的詞,除非有必要,不然不要堆砌數據庫
// bad
const theBook = {}
const _b = {}
const bookObj = {}
const newBook = {}
// good
const book = {}
複製代碼
例 2,可讀的條件判斷編程
// bad
if (username && username.includes('prefix-')) {}
// bad
const prefix = username && username.includes('prefix-')
// bad
const availableName = username && username.includes('prefix-')
// good
const hasPrefixName = username && username.includes('prefix-')
複製代碼
例 3,可讀的函數markdown
當咱們要從網絡上獲取用戶信息時,getUser
就不是一個準確的表達,get
過於寬泛,從數據庫或網絡、以用戶名或 ID 都有區別,如今咱們能夠先從 命名上思考它們的區別:網絡
// bad
const getUser = name => {}
複製代碼
// good
const fetchUsersByName = name => {}
// good
const findOneUserByID = id => {}
// good (any environment, any params)
const getUsers = (...params) => {}
複製代碼
例 4,準確的表達
屬性能夠避免沒必要要的描述,言簡意賅:
// bad
const book = {
bookname: '',
length: '',
}
// good
const book = {
title: '',
pages: '',
}
func(book.title)
複製代碼
注意單複數:
// bad
const book = findBooks()
// good
const books = findBooks()
複製代碼
例 5,沒必要要的約定
一般在示例或無心義的遍歷中,咱們會把每個回調函數的參數寫做 item
/ value
/ v
等等,這在一些場景的確可讓閱讀者忽略掉沒必要要的描述, 專一於邏輯自己,但並不是老是合適的,特別是咱們須要表達狀態時:
// bad
const titles = books
.map(item => item.title)
.filter(item => item.length > 0)
// good
const titles = books
.map(book => book.title)
.filter(title => title.length > 0)
複製代碼
觀察是一個模糊的界定,能夠嘗試讓同事朋友閱讀你的代碼,問問他們的感覺,哪些命名生硬,哪些詞不達意,哪些是感同身受的。 一個可學習的方式是在 github
上閱讀代碼,你也能注意到開發者們情緒的波動,這裏真的須要這些定義嗎、這裏有必要寫的這麼短小嗎、這是否是太 OC
了…… 試圖理解開發者創做時想法是很天然的,這些情緒的波動能讓你明白他們大約處於編程、人生、情感的什麼狀態,有助於你深入的理解接口與設計。
在一個項目中見到 created
時,即可以知道這調用在 create
以後 (而非以前或之中),依次類推即可以有 destroyed
或其餘函數,若是項目來自是富有經驗的開發者, 這些細節會幫助你在代碼中極快的理解做者的構思。
又如在 暴雪的官方 API 文檔 中能夠見到 GetRewardSpell
接口,既然有 reward spell
, 咱們能夠推斷天然是有 GetRewardXP
、GetRewardMoney
一類接口,而它們的參數天然也相差很少。真的是這樣嗎?不,徹底不是, 當你閱讀幾十分鐘的文檔後纔會明白,這些接口創造之初或許有一些設計,但在各個版本的補丁與不斷的重構後已面目全非,儘管命名的結構相差無幾參數卻截然不同。 在不斷的閱讀後將你開始注意到從新命名的接口、參數、返回值彷佛正在朝某個方向改進,他們彷佛在修補一些問題。
在閱讀 暴雪界面源碼 後咱們更能注意到這些細節,一些界面功能被改變了,程序員們只能被迫去修改這些接口, 同時想要與原有的保持的一些同步,有時也會衍生出一些新的接口,它們被置於一些結構體中,隨着時間的推移能夠推理, 將來的一些接口也會被移入這些結構體 —— 這就是咱們對於命名的觀察與思考。我曾經見過有人說 "狗是人類的朋友,taobao 的文檔連狗都不如", 這就是他觀察的結果了,雖然使人不那麼開心。
咱們能夠判定 巧妙 是優雅命名中重要的一部分,如在 rvm
與其餘一些命令行工具中的 uninstall 就是 implode
,這有點意思,是吧。但事實上我見到 絕大多數的巧妙不過是 "投機取巧",他們苦心孤詣的做品是一大段沒有說明的八進制、二進制代碼,一堆三元堆砌的單行邏輯,一些諧音名字,不合時宜地正則表達式等等,這 並不漂亮,這是歧路。
例 1,自做聰明的諧音
// bad
const markdown2html = template => {}
// good
const markdownToHTML = template => {}
複製代碼
例 2,能夠語義化的正則
代碼並不是越簡短越好,多數場景下咱們須要儘量的避免長篇累牘,但必要時,可使用命名和語義化的代碼塊來試圖說明邏輯:
// bad
const isUser = /^name/.test(user.name) && /^http/.test(user.blog)
// good
const isUserName = user.name.startsWith('name')
const isUserBlog = user.blog.startsWith('http')
const isUser = isUserName && isUserBlog
複製代碼
例 3,優美
// bad
const hasDirOrCreateDir = path => {}
// good
const ensureDir = path => {}
複製代碼
// bad
const moveCursorToLastLine = () => {}
// good
const cursorUp = () => {}
const cursorDown = () => {}
複製代碼