不求谌解

不求谌解

💻 Web Dev / Creative 💗 ⚽ 🎧 🏓
twitter
github
jike
email

變量命名的藝術

這是一篇最近翻譯的譯文1(也是我的第二篇譯文👏),在文章的部分地方做了少許改動,同時也在原文的基礎上增加了一些內容。希望不要誤人子弟。Have Fun!

在計算機科學領域裡只有兩件事可以稱之為難事:快取失效(cache invalidation)和命名問題(naming things)。—— Phil Karlton

關於命名這件事,在某些時候的確是非常困難的。但卻值得我們花費精力去研究它。回顧一下,之前你是否看過類似於這樣的代碼?

const convertObj = (x, y, z) => {
    const k = Object.keys(x);
    return k.map((key) => {
        return {
            [y]: key,
            [z]: x[key],
        }
    });
}

你能立刻知道它在做些什麼嗎?當然在你逐行讀完這段代碼後,可能會理解它想表達什麼。但是,如果這段代碼中的變量的命名方式更加優雅的話,我們會更容易理解這段內容。

好的變量命名是非常重要的,尤其是在動態類型語言中,因為沒法用已經定義好的變量基本類型去幫助你理解該變量確切的含義。然而,如果能夠在動態類型的語言中使用好的命名方法,代碼較於靜態類型的語言會變得更加易讀。

接下來我將分享一些有關命名方法的基本規則,這些是我這些年的經驗之談。通過變量不同的基本類型,對比舉出一些例子。就讓我們先從數組開始吧。

Arrays#

數組對象是有序數據的集合,各項的基本類型大致相同。因為數組包含多個變量值,變量的命名應當是有意義的複數形式。

// 很bad很sad很drama
const fruit = ['apple', 'banana', 'cucumber'];
// 凑合
const fruitArr = ['apple', 'banana', 'cucumber'];
// 還不錯
const fruits = ['apple', 'banana', 'cucumber'];
// 很好 - "names"暗示數組內容是字符串(strings)
const fruitNames = ['apple', 'banana', 'cucumber'];
// 優雅
const fruits = [{
    name: 'apple',
    genus: 'malus'
}, {
    name: 'banana',
    genus: 'musa'
}, {
    name: 'cucumber',
    genus: 'cucumis'
}];

Booleans#

布爾類型只有 2 個值,true或者false。變量命名時使用『is』,『has』或者『can』作為前綴,將有助於讀者理解變量的類型。

// bad
const open = true;
const write = true;
const fruit = true;

// good
const isOpen = true;
const canWrite = true;
const hasFruit = true;

當遇到 predicate 函數(該函數返回一個 boolean 值)時,在具名函數之後命名變量會有些煩人。

const user = {
  fruits: ['apple']
}
const hasFruit = (user, fruitName) => {
  user.fruits.includes(fruitName)
}
// 這個時候應該怎麼命名這boolean變量?
const x = hasFruit(user, 'apple');

由於我們已經給函數名稱加了個has的前綴,因此不能再以hasProjectPermission這種形式命名 x 這個 boolean 變量。在這種情況下,可以給hasFruit這個函數加上check或者get來修飾謂語 (has)。

const checkHasFruit = (user, fruitName) => {
  user.fruits.includes(fruitName)
}
const hasFruit = checkHasFruit(user, 'apple');

Numbers#

至於數字類型,想想有哪些描述數字的詞彙。諸如這些詞彙,maximum,minimum,total.

// bad
const pugs = 3;
// good
const minPugs = 1;
const maxPugs = 5;
const totalPugs = 3;

Functions#

函數應當使用動詞和名詞相結合的方式命名,當該函數在對象原型上產生某種行為時,它的名字應當能夠體現出這一點。actionResource就是一個值得借鑒的命名格式。例如:getUser

// bad
userData(userId);
userDataFunc(userId);
totalOfItems(items);
// good
getUser(userId);
calculateTotal(items);

通常情況下,我使用to作為函數名稱的前綴來表示轉換變量的值。

// I like it
toDollors('euros', 20);
toUppercase('a string')

遍歷子項的時候,我經常使用這種慣用的命名方式。當接收函數中的一個參數時,應當使用數組名稱的單數形式。

// bad
const newFruits = fruits.map(x => {
  doSomething(x);
});
// good
const newFruits = fruits.map(fruit => {
  doSomething(fruit)
})

回到開始#

重構一下開頭的那段代碼

const arrayToObject = (array, id, name) => {
  const arrayList = Object.keys(array);
  return arrayList.map((key) => {
      return {
          [id]: key,
          [name]: array[key]
      }
  });
}

三省吾身#

// 之前命名 => rsshub項目
// 1. 獲取文章列表
const list = $('.con_list li h3')
      .find('a')
      .map((i, e) => $(e).attr('href'))
      .get();
// 2. 對遍歷出的文章地址鏈接發出請求
const res = await got.get(itemUrl);

// 改進命名
const articleLists = $('.con_list li h3')
      .find('a')
      .map((i, list) => $(list).attr('href'))
      .get();

const responseData = await got.get(itemUrl);

Footnotes#

  1. 原文鏈接

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。