前端開發變數命名系列 - JavaScript篇

jenemy發表於2019-08-12

JavaScript作為前端開發從業人員必須掌握的3大基礎知識中最重要的一環,也是平是接觸時間最長、寫得最多的。在開發過程中必然會遇到命名的問題,你會詞窮、糾結、惆悵嗎?本文的出現相信能夠解決大部分煩惱,讓你輕鬆寫出符合規範、易讀、簡短的程式碼。

本文將通過大量的例項來試圖自圓其說,形成一套系統化、實用的變數命名理化體系。通過按JavaScript的資料型別分類著手、細到一個函式的引數命名,並提供眾多可選方案,並儘量給出其適用範圍和利弊。

需要注意的是由於個人寫作水平、和知識有限,很多方面敘述上有些生硬,在分類上也沒有什麼特別的依據,文章也沒有人審稿,所以有什麼紕漏還請留言告知。由於寫作倉促,內容可能不全,後續會隨著工作和學習的深入而不斷地調整和更新。

布林值(Boolean)命名

Boolean值是兩種邏輯狀態的變數,它包含兩個值:。在JavaScript中對應 truefalse,在實踐中通常使用數字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中使用了 allownXxnoXx

除了上面這些帶有特定的前置介詞、動詞方式外還有一些在語義上帶有疑問性質的組合通常也是作為Boolean命名的一種參考。

{
  virtualScroll: '是否啟用虛擬滾動模式',
  unlinkPanels: '在範圍選擇器裡取消兩個日期皮膚之間的聯動',
  validateEvent: '輸入時是否觸發表單的校驗'
}

函式命名

函式在不同的上下文中的叫法也不一樣,在物件中稱為方法,在類中有建構函式、在非同步處理時有回撥函式,還有立即執行函式、箭頭函式、柯里函式等。

函式命名的方式常常是和業務邏輯耦合在一起的,但是在命名規則上也有一些常見的模式可以遵循。

場景一:事件處理

事件處理函式是前端平時用到最多的,包括瀏覽器原生事件、非同步事件和元件自定義事件。在寫法上最常見的兩種命名分別為 onXxonXxClickhandleXxhandleXxChange

這裡如何在二者之間選擇,可以從二方面來歸類。一是,原生事件採用 onXx,而自定義事件使用 handleXx。二是,事件主動監聽採用 onXx,被動處理使用 handleXx

從實踐及三大主流框架的文件關於事件部分的內容來看,推薦使用 handleXx 這種方式,而在表單提交的時候通常採用 onSubmit 函式。

其實,在實際專案中很少嚴格這樣來命名事件處理函式,因為這種方式有一定的侷限性,比如點選按鈕開啟一個對話方塊,使用 handleOpenDlgonOpenDlg 都沒有直接寫 openDlg 方便,如果頁面有多個不同功能的對話方塊採用這種方式會顯得變數名過長,而handle和on就顯得沒有必要了,比如 hanldeOpenCommentDlg 就沒有 openCommentDlg 直白。

下面列出了一些約定成俗的適用例子:

{
  onSubmit: '提交表單',
  handleSizeChange: '處理分頁頁數改變',
  handlePageChange: '處理分頁每頁大小改變',
  onKeydown: '按下鍵'
}

場景二:非同步處理

這裡主要是指在寫資料層服務、狀態管理中的Action命名,以及Ajax回撥的命名規則。

{
  getUsers: '獲取使用者列表',
  fetchToken: '取得Token',
  deleteUser: '刪除使用者',
  removeTag: '移除標籤',
  updateUsrInfo: '更新使用者資訊',
  addUsr: '新增使用者',
  createAccount: '建立賬戶'
}

命名主要圍繞資料的增刪查詢來劃分,獲取資料通常是 getXxfetchXx,在作者看來兩者在使用上的區分在於 getXx 的資料來源不一定直接取自非同步的原始資料,可能是加工處理後的,而 fetchXx 是直接拿的原始資料。當然在實際專案中並沒有區分,看個人喜好。

deleteXx 主要用於資料刪除,而 removeXx 在語義上是移除資料,通常情況資料是還存在的,只是沒有顯示或資料假刪除。updateXx 是指資料更新操作,addXx 是在已有列表資料中新增新的子項、而createXx 主要是建立新的例項,比如新建一個賬戶。

場景三: 跳轉路由

在實際開發過種中,比如在使用react-router/vue-router時,在模板或者JSX中可以直接在標籤中寫上目標地址,但有些時候跳轉的目標地址是經過判斷或者組合後的,並且通過事件觸發跳轉的,這個時候就需要一個函式來處理,那麼在函式命名的時候可以考慮使用

{
  toTplDetail: '跳轉到模板詳情頁面',
  navigateToHome: '導航到首頁',
  jumpHome: '跳轉首頁',
  goHome: '跳轉首頁',
  redirectToLogin: '重定向到登入頁',
  switchTab: '切換Tab選項卡',
  backHome: '回到主頁'
}

推薦使用 toXxgoXx 這兩種方式,如果不是在當前頁面開啟/跳轉頁面,可以使用 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,還可以使用 queryfetch,但是需要注意和上面提到的非同步資料處理作一個區分。

{
  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/zm/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 表示物件值, xy 表示座標兩軸, 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 的方式, Visual Studio Code Api的 `on +

{
  assignedEvent: '分配事件',
  closedEvent: '關閉事件',
  labeledEvent: '標籤事件',
  lockedEvent: '鎖事件',
  deployedEvent: '部署事件'
}

此外,很多命名方式可以根據場景使用 元素 + click元素 + changeselect + 範圍等方式靈活運用

{
  selectAll: '選擇所有',
  cellClick: '當某個單元格被點選時會觸發該事件',
  sortChange: '當表格的排序條件發生變化的時候會觸發該事件'
}

狀態管理命名

如果在專案中用到了狀態管理(redux/vuex/ngrx),下面給出一些ActionType,Mutation, Action的命名參考。

// Redux 的 actionType
LOAD_SUCCESS
LOAD_FAIL
TOGGLE_SHOW_HISTORY
ON_PLAY
ON_LOAD_START
FETCH_SONGS_REQUEST
RECEIVE_PRODUCTS

// ngrx
const SET_CURRENT_USER = '[User] Set current';
const ADD_THREAD = '[Thread] Add';
const UPDATE_TRIP_SUCCESS = 'Update [Trip] Success';

其它命名

// 日期、時間
// --------------------------------------------------------
sentAt: '傳送時間'
addAt: '新增時間'
updateAt: '更新時間'
startDate: '開始日期'
endDate: '結束日期'
startTime: '開時時間'
endTime: '結束時間'

參考

文章的寫作過程中參考大量優秀的文章、優秀開源專案的原始碼、流行框架的最佳實踐指南以及一些SDK的介面文件。

相關文章