5 lời khuyên để viết câu điều kiện tốt hơn trong JavaScript

Khi làm việc với JavaScript, chúng ta gặp khá nhiều loại câu điều kiện, sau đây là 5 lời khuyên giúp bạn viết câu điều kiện tốt hơn.

1. Sử dụng Array.includes cho nhiều tiêu chí

2. Ít lồng lại với nhau, Return sớm

3. Sử dụng Function có tham số mặc định và Destructuring

4. Ưu tiên đối tượng chữ viết hơn là lệnh switch

5. Sử dụng Array.every và Array.some cho tất cả hoặc 1 phần tiêu chí

6. Tổng kết

#1. Sử dụng Array.includes cho nhiều tiêu chí

Chúng ta cùng xem ví dụ bên dưới

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

Thoạt nhìn, ví dụ trên có vẻ ổn. Tuy nhiên, điều gì xảy ra nếu chúng ta có nhiều fruit màu đỏ hơn, như "cherry" và "cranberries"? Chúng ta sẽ mở rộng statement với nhiều || hơn?

Chúng ta có thể viết lại câu điều kiện trên bằng cách sử dụng Array.includes (Array.includes)

function test(fruit) {
  // extract conditions to array
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}

Chúng ta lấy ra red fruits (điều kiện) thành một mảng. Bằng cách này code của chúng ta trông sẽ gọn hơn.

#2. Ít lồng lại với nhau, Return sớm

Cùng mở rộng ví dụ trước với 2 điều kiện:

  • Nếu không có fruit cung cấp, trả về lỗi.
  • Chấp nhận và in số lượng fruit nếu vượt quá 10 quả.
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  // condition 1: fruit must has value
  if (fruit) {
    // condition 2: must be red
    if (redFruits.includes(fruit)) {
      console.log('red');

      // condition 3: must be big quantity
      if (quantity > 10) {
        console.log('big quantity');
      }
    }
  } else {
    throw new Error('No fruit!');
  }
}

// test results
test(null); // error: No fruits
test('apple'); // print: red
test('apple', 20); // print: red, big quantity

Nhìn đoạn code bên trên, chúng ta có:

  • 1 lệnh if/else lọc ra trường hợp không hợp lệ.
  • 3 mức lồng nhau nếu câu lệnh (điều kiện 1, 2 & 3).

Nguyên tắc chung của cá nhân tôi là return sớm khi tìm thấy điều kiện không hợp lệ.

/_ return early when invalid conditions found _/

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  // condition 1: throw error early
  if (!fruit) throw new Error('No fruit!');

  // condition 2: must be red
  if (redFruits.includes(fruit)) {
    console.log('red');

    // condition 3: must be big quantity
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}

Bằng cách này, chúng ta có thể hạn chế một chút câu lệnh lồng nhau. Kiều code này rất ổn, đặc biệt là khi bạn có câu lệnh if dài (hãy tưởng tượng bạn phải cuộn xuống dưới cùng để biết ở đó có một câu lệnh khác, not cool).

Chúng ta có thể làm giảm việc lồng nhau trong lệnh if bằng cách đảo ngược các điều kiện và return sớm. Hãy xem 2 loại câu điều kiện bên dưới để xem cách chúng tôi thực hiện:

/_ return early when invalid conditions found _/

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early
  if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red

  console.log('red');

  // condition 3: must be big quantity
  if (quantity > 10) {
    console.log('big quantity');
  }
}

Bằng cách đảo ngược các điều kiện của điều kiện thứ 2, code của chúng ta đã loại bỏ các câu lệnh lồng nhau. Kỹ thuật này rất hữu ích khi chúng ta có mạch logic dài và chúng ta muốn dừng quá trình khi điều kiện không được đáp ứng.

Tuy nhiên, không có quy tắc nào bắt buộc phải làm điều này. Hãy tự hỏi bản thân, kiểu code này có tốt hơn hay dễ đọc hơn kiểu code trước (điều kiên 2 có lồng nhau)?

Đối với tôi, tôi sẽ để nó như code trước (điều kiện 2 có lồng nhau). Đó là vì

  • Code ngắn và thẳng về phía trước, nó rõ ràng hơn với việc lồng if
  • Điều kiện đảo ngược có thể tạo ra nhiều quá trình xử lí

Do đó, luôn luôn hướng tới tối giản việc lồng nhau và return sớm nhưng đừng lạm dụng nó. Có một bài viết thảo luận StackOverflow có nói thêm về chủ để này, nếu bạn quan tâm: 

#3. Sử dụng Function có tham số mặc định và Destructuring

Tôi đoán mã bên dưới trông có vẻ quen thuộc với bạn, chúng tôi luôn luôn cần kiểm tra giá trị "null"/"undefined" và gán giá trị mặc định khi chạy JavaScript.

