1. 程式人生 > >5個技巧讓你更好的編寫 JavaScript(ES6) 中條件語句

5個技巧讓你更好的編寫 JavaScript(ES6) 中條件語句

使用 JavaScript 時,我們經常需要處理很多條件語句,這裡分享5個小技巧,可以讓你編寫更好/更清晰的條件語句。

1.使用 Array.includes 來處理多個條件

我們來看看下面的例子:

JavaScript 程式碼:
// condition
function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}

乍一看,上面的例子看起來似乎沒什麼問題。 但是,如果我們還有更多的紅色水果呢?比如櫻桃(cherry)和蔓越莓(cranberries)。 我們是否要用更多的 ||

 操作符來擴充套件該語句呢?

JavaScript 程式碼:
function test(fruit) {
  // 條件提取到陣列中
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}

我們將紅色水果(條件)提取到一個數組中。這樣做,可以讓程式碼看起來更整潔。

2.減少巢狀,提前使用 return 語句

讓我們擴充套件前面的示例,再包含另外兩個條件:

  • 如果沒有提供水果,丟擲錯誤
  • 接受水果 quantity(數量)引數,如果超過 10,則並列印相關資訊。
JavaScript 程式碼:
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  // 條件 1:fruit 必須有值
  if (fruit) {
    // 條件 2:必須為紅色
    if (redFruits.includes(fruit)) {
      console.log('red');
 
      // 條件 3:數量必須大於 10
      if (quantity > 10) {
        console.log('big quantity');
      }
    }
  } else {
    throw new Error('No fruit!');
  }
}
 
// 測試結果
test(null); // 丟擲錯誤:No fruits
test('apple'); // 列印:red
test('apple', 20); // 列印:red,big quantity

看看上面的程式碼,我們有:
– 1 個 if / else 語句過濾掉無效條件
– 3 層 if 語句巢狀(分別是條件1,2和3)

我個人遵循的一般規則是 在發現無效條件時提前 return

JavaScript 程式碼:
/* 在發現無效條件時提前 return */
 
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  // 條件 1:提前丟擲錯誤
  if (!fruit) throw new Error('No fruit!');
 
  // 條件2:必須為紅色
  if (redFruits.includes(fruit)) {
    console.log('red');
 
    // 條件 3:數量必須大於 10
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}

這樣做,我們可以減少一個巢狀層級。 這種編碼風格很好,特別是當你的 if 語句很長時(想象一下,你需要滾動到最底部才知道那裡有一個 else 語句,這樣程式碼的可讀性就變得很差了)。

如果通過反轉條件並提前 return ,我們可以進一步減少巢狀。 請檢視下面的條件 2 ,看看我們是如何做到的:

JavaScript 程式碼:
/* 在發現無效條件時提前 return */
 
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  if (!fruit) throw new Error('No fruit!'); // 條件 1:提前丟擲錯誤
  if (!redFruits.includes(fruit)) return;  // 條件 2:當 fruit 不是紅色的時候,提前 return
 
  console.log('red');
 
  // 條件 3:必須是大量存在
  if (quantity > 10) {
    console.log('big quantity');
  }
}

通過反轉條件2的條件,我們的程式碼現在沒有巢狀語句了。 當我們有很長的邏輯程式碼時,這種技巧非常有用,我們希望在條件不滿足時停止下一步的處理。

然而,這並不是嚴格的規定。問問自己,這個版本(沒有巢狀)是否要比前一個版本(條件 2 有巢狀)的更好、可具可讀性?

對我來說,我會選擇前一個版本(條件 2 有巢狀)。 這是因為:

  • 程式碼簡短直接,巢狀 if 更清晰
  • 反轉條件可能會引發更多的思考過程(增加認知負擔)

因此,始終追求更少的巢狀,提前 return,但是不要過度。但不要過度。如果您感興趣,這裡有一篇文章和 StackOverflow 的討論, 進一步討論這個話題:

3.使用函式的預設引數 和 解構

我想下面的程式碼可能看起來很熟悉,我們在使用 JavaScript 時總是需要檢查 null / undefined 值並分配預設值:

JavaScript 程式碼:
function test(fruit, quantity) {
  if (!fruit) return;
  const q = quantity || 1; // 如果沒有提供 quantity 引數,則預設為 1
 
  console.log(`We have ${q} ${fruit}!`);
}
 
// 測試結果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

實際上,我們可以通過分配預設函式引數來消除變數 q 。

JavaScript 程式碼:
function test(fruit, quantity = 1) { // i如果沒有提供 quantity 引數,則預設為 1
  if (!fruit) return;
  console.log(`We have ${quantity} ${fruit}!`);
}
 
// 測試結果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

更簡單直觀不是嗎? 請注意,每個函式引數都有自己的預設值。 例如,我們也可以為 fruit 分配一個預設值:function test(fruit = 'unknown', quantity = 1)

如果我們的 fruit 是一個 Object 物件怎麼辦? 我們可以指定預設引數嗎?

JavaScript 程式碼:
function test(fruit) { 
  // 如果有值,則列印 fruit.name
  if (fruit && fruit.name)  {
    console.log (fruit.name);
  } else {
    console.log('unknown');
  }
}
 
