+5

Thay thế switch statements bằng object trong javascript

Khi mà chúng ta nhúng tay vào code JavaScript, ta sẽ thường xuyên sử dụng các Object (đối tượng), khởi tạo và thao tác với chúng. Các đối tượng thực sự linh hoạt, chúng là trung tâm của hầu hết mọi thứ trong JavaScript. Và nó có liên quan gì tới switch thì chúng ta đọc tiếp 😄

switch statement là gì?

Chắc hẳn là có rất ít người không biết sử dụng switch, nó nhận vào 1 input đầu vào và trả ra 1 output gì đó 😄 giống như cái ví dụ dưới đây:

var type = 'coke';
var drink;

switch(type) {
    case 'coke':
      drink = 'Coke';
      break;
    case 'pepsi':
      drink = 'Pepsi';
      break;
    default:
      drink = 'Unknown drink!';
}

console.log(drink); // 'Coke'

Nó tương tự như câu lệnh if else, nhưng nó sẽ đánh giá một giá trị duy nhất - bên trong switch. Và sử dụng case để đánh giá theo mỗi giá trị đầu vào.

Khi mà đoạn code của ta bắt đầu có 1 tá những if elseelse if thì có vẻ như có gì đó sai sai và chúng ta cần nghĩ đến việc nên chuyển qua dùng switch, nhìn nó sáng sủa hơn là dùng if else :v

Đây là 1 ví dụ mà có vẻ như đang quá lạm dụng if else:

function getDrink(type) {
  let drink;
  if (type === 'coke') {
    drink = 'Coke';
  } else if (type === 'pepsi') {
    drink = 'Pepsi';
  } else if (type === 'mountain dew') {
    drink = 'Mountain Dew';
  } else if (type === 'lemonade') {
    drink = 'Lemonade';
  } else if (type === 'fanta') {
    drink = 'Fanta';
  } else {
    // acts as our "default"
    drink = 'Unknown drink!';
  }
 
  return 'You\'ve picked a ' + drink;
}

Ngta cố gắng dry (dont repeat yourself) còn ví dụ này thì đang kry (keep repeating yourself) :v Cách triển khai vừa rồi thì khá lỏng lẻo, tiềm ẩn nhiều lỗi và dài vl.

switch là công cụ tốt cho công việc của chúng ta, mặc dù ta cần phải thêm các break statement để ngăn chặn đoạn code chạy tiếp và rơi vào các trường hợp khác, đó là một trong nhiều vấn đề của nó.

Vấn đề của switch

Có nhiều vấn đề với switch, từ luồng điều khiển đến cách nhìn không chuẩn của nó, nó xử lý các khối mã, phần còn lại của JavaScript sử dụng dấu ngoặc nhọn nhưng switch thì không. (Ý ở đây là, với những đoạn / khối mã (block code), thì nếu xử lý trong switch, đoạn mã đó sẽ nằm giữa casebreak, trong khi phần còn lại của js, nó sẽ nằm trong {})

Về mặt cú pháp, nó không phải là một trong những cách xử lý JavaScript tốt nhất, cũng không phải là thiết kế của nó: chúng ta buộc phải thêm break statement phía sau mỗi case, chuyện này gây ra nhiều vấn đề phát sinh như lỗi tiềm ẩn có thể xảy ra (quên break, nó nhảy xuống dòng tiếp theo để chạy, thế là chết cmnr) hoặc khó debug.

Như bài trước đã đề cập, chúng ta có thể sử dụng Object để làm nhiều thứ thú vị, vậy tại sao lại ko dùng để thay thế cho switch? Các đối tượng linh hoạt hơn nhiều, có khả năng dễ đọc hiểu, bảo trì tốt hơn và chúng ta không cần phải thêm break 1 cách thủ công cho từng trường hợp, rồi quan tâm đến trường hợp quên thêm break 😄. Nó (object) cũng rất thân thiện với người dùng (coder), vì nó là đối tượng tiêu chuẩn.

Khi mà các trường hợp cần tìm kiếm / so sánh / kiểm tra tăng lên, thì đó là lúc mà sử dụng Object hiệu quả hơn dùng switch, vì 1 bên sử dụng hash table lookup trong khi 1 bên phải so sánh từng trường hợp cho đến khi đúng rồi mới break.

Object Literal lookups

Ok, nói từ nãy rồi là bảo thay thế nọ thay thế kia, mà không nói là thay như thế nào. Thông thường, chúng ta sử dụng chúng cho mục đích Object lookup, là để lấy các giá trị từ các thuộc tính của đối tượng. Tức là như thế này này, quay lại ví dụ ở trên theo 1 cách khác:

function getDrink(type) {
  var drinks = {
    'coke': 'Coke',
    'pepsi': 'Pepsi',
    'lemonade': 'Lemonade',
    'default': 'Default item'
  };
  return 'The drink I chose was ' + (drinks[type] || drinks['default']);
}

var drink = getDrink('coke');
console.log(drink);
// The drink I chose was Coke