function test(fruit, quantity) {
  if (!fruit) return;
  const q = quantity || 1; // if quantity not provided, default to one

  console.log(`We have ${q} ${fruit}!`);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

Trong thực tế, chúng ta có thể loại bỏ biến q bằng cách gán function có tham số mặc định.

function test(fruit, quantity = 1) { // if quantity not provided, default to one
  if (!fruit) return;
  console.log(`We have ${quantity} ${fruit}!`);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

Dễ dàng và trực quan hơn phải không? Hãy lưu ý rằng mỗi tham số có thể có tham số hàm mặc định của riêng nó. Ví dụ, chúng ta có thể gán giá trị mặc định cho fruit: function test(fruit = 'unknown', quantity = 1).

Nếu fruit là một object thì sao? Chúng ta có thể gán tham số mặc định không?

function test(fruit) { 
  // printing fruit name if value provided
  if (fruit && fruit.name)  {
    console.log (fruit.name);
  } else {
    console.log('unknown');
  }
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

Nhìn vào ví dụ trên, chúng tôi muốn in tên của fruit nếu nó đã khai báo hoặc nó sẽ in unknow. Chúng ta có thể tránh điều kiện fruit && fruit.name bằng cách kiểm tra với default function parameter & destructing.

// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
  console.log (name || 'unknown');
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

Khi chúng ta chỉ cần thuộc tính từ fruit, ta có thể hủy cấu trúc tham số bằng cách sử dụng {name}, sau đó ta có thẻ sử dụng name như là biến trong code thay vì fruit.name.

Chúng tôi cũng gán Object{} là giá trị mặc định. Nếu ta không làm vậy sẽ bị lỗi khi chạy dòng lệnh test(undefined) - Cannot destructure property name of 'undefined' or 'null'. vì không có name.

Nếu bạn không nhớ sử dụng 3rd party libraries, có một số cách để kiểm tra

  • Sử dụng Lodash get function
  • sử dụng thư viện nguồn mở của Facebook idx

Dưới đây là ví dụ về việc sử dụng Lodash:

// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
  console.log (name || 'unknown');
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

Bạn có thể chạy demo code ở đây. Bên cạnh đó,  nếu bạn là tín đồ của Functional Programming (FP), bạn có thể chọn sử dụng Lodash fp, phiên bản tính năng của Lodash.

#4. Ưu tiên đối tượng chữ viết hơn là lệnh switch

Cùng nhìn ví dụ bên dưới, chúng tôi muốn in fruit theo màu:

function test(color) {
  // use switch case to find fruits in color
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}

//test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']

Đoạn code trên dường như không có gì sai nhưng tôi thấy nó khá dài dòng. Ta cũng có thể đạt được kết quả trên với cú pháp ngắn gọn hơn:

// use object literal to find fruits in color
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };

function test(color) {
  return fruitColor[color] || [];
}

 Ngoài ra bạn có thể sử dụng Map để có kết quả tương tự

// use Map to find fruits in color
  const fruitColor = new Map()
    .set('red', ['apple', 'strawberry'])
    .set('yellow', ['banana', 'pineapple'])
    .set('purple', ['grape', 'plum']);

function test(color) {
  return fruitColor.get(color) || [];
}

Map là loại Object có sẵn từ ES2015 cho phép bạn lưu trữ cặp giá trị key.

Chúng ta có nên cấm sử dụng câu lệnh switch không? Đừng giới hạn bản thân. Cá nhân tôi sử dụng object theo nghĩa đen bất cứ khi nào có thể nhưng không quá cứng nhắc, hãy sử dụng sao cho thật hợp lý.

Todd Motto có một bài viết đào sâu hơn về việc chuyển đổi object theo nghĩa đen, bạn có thể đọc ở đây

TL;DR; Refactor the syntax

Đối với ví dụ trên, ta có thể viết lại code trên với kết quả tương tự bằng cách sử dụng Array.filter:

 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) {
  // use Array filter to find fruits in color

  return fruits.filter(f => f.color == color);
}

Luôn có nhiều cách để cho ra kết quả tương tự. chúng tôi đã chỉ ra 4 trường hợp với kết quả tương tự. Coding is fun!

#5. Sử dụng Array.every và Array.some cho tất cả hoặc 1 phần tiêu chí

Lời khuyên cuối cùng là hãy sử dụng  Javascript Array function để giảm các dòng code. Nhìn vào đoạn code bên dưới, chúng tôi muốn kiểm tra xem tất cả fruit có màu đỏ hay không.

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  let isAllRed = true;

  // condition: all fruits must be red
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }

  console.log(isAllRed); // false
}

Đoạn code trên quá dài, ta có thể giảm các dòng code bằng cách sử dụng Array.every

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  // condition: short way, all fruits must be red
  const isAllRed = fruits.every(f => f.color == 'red');

  console.log(isAllRed); // false
}

Trông gọn hơn nhiều rồi đúng không? Theo cách tương tự, chúng ta muốn test xem nếu bất kì fruit nào có màu đỏ chúng ta có thể sử dụng Arrat.some để có được kết quả đúng trong 1 dòng:

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];

function test() {
  // condition: if any fruit is red
  const isAnyRed = fruits.some(f => f.color == 'red');

  console.log(isAnyRed); // true
}

#6. Tổng kết

Hãy cùng nhau viết những đoạn code thật gọn gàn và dễ hiểu. Tôi hy vọng bạn sẽ tìm được điều gì đó mới mẻ qua bài viết này.

Lập trình Javascript trong năm 2017 Lập trình Javascript trong năm 2017 Đinh Thiên Phúc Blog Home Tạo website tĩnh với Github Tạo website tĩnh với Github Đặng Quang Huy
Đình Quân