Toán tử 3 ngôi (Ternary Operator): Phép thuật rút gọn code hay "Chén thuốc độc" của dân lập trình?
Chào anh em cộng đồng Viblo! Tiếp tục series dọn dẹp và tối ưu hóa code, hôm nay chúng ta sẽ bàn về một thứ mà bất kỳ anh em nào từ Frontend (JavaScript) đến Backend (PHP, Java, C#) cũng dùng hàng ngày: Toán tử 3 ngôi (Ternary Operator).
Nhớ lại hồi mới chập chững bước chân vào nghề, lúc vừa khám phá ra cú pháp này, mình có cảm giác như vừa nhặt được "bí kíp võ công" vậy. Thay vì phải viết một khối if-else dài đến 5-6 dòng, mình có thể nén tất cả lại chỉ trong đúng một dòng duy nhất. Nhìn đoạn code ngắn gọn, vuông vức, mình từng tự đắc nghĩ rằng: "Code càng ngắn chứng tỏ trình độ càng cao!" Nhưng khi dự án phình to, và đặc biệt là khi phải đi maintain (bảo trì) đống code cũ của người khác (và của chính mình), mình mới nhận ra: Toán tử 3 ngôi là một con dao hai lưỡi cực kỳ sắc bén.
1. Bản chất: Quyến rũ vì sự ngắn gọn
Toán tử 3 ngôi là toán tử duy nhất trong hầu hết các ngôn ngữ lập trình nhận vào 3 toán hạng, với cú pháp chuẩn: Điều_kiện ? Kết_quả_nếu_đúng : Kết_quả_nếu_sai
Nó sinh ra để giải quyết một bài toán rất cụ thể: Gán giá trị cho một biến dựa trên một điều kiện đơn giản.
Thay vì viết thế này:
let userRole;
if (isAdmin) {
userRole = "Quản trị viên";
} else {
userRole = "Khách";
}
Bạn chỉ cần vung tay một nhát:
let userRole = isAdmin ? "Quản trị viên" : "Khách";
Quá đẹp! Code đọc lên y như ngôn ngữ tự nhiên: "Nếu là Admin thì gán là Quản trị viên, còn không thì là Khách". Ở mức độ này, toán tử 3 ngôi thực sự là một phép thuật tuyệt vời của Clean Code.
2. Cạm bẫy "Lồng nhau" (Nested Ternary) - Nỗi ác mộng khi review code
Mọi thứ bắt đầu tồi tệ khi anh em vướng vào tâm lý "cuồng" viết code một dòng (one-liner) và bắt đầu lồng các toán tử 3 ngôi vào nhau.
Giả sử logic nghiệp vụ yêu cầu: Nếu là Admin thì có toàn quyền; nếu không phải Admin nhưng là User VIP thì được quyền Edit; còn lại thì chỉ được View.
Và đây là "tác phẩm" mà mình từng thấy (thậm chí từng viết) trong các pull request:
const permission = isAdmin ? "All" : isVip ? "Edit" : "View";
Khoan đã, có vẻ vẫn đọc được đúng không? Vậy thử nâng cấp lên một chút với hệ thống tính điểm xếp loại học lực:
const grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
Lúc này, não bộ của người đọc code sẽ phải hoạt động y hệt một trình biên dịch (compiler). Mắt phải căng ra để dò xem cái dấu ? này đi cặp với cái dấu : nào. Nếu trong các biểu thức kia còn chứa thêm các hàm tính toán phức tạp nữa thì xin chúc mừng, bạn vừa tạo ra một "hố đen" bảo trì.
Kinh nghiệm rút ra: Đừng bắt đồng nghiệp phải giải đố khi đọc code của bạn. Khối lệnh if-else if hoặc switch-case tuy dài dòng hơn, nhưng nó giúp luồng logic hiện ra rõ ràng từ trên xuống dưới. Chỉ dùng toán tử 3 ngôi cho MỘT tầng điều kiện duy nhất!
3. Cú lừa lịch sử của PHP đời cũ (Left-Associativity)
Nếu anh em làm PHP và đang duy trì các hệ thống cũ (trước bản PHP 8.0), có một cái hố bom liên quan đến Nested Ternary mà cực nhiều người đạp trúng.
Trong các ngôn ngữ như JavaScript, C++ hay Java, toán tử 3 ngôi được tính toán từ Phải sang Trái (Right-associative). Đoạn code tính điểm ở trên trong JS chạy hoàn toàn chính xác.
Nhưng trong PHP 7.x trở về trước, nó lại tính từ Trái sang Phải (Left-associative). Hãy xem đoạn code PHP này:
$result = true ? 'a' : true ? 'b' : 'c';
- Bằng tư duy thông thường (như JS), bạn nghĩ nó sẽ ra 'a'.
- Nhưng PHP 7 sẽ gom nhóm thế này: (true ? 'a' : true) ? 'b' : 'c'.
- Bước 1: (true ? 'a' : true) trả về 'a'.
- Bước 2: 'a' ? 'b' : 'c'. Vì chuỗi 'a' là truthy, nên kết quả cuối cùng nó in ra là... 'b'.
Một cú lừa ngoạn mục khiến logic gãy vụn! Rất may là từ PHP 8.0, đội ngũ phát triển đã bắt buộc lập trình viên phải dùng dấu ngoặc đơn () nếu muốn lồng toán tử 3 ngôi để tránh sự mập mờ này, nếu không sẽ ném ra lỗi cú pháp.
4. Những kẻ thay thế hoàn hảo thời hiện đại
Ngày nay, với sự tiến hóa của các ngôn ngữ lập trình, nhiều trường hợp chúng ta dùng toán tử 3 ngôi đã có thể được thay thế bằng những toán tử gọn gàng và an toàn hơn:
Null Coalescing Operator (??) Dùng khi bạn muốn kiểm tra xem biến có tồn tại (khác null/undefined) hay không, nếu không thì lấy giá trị mặc định.
- Cũ: const name = (user.name !== null && user.name !== undefined) ? user.name : "Anonymous";
- Mới: const name = user.name ?? "Anonymous";
Toán tử Optional Chaining (?.) kết hợp ?? Tuyệt chiêu truy cập object lồng nhau mà không sợ lỗi "Cannot read property of undefined".
- const zipCode = user?.address?.zipCode ?? "00000";
Lời kết
Code được sinh ra là để cho con người đọc, sau đó mới tới máy móc chạy. Sự ngắn gọn của Toán tử 3 ngôi là vô giá, nhưng lạm dụng nó để nhồi nhét logic phức tạp thì lại là một tư duy sai lầm.
Quy tắc sinh tồn của mình rất đơn giản: Nếu biểu thức bắt đầu vượt quá giới hạn chiều ngang của màn hình hoặc khiến bạn mất quá 3 giây để hiểu nó đang làm gì, hãy mạnh dạn đập bỏ và viết lại bằng if-else.
Anh em có từng "dính chưởng" với đoạn code Nested Ternary nào của người cũ để lại chưa? Hãy chia sẻ bên dưới phần bình luận nhé! Happy coding!
All rights reserved