//測試結果
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

看看上面的例子,我們想要的是如果 fruit.name 可用則列印水果名稱,否則將列印 unknown 。我們可以使用預設函式引數和解構(destructing) 來避免 fruit && fruit.name 這樣的檢查。

JavaScript 程式碼:
// 解構 —— 只獲得 name 屬性
// 引數預設分配空物件 {}
function test({name} = {}) {
  console.log (name || 'unknown');
}
 
//測試結果
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

由於我們只需要來自 fruit 的 name 屬性,我們可以使用 {name} 來解構引數,然後我們可以在程式碼中使用 name 作為變數來取代fruit.name

我們還將空物件 {} 指定為預設值。 如果我們不這樣做,你將在執行行測試時遇到test(undefined) – Cannot destructure property name of 'undefined' or 'null'.(無法解析’undefined’或’null’的屬性名稱)。 因為 undefined中 沒有 name 屬性。

如果您不介意使用第三方庫,有幾種方法可以減少空檢查:

  • 使用 Facebook 開源的 idx 庫(需搭配 Babeljs)

以下是使用Lodash的示例:

JavaScript 程式碼:
// 引入 lodash 庫,我們將獲得 _.get()
function test(fruit) {
  console.log(_.get(fruit, 'name', 'unknown'); // 獲取 name 屬性,如果沒有分配,則設為預設值 unknown
}
 
//測試結果
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

您可以在這裡 執行演示程式碼 。此外,如果你喜歡函數語言程式設計(FP),您可以選擇使用Lodash fp ,Lodash的函式式能版本(方法名更改為 get 或 getOr)。

4.選擇 Map / Object 字面量,而不是Switch語句

讓我們看看下面的例子,我們想根據顏色列印水果:

JavaScript 程式碼:
function test(color) {
  // 使用 switch case 語句,根據顏色找出對應的水果
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}
 
//測試結果
test(null); // []
test('yellow'); // ['banana', 'pineapple']

上面的程式碼似乎沒有錯,但我覺得它很冗長。使用具有更清晰語法的 object 字面量可以實現相同的結果:

JavaScript 程式碼:
// 使用物件字面量,根據顏色找出對應的水果
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };
 
function test(color) {
  return fruitColor[color] || [];
}

或者,您可以使用 Map 來實現相同的結果:

JavaScript 程式碼:
// 使用 Map ,根據顏色找出對應的水果
  const fruitColor = new Map()
    .set('red', ['apple', 'strawberry'])
    .set('yellow', ['banana', 'pineapple'])
    .set('purple', ['grape', 'plum']);
 
function test(color) {
  return fruitColor.get(color) || [];
}

Map 是 ES2015(ES6) 引入的新的物件型別,允許您儲存鍵值對。

我們是不是應該禁止使用 switch 語句呢? 不要侷限於此。 就個人而言,我儘可能使用物件字面量,但我不會設定硬規則來阻止使用 switch ,是否使用應該根據你的場景而決定。

Todd Motto 有一篇文章深入地研究了 switch語句與物件字面量,你可以在 這裡 閱讀。

重構語法

對於上面的示例,我們實際上可以使用 Array.filter 來重構我們的程式碼,以實現相同的結果。

JavaScript 程式碼:
 const fruits = [
    { name: 'apple', color: 'red' }, 
    { name: 'strawberry', color: 'red' }, 
    { name: 'banana', color: 'yellow' }, 
    { name: 'pineapple', color: 'yellow' }, 
    { name: 'grape', color: 'purple' }, 
    { name: 'plum', color: 'purple' }
];
 
function test(color) {
  // 使用 Array filter  ,根據顏色找出對應的水果
 
  return fruits.filter(f => f.color == color);
}

總有不止一種方法可以達到相同的效果。對於這個例子我們展示了 4 種實現方法。編碼很有趣!

5. 使用 Array.every 和 Array.some 來處理全部/部分滿足條件

最後一個小技巧更多地是利用新的(但不是那麼新的)Javascript Array函式來減少程式碼行。檢視下面的程式碼,我們想檢查所有水果是否都是紅色的:

JavaScript 程式碼:
const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];
 
function test() {
  let isAllRed = true;
 
  // 條件:所有的水果都必須是紅色
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }
 
  console.log(isAllRed); // false
}

程式碼太長了!我們可以使用 Array.every 減少行數:

JavaScript 程式碼:
const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];
 
function test() {
  // 條件:簡短方式,所有的水果都必須是紅色
  const isAllRed = fruits.every(f => f.color == 'red');
 
  console.log(isAllRed); // false
}

乾淨多了對吧?類似的,如果我們想要檢查是否有至少一個水果是紅色的,我們可以使用 Array.some 僅用一行程式碼就實現出來。

JavaScript 程式碼:
const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];
 
function test() {
  // 條件:是否存在紅色的水果
  const isAnyRed = fruits.some(f => f.color == 'red');
 
  console.log(isAnyRed); // true
}

總結

讓我們一起生成更多可讀程式碼。我希望你能在本文中學到一些新東西。

就這樣。編碼快樂!