前端開發變量命名系列 - JavaScript篇
JavaScript作為前端開發從業人員必須掌握的3大基礎知識中最重要的一環,也是平是接觸時間最長、寫得最多的。在開發過程中必然會遇到命名的問題,你會詞窮、糾結、惆悵嗎?本文的出現相信能夠解決大部分煩惱,讓你輕松寫出符合規范、易讀、簡短的代碼。
本文將通過大量的實例來試圖自圓其說,形成一套系統化、實用的變量命名理化體系。通過按JavaScript的數據類型分類著手、細到一個函數的參數命名,并提供眾多可選方案,并盡量給出其適用范圍和利弊。
需要注意的是由于個人寫作水平、和知識有限,很多方面敘述上有些生硬,在分類上也沒有什么特別的依據,文章也沒有人審稿,所以有什么紕漏還請留言告知。由于寫作倉促,內容可能不全,后續會隨著工作和學習的深入而不斷地調整和更新。
布爾值(Boolean)命名
Boolean值是兩種邏輯狀態的變量,它包含兩個值:真和假。在JavaScript中對應 true 和 false,在實踐中通常使用數字1表示真值,0來表示假值。
雖然Boolean的狀態只有兩種但是在命名時可以進一步分類,這里給出幾種場景:
- 場景一:表示可見性、進行中的狀態
解釋:可見性在通常指頁面中的元素、組件是否顯示(或者組件掛載到DOM上,但并不可見)。進行中主要是說明某種狀態是處于持續進行中。
推薦命名方式為 is + 動詞(現在進行時)/形容詞,同時這種方式也可以直接不寫 is,但是為了更好的作區分,建議還是加上。
- {
- isShow: '是否顯示',
- isVisible: '是否可見',
- isLoading: '是否處于加載中',
- isConnecting: '是否處于連接中',
- isValidating: '正在驗證中',
- isRunning: '正在運行中',
- isListening: '正在監聽中'
- }
注意: 在Java中使用這種方式是有一定副作用的,為什么請移步:為什么阿里巴巴禁止開發人員使用 “isSuccess” 作為變量名?
- 場景二:屬性狀態類
解釋:通常用來描述實體(例如:HTML標簽、組件、對象)的功能屬性,而且定法比較固定。
- {
- disabled: '是否禁用',
- editable: '是否可編輯',
- clearable: '是否可清除',
- readonly: '只讀',
- expandable: '是否可展開',
- checked: '是否選中',
- enumberable: '是否可枚舉',
- iterable: '是否可迭代',
- clickable: '是否可點擊',
- draggable: '是否可拖拽'
- }
- 場景三:配置類、選項類
解釋:主要是指組件功能的開啟與關閉,功能屬性的配置。
這是一種比較常見的情景,目前命名方式也有很多種,但是歸納起來也不多。推薦使用 withXx 來表示組件在基本功能形態外的其它功能,比如組件的基礎功能到高級功能的開啟;使用 enableXx 來表示組件某些功能的開啟;使用 allowXx 來表示功能屬性的配置;使用 noXx 用于建議功能使用者這個不建議開啟。
- {
- withTab: '是否帶選項卡',
- withoutTab: '不帶選項卡',
- enableFilter: '開啟過濾',
- allownCustomScale: '允許自定義縮放',
- shouldClear: '是否清除',
- canSelectItem: '是否能選中元素',
- noColon: '不顯示label后面的冒號',
- checkJs: '檢查Js',
- emitBOM: 'Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.'
- }
注意:如果嫌分類太多,可以只使用其中一種方式,比如在Typescript中使用了 allownXx 和 noXx。
除了上面這些帶有特定的前置介詞、動詞方式外還有一些在語義上帶有疑問性質的組合通常也是作為Boolean命名的一種參考。
- {
- virtualScroll: '是否啟用虛擬滾動模式',
- unlinkPanels: '在范圍選擇器里取消兩個日期面板之間的聯動',
- validateEvent: '輸入時是否觸發表單的校驗'
- }
函數命名
函數在不同的上下文中的叫法也不一樣,在對象中稱為方法,在類中有構造函數、在異步處理時有回調函數,還有立即執行函數、箭頭函數、柯里函數等。
函數命名的方式常常是和業務邏輯耦合在一起的,但是在命名規則上也有一些常見的模式可以遵循。
- 場景一:事件處理
事件處理函數是前端平時用到最多的,包括瀏覽器原生事件、異步事件和組件自定義事件。在寫法上最常見的兩種命名分別為 onXx、onXxClick和handleXx、handleXxChange。
這里如何在二者之間選擇,可以從二方面來歸類。一是,原生事件采用 onXx,而自定義事件使用 handleXx。二是,事件主動監聽采用 onXx,被動處理使用 handleXx。
從實踐及三大主流框架的文檔關于事件部分的內容來看,推薦使用 handleXx 這種方式,而在表單提交的時候通常采用 onSubmit 函數。
其實,在實際項目中很少嚴格這樣來命名事件處理函數,因為這種方式有一定的局限性,比如點擊按鈕打開一個對話框,使用 handleOpenDlg 和 onOpenDlg 都沒有直接寫 openDlg 方便,如果頁面有多個不同功能的對話框采用這種方式會顯得變量名過長,而handle和on就顯得沒有必要了,比如 hanldeOpenCommentDlg 就沒有 openCommentDlg 直白。
下面列出了一些約定成俗的適用例子:
- {
- onSubmit: '提交表單',
- handleSizeChange: '處理分頁頁數改變',
- handlePageChange: '處理分頁每頁大小改變',
- onKeydown: '按下鍵'
- }
- 場景二:異步處理
這里主要是指在寫數據層服務、狀態管理中的Action命名,以及Ajax回調的命名規則。
- {
- getUsers: '獲取用戶列表',
- fetchToken: '取得Token',
- deleteUser: '刪除用戶',
- removeTag: '移除標簽',
- updateUsrInfo: '更新用戶信息',
- addUsr: '添加用戶',
- createAccount: '創建賬戶'
- }
命名主要圍繞數據的增刪查找來劃分,獲取數據通常是 getXx 和 fetchXx,在作者看來兩者在使用上的區分在于 getXx 的數據來源不一定直接取自異步的原始數據,可能是加工處理后的,而 fetchXx 是直接拿的原始數據。當然在實際項目中并沒有區分,看個人喜好。
deleteXx 主要用于數據刪除,而 removeXx 在語義上是移除數據,通常情況數據是還存在的,只是沒有顯示或數據假刪除。updateXx 是指數據更新操作,addXx 是在已有列表數據中添加新的子項、而createXx 主要是創建新的實例,比如新建一個賬戶。
- 場景三: 跳轉路由
在實際開發過種中,比如在使用react-router/vue-router時,在模板或者JSX中可以直接在標簽中寫上目標地址,但有些時候跳轉的目標地址是經過判斷或者組合后的,并且通過事件觸發跳轉的,這個時候就需要一個函數來處理,那么在函數命名的時候可以考慮使用
- {
- toTplDetail: '跳轉到模板詳情頁面',
- navigateToHome: '導航到首頁',
- jumpHome: '跳轉首頁',
- goHome: '跳轉首頁'
- }
推薦使用 toXx 和 goXx 這兩種方式,如果不是在當前頁面打開/跳轉頁面,可以使用 toBlankTplDetail 或者 goBlankHome 這種方式來進一步語義化。如果前端頁面是位于Webview中,也可以考慮采用 toNativeShare 這種方式來命名。
- 場景四:框架相關特定方法
這里主要是針對前端3大主流流行框架,有一些命名是帶有特定標識符的,還有就是一些生命周期的命名方式。
- {
- formatTimeFilter: '在AngularJs和Vue中,通常用于過濾器命名',
- storeCtrl: '用于AngularJs定義控制器方法',
- formatPipe: '用于Angular中,標識管道方法',
- $emit: 'Vue中的實例方法',
- $$formatters: 'AngularJs中的內置方法',
- beforeCreate: 'Vue的生命周期命名',
- componentWillMount: 'React生命周期命名',
- componentDidMount: 'React生命周期命名',
- afterContentInit: 'Anuglar生命周期命名',
- afterViewChecked: 'Angula生命周期命名',
- httpProvider: 'AngularJs服務',
- userFactory: '工廠函數',
- useCallback: 'React鉤子函數'
- }
- 場景五:數據的加工
這類場景在處理列表的時候經常會碰到,比如排序、過濾、添加額外的字段、根據ID和索引獲取特定數據等。
情形一:根據特定屬性獲取屬性
這里可以參考DOM方法的命名,比如:getElememtById()、getElementsByTagName()、getElementsByClassName() 和 getElementsByName(),然后提煉出一個比較實用的模板:getXxByYy()。其中 Xx 可以是:element, item, option, data, selection, list, options 以及一些特定上下文的名字,比如:user(s), menu(s) 等。Yy 相對來說比較固定,經常用到的就是 id, index, key, value, selected, actived 等。
除了使用 get,還可以使用 query 和 fetch,但是需要注意和上面提到的異步數據處理作一個區分。
- {
- getItemById: '根據ID獲取數據元素',
- getItemsBySelected: '根據傳入的已選列表ID來獲取列表全部數據',
- queryUserByUid: '根據UID查詢用戶'
- }
注意:在 getItemsBySelected 這種情形下直接寫成 getItemsSelected 更好,但只適用于 Yy 為形容詞的場景
情形二:格式化數據
這里的格式化操作包括排序、調整數據結構、過濾數據、添加屬性。命名通常使用 formatXx, convertXx, inverseXx, toggleXx, parseXx, flatXx, 也可以結合數組的一些操作方法來命名,比如 sliceXx, substrXx, spliceXx, sortXx, joinXx 等來命名。
- {
- formatDate: '格式化日期',
- convertCurrency: '轉換貨幣單位',
- inverseList: '反轉數據列表',
- toggleAllSelected: '切換所有已選擇數據狀態',
- parseXml: '解析XML數據',
- flatSelect: '展開選擇數據',
- sortByDesc: '按降序排序'
- }
數組命名
數組的命名推薦使用復數形式來命名,還有就是名詞和具有列表意思的單詞組合。常見的詞匯有 options, list, maps, nodes, entities, collection 等。
- {
- users: '用戶列表',
- userList: '用戶列表',
- tabOptions: '選項卡選項',
- stateMaps: '狀態映射表',
- selectedNodes: '選中的節點',
- btnGroup: '按鈕組',
- userEntities: '用戶實體'
- }
選項元素、下拉元素命名
主要針對的是在下拉選擇框、選項卡元素、Radio、Checkbox等數據源每個選項數據的命名。常見的詞匯有:title, name, key, label, field, value, id, children, index, nodes 等。
基中 title/name/key/label/field 作為選項顯示名, value/id 用于唯一標識選項,children/nodes 用于包含子節點內容。如果選項元素的語義很明確的情況下也可以直接使用特定單詞來代替提到的這些泛指的詞匯,例如菜單列表就可以使用 menu 來作為顯示名。
- // 最常見組合
- {
- title: '標題',
- value: 'ID值'
- }
- // 組合二
- {
- label: '標簽名',
- value: 'ID值'
- }
- // 組合三
- {
- name: '元素名',
- id: 'ID值'
- }
- // 組合四
- {
- field: '字段',
- value: '標識',
- index: '索引'
- }
當前選項、激活項命名
適用列表的選中項、菜單選中項、步驟操作的當前進行步驟、頁面路由當前路由等的命名。
- {
- activeTab: '當前選中選項卡',
- currentPage: '當前頁',
- selectedData: '當前選項中數據',
- }
臨時數據、比對數據命名
針對在代碼中有時會使用一些臨時的變量來存儲數據、保存數據快照的場景下的命名。
- {
- swapData: '臨時交換數據',
- tempData: '臨時數據',
- dataSnapshot: '數據快照'
- }
數據循環語句中變量命名
在使用 for 循環時,多層嵌套請依次使用 i/j/k,超過3層可以使用 x/y/z,m/n 來命名。使用 forEach, map, filter 等方法時,每一個元素命名可以根據不同語境下的特殊名字來命名,比如遍歷 menus,那么每個元素可以命名為 menu,不然則使用上下文無關的詞匯,比如:item, option, data, key, object 等。至于索引通常使用 index,如果多層可以使用 index + 數字 的形式,也可以直接使用 i/j/k 來作為索引,甚至還可以使用 subIndex/grandIndex 這種方式來命名。
對于在使用 for 循環時數組長度如果需要單獨命名的話,可以使用 xxlength/xxLens,或者 xxCount。
在循環的過程中通常還會統計某個條件下數據匹配的次數、重復元素數量、記錄中間結果等情況。這里推薦使用 count 表示次數,skipped 表示跳過的數量,result 表示結果。
- // for 循環
- for (let i = 0; i < 10; i++) {
- for (let j = 0; j < 10; j++) {
- for (let k = 0; k < 10; k++) {
- // do something
- }
- }
- }
- for (let i = 0, lens = this.options.length; i < lens; i++) {
- // do something
- }
- // forEach
- users.forEach((item, index) => {
- // do something
- })
- menus.forEach((menu, index) => {
- if (menu.children) {
- menu.children.forEach((subMenu, subIndex) => {
- if (subMenu.children) {
- subMenu.children.forEach((grandMenu, grandIndex) => {
- // 一個不常用的示例
- })
- }
- })
- }
- })
方法參數命名
方法的參數名稱和數量在不同的方法中各不相同,但是還是有一些固定的模式可以參考,比如在Vue中監聽屬性變化的新值和舊值;reduce 方法的上一個值,當前值;回調函數的命名、剩余參數的命名等。
情形一:新值、舊值
常見于Vue中watch 對像中的屬性監聽回調函數,推薦使用
- {
- oldVal: '舊值',
- newVal: '新值'
- }
情形二:上一個值、下一個值和當前值
這種情形見于路由的鉤子方法,Object.assign 數據拷貝的參數。
- // 組合一
- {
- from: '從...',
- to: '到...'
- }
- // 組合二
- {
- prev: '上一個...',
- next: '下一個...',
- cur: '當前'
- }
- // 組合三
- {
- source: '源',
- target: '目標'
- }
- // 組合四
- {
- start: '開始',
- end: '結束'
- }
情形三:異步數據返回
用于Promise的then方法參數,await 的返回的Promise等。可選擇的詞匯有:res, data, json, entity,推薦使用 res。
- demoPromise.then(res => {
- // do something
- })
情形四:其它情況
一些使用不多,但是在編程時約定成俗的命名方式。例如 ch 表示單個字符,str 表示字符串, n 代表次數, reg 表示正則, expr 表示表達式, lens 表示數組長度, count 表示數量, p 表示數據的精度, q 表示查詢(query), src 表示數據源(source), no 表示數字(number), rate 表示比率, status 表示狀態, bool 表示布爾值, arr 表示數組值, obj 表示對象值, x 和 y 表示坐標兩軸, func 表示函數, ua表示User Agent, size 表示大小, unit 表示單位, hoz 表示水平方向, vert 表示垂直方向, radix 表示基數,根
- // 傳入單個字符
- function upper(ch) {}
- // 數量重復
- function repeat(str, n)
- // 正則
- 'abab'.replace(reg, 'bb')
事件命名
這里根據DOM、nodejs和一些框架和UI組件的事件進行歸納
- DOM事件
這里列舉DOM中常見的事件命名
- {
- load: '已完成加載',
- unload: '資源正在被卸載',
- beforeunload: '資源即將被卸載',
- error: '失敗時',
- abort: '中止時',
- focus: '元素獲得焦點',
- blur: '元素失去焦點',
- cut: '已經剪貼選中的文本內容并且復制到了剪貼板',
- copy: '已經把選中的文本內容復制到了剪貼板',
- paste: '從剪貼板復制的文本內容被粘貼',
- resize: '元素重置大小',
- scroll: '滾動事件',
- reset: '重置',
- submit: '表單提交',
- online: '在線',
- offline: '離線',
- open: '打開',
- close: '關閉',
- connect: '連接',
- start: '開始',
- end: '結束',
- print: '打印',
- afterprint: '打印機關閉時觸發',
- click: '點擊',
- dblclick: '雙擊',
- change: '變動',
- select: '文本被選中被選中',
- keydown/keypress/keyup: '按鍵事件',
- mousemove/mousedown/mouseup/mouseleave/mouseout: '鼠標事件',
- touch: '輕按',
- contextmenu: '右鍵點擊 (右鍵菜單顯示前)',
- wheel: '滾輪向任意方向滾動',
- pointer: '指針事件',
- drag/dragstart/dragend/dragenter/dragover/dragleave: '拖放事件',
- drop: '元素在有效釋放目標區上釋放',
- play: '播放',
- pause: '暫停',
- suspend: '掛起',
- complete: '完成',
- seek: '搜索',
- install: '安裝',
- progress: '進行',
- broadcast: '廣播',
- input: '輸入',
- message: '消息',
- valid: '有效',
- zoom: '放大',
- rotate: '旋轉',
- scale: '縮放',
- upgrade: '更新',
- ready: '準備好',
- active: '激活'
- }
- 自定義事件
在封裝組件時提供的事件名除了參考DOM事件外,在命名上也可以參考Github Api 采用 動詞過去時 + Event 的方式
- {
- assignedEvent: '分配事件',
- closedEvent: '關閉事件',
- labeledEvent: '標簽事件',
- lockedEvent: '鎖事件',
- deployedEvent: '部署事件'
- }
此外,很多命名方式可以根據場景使用 元素 + click 、元素 + change 、select + 范圍等方式靈活運用
- {
- selectAll: '選擇所有',
- cellClick: '當某個單元格被點擊時會觸發該事件',
- sortChange: '當表格的排序條件發生變化的時候會觸發該事件'
- }