Khám phá sức mạnh của toán tử Optional Chain (?.) trong JavaScript

Khám phá sức mạnh của toán tử Optional Chain (?.) trong JavaScript

Bạn đã bao giờ đau đầu khi gỡ lỗi đoạn code và gặp lỗi "Cannot read property 'xxx' of undefined"? Hoặc thắc mắc tại sao nội dung bên phải không có ?. mà code vẫn có thể an toàn "kiểm tra" res.data.nội dung? Hôm nay, chúng ta sẽ đi sâu vào sức mạnh kỳ diệu của toán tử chuỗi tùy chọn ?. trong JavaScript, kết hợp với sự thật về lỗi code và logic truy cập thuộc tính lồng nhau, chia sẻ những kinh nghiệm thực chiến độc đáo, giúp bạn phát triển frontend từ "mìn" thành "con đường rộng mở"!💪

Mở đầu: Cơn ác mộng của lỗi code

Hãy tưởng tượng: bạn đã vất vả viết xong dự án Vue, khi gọi API thì res.data.nội dung báo lỗi, màn hình trắng, animation tải vô hạn, người dùng mặt đầy bối rối. Đây chính là tình huống điển hình của "lỗi code"!💥 Trong JavaScript, việc truy cập thuộc tính đối tượng không tồn tại (ví dụ null hoặc undefined) sẽ gây ra TypeError, làm gián đoạn việc thực thi chương trình. Và toán tử ?. chính là "cứu cánh" giải quyết điểm đau này.

Nhưng ?. không chỉ ngăn chặn lỗi, nó còn có logic sâu sắc hơn: cách xử lý truy cập thuộc tính lồng nhau một cách tinh tế, ví dụ tại sao trong res?.data?.nội dung thì nội dung bên lại không có ?.? Hãy cùng khám phá!

Màn đầu tiên: Nguyên lý kỳ diệu của ?.

Toán tử chuỗi tùy chọn ?. là tính năng được giới thiệu trong ES2020, cho phép bạn truy cập các thuộc tính đối tượng lồng nhau một cách an toàn, bỏ qua null hoặc undefined. Trong code của bạn:

this.danhSach = res?.data?.nộiDung;

Cơ chế hoạt động

  • Nếu resundefined hoặc null, res?.data sẽ trả về undefined ngay lập tức, không ném ra TypeError.
  • Nếu res.data tồn tại nhưng res.data.nộiDung không tồn tại, res?.data?.nộiDung vẫn trả về undefined.
  • Cuối cùng, this.danhSach có thể trở thành undefined, nhưng code sẽ không bị sập.
Viết truyền thống đầy đau khổ

Không có ?., bạn có thể phải viết:

if (res && res.data && res.data.nộiDung) {
  this.danhSach = res.data.nộiDung;
} else {
  this.danhSach = [];
}

Dài dòng và dễ bỏ sót kiểm tra,稍不小心就 gặp rắc rối.

Cách viết tinh tế với ?.
this.danhSach = res?.data?.nộiDung || [];

Chỉ một dòng code, vừa chống sập vừa gán giá trị mặc định, hiệu suất tăng gấp đôi!🚀

Sơ đồ quy trình Mermaid: Quá trình thực thi ?.

Sơ đồ quy trình này minh họa cách ?. kiểm tra từng tầng, gặp "mìn" thì quyết đoạt trả về undefined, tránh cho chương trình bị treo.

Màn thứ hai: Sự thật về lỗi code và tác động của nó

"Lỗi code" là gì?

"Lỗi code" là chương trình bị gián đoạn do lỗi chưa được xử lý (như TypeError), dẫn đến chức năng bị vô hiệu hóa. Hãy tưởng tượng, xe đang chạy trên đường va phải chướng ngại vật, nếu không có hệ thống giảm xóc, xe sẽ hư hỏng; với ?., giống như có túi khí, xe vẫn có thể chạy tiếp!