Chúng ta đã tiết kiệm được vài dòng code từ switch case và dữ liệu được trình bày rõ ràng hơn rất nhiều. Chúng ta thậm chí có thể đơn giản hóa nó hơn nữa, mà không cần trường hợp mặc định:

function getDrink(type) {
  return 'The drink I chose was ' + {
    'coke': 'Coke',
    'pepsi': 'Pepsi',
    'lemonade': 'Lemonade'
  }[type];
}

var drink = getDrink('zzz');
console.log(drink);
// The drink I chose was undefined

Chúng ta có thể sử dụng cách này theo kiểu phức tạp hơn là chỉ có trả về String, ví dụ đơn giản:

var type = 'coke';

var drinks = {
  'coke': function () {
    return 'Coke';
  },
  'pepsi': function () {
    return 'Pepsi';
  },
  'lemonade': function () {
    return 'Lemonade';
  }
};

Chỉ có 1 chút khác biệt để gọi:

drinks[type]();

=> Dễ bảo trì và dễ đọc hơn. Chúng ta cũng không phải lo lắng về break statement và trường hợp mặc định, đơn giản nó chỉ là một đối tượng.

Quay lại ví dụ, ta có hàm getDrink trong diện mạo mới:

function getDrink(type) {
  var drinks = {
    'coke': function () {
      return 'Coke';
    },
    'pepsi': function () {
      return 'Pepsi';
    },
    'lemonade': function () {
      return 'Lemonade';
    }
  };
 
  return drinks[type]();
}

var drink = getDrink('coke');
console.log(drink); // 'Coke'

ez và đẹp, nhưng chưa có trường hợp default, để thêm vào:

function getDrink(type) {
  var fn;
  var drinks = {
    'coke': function () {
      return 'Coke';
    },
    'pepsi': function () {
      return 'Pepsi';
    },
    'lemonade': function () {
      return 'Lemonade';
    },
    'default': function () {
      return 'Default item';
    }
  };
 
  if (drinks[type]) {
    fn = drinks[type];
  } else {
    fn = drinks['default'];
  }
  
  return fn();
}

var drink = getDrink('dr Thanh');
console.log(drink); // 'Default item'

Vẫn có if else này, sửa lại 1 chút bằng cách sử dụng ||:

function getDrink (type) {
  ...
  
  return (drinks[type] || drinks['default'])();
}

Chúng ta không cần phải có return bên trong hàm, chúng ta có thể thay đổi các tham chiếu đến bất kỳ biến nào, sau đó thì trả về thôi:

function getDrink(type) {
  var drink;
  var drinks = {
    'coke': function () {
      drink = 'Coke';
    },
    'pepsi': function () {
      drink = 'Pepsi';
    },
    'lemonade': function () {
      drink = 'Lemonade';
    },
    'default': function () {
      drink = 'Default item';
    }
  };
    
  (drinks[type] || drinks['default'])();
    
  return 'The drink I chose was ' + drink;
}

var drink = getDrink('coke');
console.log(drink);
// The drink I chose was Coke

Đó là 1 vài cách sử dụng Object để thay thế cho switch case, còn sử dụng tiếp nữa như thế nào thì tùy thuộc vào sự sáng tạo của mỗi người :v

Object Literal “fall through”

Với các trường hợp sử dụng switch, chúng ta có thể cho phép chúng "rơi qua" (fall through) các case khác nhau (có nghĩa là nhiều trường hợp có thể áp dụng cho một đoạn code), ví dụ thế này:

var type = 'coke';
var snack;
switch(type) {
    case 'coke':
    case 'pepsi':
      snack = 'Drink';
      break;
    case 'cookies':
    case 'crisps':
      snack = 'Food';
      break;
    default:
      drink = 'Unknown type!';
}
console.log(snack); // 'Drink'

Chúng ta đã để coke và pepsi "fall through" bằng cách không cho thêm break statement vào sau case. Làm điều này cho Object thì cũng đơn giản nhưng khai báo nhiều hơn 1 chút - cũng như ít bị lỗi hơn. Code của chúng ta trở nên có cấu trúc hơn, dễ đọc và có thể sử dụng lại:

function getSnack(type) {
  var snack;
  
  function isDrink() {
    return snack = 'Drink';
  }
  
  function isFood() {
    return snack = 'Food';
  }
  
  var snacks = {
    'coke': isDrink,
    'pepsi': isDrink,
    'cookies': isFood,
    'crisps': isFood,
  };
  
  return snacks[type]();
}

var snack = getSnack('coke');
console.log(snack); // 'Drink'

Kết luận

  • Vừa rồi chúng ta đã thấy được 1 khía cạnh khá thú vị của Object, thay thế cho câu lệnh switch case, if else
  • Object có một luồng điều khiển khá tự nhiên hơn switch, switch hơi cũ và cồng kềnh, khó khăn trong việc debug.
  • Các đối tượng có khả năng mở rộng hơn, có thể bảo trì và chúng ta có thể kiểm tra chúng tốt hơn rất nhiều.
  • Bài viết lấy cảm hứng và dựa theo nguồn Replacing switch statements with Object literals

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí