Kinh nghiệm xử lý câu lệnh điều kiện trong JavaScript

Nếu bạn là một lập trình viên thích viết clean code, cố gắng viết code một cách ngắn gọn nhất có thể thì việc tối ưu hóa các khối lệnh điều kiện là điều cơ bản quan trọng trong JavaScript. Bằng cách thay đổi điều kiện check, bạn có thể giản lược rất nhiều đoạn code của mình cũng như giúp source code của bạn trở nên rõ ràng hơn. Bài viết dưới đây, mình sẽ chia sẻ một vài kinh nghiệm nhỏ giúp bạn cấu trúc lại những đoạn câu lệnh if/ else trong JavaScript một cách hiệu quả nhé.

Không nên sử dụng các điều kiện phủ định

Bạn không nên sử dụng điều kiện phủ định (giá trị false) cho các câu lệnh if của mình; điều này đơn giản giúp cho việc đọc lại source code của bạn sẽ trở nên tự nhiên hơn. Thêm vào đó, với các biến boolean, không cần thiết phải viết điều kiện check với giá trị true/ false, sử dụng toán tử phủ định sẽ giúp ngắn gọn cho dòng code của bạn.

Không nên viết code thế này:

const isEmailNotVerified = (email) => {
  // implementation
}
if (!isEmailNotVerified(email)) {
  // do something...
}
if (isVerified === true) {
  // do something...
}

Mà hãy chuyển sang cách viết như dưới đây:

const isEmailVerified = (email) => {
  // implementation
};
if (isEmailVerified(email)) {
  // do something...
}
if (isVerified) {
  // do something...
}

Hãy sử dụng Array.includes để gộp nhiều điều kiện

Giả sử chúng ta muốn kiểm tra xem mẫu xe đầu vào là renault hay peugeot, điều kiện if có thể xử lý đơn giản như sau:

const checkCarModel = (model) => {
  if (model === "renault" || model === "peugeot") {
    console.log("model valid");
  }
};

checkCarModel("renault"); // outputs 'model valid'

Trong trường hợp nếu chúng ta cần check trong 1 tập hợp nhiều mẫu xe khác nhau, lúc này cách viết trên sẽ khiến code của bạn trở nên dài và cồng kềnh hơn. Để giải quyết vấn đề này, chúng ta sử dụng Array.includes để kiểm tra xem trong mảng đầu vào có giá trị cần tìm hay không.

const checkCarModel = (model) => {
  if (["peugeot", "renault"].includes(model)) {
    console.log("model valid");
  }
};

checkCarModel("renault"); // outputs 'model valid'

Tinh chỉnh thêm đoạn code một chút nữa, ta có thể tách biệt phần khai báo dữ liệu và phần xử lý logic (kiểm tra điều kiện), code sẽ trở nên sáng sủa hơn rất nhiều.

const checkCarModel = (model) => {
  const models = ["peugeot", "renault"];

  if (models.includes(model)) {
    console.log("model valid");
  }
};

checkCarModel("renault"); // outputs 'model valid'

  Các cách xóa một property của Object trong Javascript

  Kinh nghiệm truy xuất giá trị trong object lồng nhau trong Javascript

Hãy sử dụng Array.every hoặc Array.find để bắt điều kiện khớp tất cả tiêu chí

Ở ví dụ dưới đây, chúng ta cần kiểm tra xem danh sách các xe đầu vào (cars) có đều thuộc về cùng một hãng xe (model) được nhập vào hay không. Để thực hiện việc kiểm tra, đoạn code dưới đây sử dụng một vòng lặp for để duyệt qua tất cả các phần tử trong mảng; biến isValid được dùng làm biến để trả về giá trị cho hàm check.

const cars = [
  { model: "renault", year: 1956 },
  { model: "peugeot", year: 1968 },
  { model: "ford", year: 1977 },
];

const checkEveryModel = (model) => {
  let isValid = true;

  for (let car of cars) {
    if (!isValid) {
      break;
    }
    isValid = car.model === model;
  }

  return isValid;
};

console.log(checkEveryModel("renault")); // outputs false

Để viết code một cách gọn gàng hơn, chúng ta có thể sử dụng phương every có sẵn trong lớp Array. Hàm này trả về true nếu như tất cả các phần tử trong mảng đều thỏa mãn điều kiện truyền vào.

const checkEveryModel = (model) => {
  return cars.every((car) => car.model === model);
};

console.log(checkEveryModel("renault")); // outputs false

Bạn cũng có thể sử dụng phương thức find để kiểm tra xem có tồn tại phần tử nào mà giá trị model không giống với giá trị đầu vào hay không. Kết quả trả về có ý nghĩa tương tự.

const checkEveryModel = (model) => {
  return cars.find((car) => car.model !== model) === undefined;
};

console.log(checkEveryModel("renault")); // outputs false

Về mặt hiệu năng, cả 3 cách trên đều cho kết quả như nhau vì cả hai hàm everyfind đều thực hiện vòng lặp gọi từng phần tử của mảng, cũng đều trả về false ngay lập tức nếu tìm thấy giá trị điều kiện sai.

Sử dụng Array.some để check điều kiện tồn tại

Tương tự như cách sử dụng Array.every, chúng ta có thể sử dụng Array.some cho việc kiểm tra xem có tồn tại phần tử nào trong mảng thỏa mãn điều kiện truyền vào hay không. Ví dụ dưới đây kiểm tra xem có mẫu xe “renault” (giá trị đầu vào) nào trong danh sách xe (cars) ban đầu hay không.

const cars = [
  { model: "renault", year: 1956 },
  { model: "peugeot", year: 1968 },
  { model: "ford", year: 1977 },
];

const checkForAnyModel = (model) => {
  return cars.some((car) => car.model === model);
};

console.log(checkForAnyModel("renault")); // outputs true

Kiểm tra điều kiện và return hàm sớm

Nhiều bạn có thói quen khi viết một function sẽ tạo biến giữ giá trị trả về của hàm, sau đó lần lượt qua các câu lệnh if khác nhau, biến đó sẽ được thay đổi, gán giá trị và cuối cùng trả về qua câu lệnh return ở cuối hàm. 

const checkModel = (car) => {
  let result; // first, we need to define a result value

  // check if car exists
  if (car) {
    // check if car model exists
    if (car.model) {
      // check if car year exists
      if (car.year) {
        result = `Car model: ${car.model}; Manufacturing year: ${car.year};`;
      } else {
        result = "No car year";
      }
    } else {
      result = "No car model";
    }
  } else {
    result = "No car";
  }

  return result; // our single return statement
};

console.log(checkModel()); // outputs 'No car'
console.log(checkModel({ year: 1988 })); // outputs 'No car model'
console.log(checkModel({ model: "ford" })); // outputs 'No car year'
console.log(checkModel({ model: "ford", year: 1988 })); // outputs 'Car model: ford; Manufacturing year: 1988;'

Bạn có thể thấy rằng cách viết trên khiến đoạn code của chúng ta trở nên rất dài và khó đọc. Các điều kiện if lồng nhau khiến việc debug luồng chạy của chương trình trở nên khó khăn hơn. Để giải quyết vấn đề này, bạn nên dùng kết hợp việc return sớm theo từng điều kiện, kết hợp sử dụng toán tử ba ngôi (if/ else rút gọn) để giúp code của bạn clear hơn.

const checkModel = ({ model, year } = {}) => {
  if (!model && !year) return "No car";
  if (!model) return "No car model";
  if (!year) return "No car year";

  // here we are free to do whatever we want with the model or year
  // we made sure that they exist
  // no more checks required

  // doSomething(model);
  // doSomethingElse(year);

  return `Car model: ${model}; Manufacturing year: ${year};`;
};

console.log(checkModel()); // outputs 'No car'
console.log(checkModel({ year: 1988 })); // outputs 'No car model'
console.log(checkModel({ model: "ford" })); // outputs 'No car year'
console.log(checkModel({ model: "ford", year: 1988 })); // outputs 'Car model: ford; Manufacturing year: 1988;'

Sử dụng Indexing hoặc Maps thay thế cho câu lệnh Switch

Ví dụ dưới đây, hàm getCarsByState trả về các hãng xe theo giá trị quốc gia truyền vào, trong đó giá trị trả về dưới dạng một mảng các tên hãng xe. Sử dụng cấu trúc switch là một cách giải quyết tự nhiên cho bài toán này.

const getCarsByState = (state) => {
  switch (state) {
    case "usa":
      return ["Ford", "Dodge"];
    case "france":
      return ["Renault", "Peugeot"];
    case "italy":
      return ["Fiat"];
    default:
      return [];
  }
};

console.log(getCarsByState()); // outputs []
console.log(getCarsByState("usa")); // outputs ['Ford', 'Dodge']
console.log(getCarsByState("italy")); // outputs ['Fiat']

Vấn đề vẫn là bài toán khi dữ liệu tăng lên và logic code của bạn sẽ không tách biệt khỏi phần khai báo dữ liệu. Hãy sử dụng Indexing hoặc Map để tái cấu trúc đoạn code trên, các bạn sẽ được đoạn code như dưới đây:

Sử dụng Maps:

const cars = new Map()
  .set("usa", ["Ford", "Dodge"])
  .set("france", ["Renault", "Peugeot"])
  .set("italy", ["Fiat"]);

const getCarsByState = (state) => {
  return cars.get(state) || [];
};

console.log(getCarsByState()); // outputs []
console.log(getCarsByState("usa")); //outputs ['Ford', 'Dodge']
console.log(getCarsByState("italy")); // outputs ['Fiat']

Sử dụng indexing:

const carState = {
  usa: ["Ford", "Dodge"],
  france: ["Renault", "Peugeot"],
  italy: ["Fiat"],
};

const getCarsByState = (state) => {
  return carState[state] || [];
};

console.log(getCarsByState()); // outputs []
console.log(getCarsByState("usa")); // outputs ['Ford', 'Dodge']
console.log(getCarsByState("france")); // outputs ['Renault', 'Peugeot']

Dễ thấy sự thay đổi trong 2 đoạn code dưới so với sử dụng khối switch/ case; vừa ngắn gọn hơn và tách biệt được khai báo dữ liệu ra khỏi logic code. Bạn sẽ dễ dàng bổ sung phần dữ liệu vào chương trình mà không làm thay đổi nội dung hàm.

Kết bài

Hy vọng bài viết này mang lại những kinh nghiệm hữu ích dành cho bạn trong việc xử lý các khối lệnh điều kiện trong JavaScript. Việc tận dụng được đặc trưng, các phương thức có sẵn của ngôn ngữ lập trình sẽ giúp bạn tối ưu hóa được source code mà bạn viết ra. Cảm ơn các bạn đã đọc bài và hẹn gặp lại trong các bài viết tiếp theo của mình.

Tác giả: Phạm Minh Khoa

Xem thêm:

Tìm việc làm IT mới nhất trên TopDev