Ví dụ về lỗi sập
let obj = null;
console.log(obj.tên); // Uncaught TypeError: Cannot read property 'tên' of null

  • Lỗi được ném ra, code sau đó không thực thi, trang có thể trắng.
Trong code của bạn
this.danhSach = res.data.nộiDung; // res là undefined

  • Ném ra TypeError, this.loading = false không được thực thi, animation tải bị kẹt, trải nghiệm người dùng sụp đổ.

Chi phí khi không sử dụng ?.

  1. Chương trình bị ngắt quãng: Lỗi dừng thực thi hàm hiện tại, ảnh hưởng đến logic sau.
  2. UI bất thường: Trạng thái tải không được xóa, nút vô hiệu.
  3. Người dùng không hài lòng: Ứng dụng không sử dụng được, giảm độ tin cậy.

Kinh nghiệm độc đáo: Nguồn gốc của lỗi thường là "giả định"

Lỗi thường bắt nguồn từ sự "giả định quá mức" của nhà phát triển về dữ liệu: giả định res chắc chắn có data, data chắc chắn có nộiDung. Thực tế là API có thể thất bại, backend có thể thay đổi cấu trúc. ?. dạy chúng ta chấp nhận sự không chắc chắn, chứ không đối đầu với nó.

Màn thứ ba: Đi sâu vào logic lồng của ?.: Tại sao nộiDung bên phải không có ?.?

Bạn có thể thắc mắc: trong res?.data?.nộiDung, tại sao nộiDung bên lại không có ?., nhưng code vẫn có thể "kiểm tra" res.data.nộiDung? Hãy cùng phân tích biểu thức này, hé lộ logic đằng sau.

Phân tích res?.data?.nộiDung

  1. Kiểm tra res:
  • Nếu resnull hoặc undefined, res?.data trả về undefined, không tiếp tục truy cập data hay nộiDung.
  1. Kiểm tra res.data:
  • Nếu res?.datanull hoặc undefined, res?.data?.nộiDung trả về undefined, không truy cập nộiDung.
  1. Truy cập res.data.nộiDung:
  • Nếu res.data tồn tại, res?.data?.nộiDung truy cập trực tiếp res.data.nộiDung.
  • Ở đây nộiDung là thuộc tính cấp cuối, res.data.nộiDung có thể là mảng, đối tượng, hoặc undefined (nếu nộiDung không tồn tại).
Tại sao nộiDung bên lại không có ?.?
  • nộiDung là thuộc tính cấp cuối của biểu thức, ?. có tác dụng đảm bảo đối tượng truy cập thuộc tính (như resres.data) tồn tại, chứ không phải kiểm tra giá trị thuộc tính.
  • Nói cách khác, res?.data?.nộiDung đã dùng ?. để xác nhận res.data tồn tại, lúc này truy cập res.data.nộiDung là an toàn, không ném ra TypeError.
  • Nếu res.data.nộiDung không tồn tại (ví dụ res.data = {}), res.data.nộiDung tự nhiên trả về undefined, không cần thêm ?..
Nếu res.data.nộiDung không tồn tại sẽ xảy ra gì?

Giả sử backend trả về:

{
  "code": 0,
  "msg": "Thành công",
  "data": {}
}

  • res.data là đối tượng rỗng {}, không có trường nộiDung.
  • Thực thi res?.data?.nộiDung:
  • res tồn tại → res?.data nhận được {}
  • res.data tồn tại → res?.data?.nộiDung truy cập res.data.nộiDung
  • res.data.nộiDung không tồn tại → trả về undefined
  • Kết quả: this.danhSach = undefined.
Kinh nghiệm độc đáo: Hiểu lầm về "kiểm tra" thuộc tính cấp cuối
  • "Kiểm tra res.data.nộiDung" không có nghĩa là nộiDung cần một ?., mà là cả biểu thức res?.data?.nộiDung khi thực thi phải xác nhận res.data tồn tại trước khi có thể truy cập res.data.nộiDung một cách an toàn.
  • Nếu cần lồng sâu hơn (ví dụ res.data.nộiDung.sanPham), thì cần tiếp tục dùng `?:``` const sanPham = res?.data?.nộiDung?.sanPham;

###  Màn thứ tư: Tối ưu thực chiến và chia sẻ kinh nghiệm

#### Vấn đề: `this.danhSach` có thể là `undefined`

Nếu `res?.data?.nộiDung` là `undefined`, gán trực tiếp cho `this.danhSach` sẽ khiến `<el-table>` bị lỗi khi render (kỳ vọng mảng).

##### Cách viết cơ bản

this.danhSach = res?.data?.nộiDung; // có thể là undefined


- Rủi ro: `<el-table>` báo lỗi.

##### Cách viết nâng cao: giá trị mặc định

this.danhSach = res?.data?.nộiDung || [];


- Dùng `||` cung cấp mảng rỗng mặc định, an toàn tuyệt đối.

##### Cách viết chuyên gia: hợp nhất null

this.danhSach = res?.data?.nộiDung ?? [];


- `??` chỉ dùng giá trị mặc định khi là `null` hoặc `undefined`, chính xác hơn `||` (vì `||` kích hoạt khi gặp `0`, `false` và các "giá trị giả" khác).

#### Tăng cường với TypeScript

Dùng TypeScript định nghĩa interface, tránh phụ thuộc mù quáng vào `?.`:

interface TraVeAPI { data?: { nộiDung?: Array<{ id: number; tenSanPham: string }>; }; } const res: TraVeAPI = await danhSach(this.danhSachQuery); this.danhSach = res.data?.nộiDung ?? [];


- Kiểm tra kiểu giảm vấn đề runtime, `?.` chỉ là bổ sung.

#### Kinh nghiệm độc đáo: Phòng bệnh hơn chữa bệnh

- **Ghi log**: Thêm `console.log(res?.data)` sau `?.`, phát hiện vấn đề sớm.
- **Ranh lỗi**: Dùng `try/catch` bao quanh lời gọi API, bắt lỗi không mong muốn.
- **Test bao phủ**: Mô phỏng `res` là `null` và `res.data` rỗng, kiểm tra độ bền code.
- **Xử lý thuộc tính cấp cuối**: Thuộc tính cấp cuối (như `nộiDung`) có thể là `undefined`, luôn chuẩn bị giá trị mặc định cho nó.

###  Tổng kết: Triết lý và tương lai của `?.`

- **Triết lý**: `?.` không chỉ là công cụ, mà còn là tư duy - chấp nhận sự không chắc chắn của dữ liệu, ứng xử tinh tế.
- **Logic lồng**: Thuộc tính cấp cuối (như `nộiDung`) không cần `?.`, nhưng đối tượng tiền đề (như `res` và `res.data`) cần `?.` để đảm bảo truy cập an toàn.
- **Giá trị thực chiến**: chống sập code, code ngắn gọn, nâng cao trải nghiệm người dùng.
- **Xu hướng tương lai**: Khi API càng phức tạp, `?.` và TypeScript sẽ trở thành tiêu chuẩn.

Từ "cơn ác mộng lỗi code" đến "cứu cánh phát triển frontend", `?.` giúp chúng ta từ bỏ vũng lầy kiểm tra thủ công, ôm chặt hiệu suất và an toàn của phát triển hiện đại.💡 Nắm vững nó, bạn chính là người bảo vệ code!

###  Món quà: Thách thức và trưởng thành

- Thử xóa bỏ mọi `?.` trong dự án, xem điểm sập code ở đâu!
- Nghiên cứu đề xuất ES2020, khám phá thêm tính năng hiện đại.
- Suy nghĩ: Nếu backend trả về `nộiDung` là chuỗi rỗng `""`, `||` và `??` sẽ khác nhau thế nào?

Có thắc mắc? Để lại bình luận, cùng nhau trưởng thành!🌊

Thẻ: JavaScript ES2020 optional-chaining frontend-development

Đăng vào ngày 4 tháng 6 lúc 21:11