Chuỗi ?.là một kiểu tuỳ chọn, nó là cách chống lỗi truy cập các thuộc tính đối tượng lồng nhau, ngay cả khi thuộc tính trung gian đó không tồn tại.

1. Vấn đề

Nếu bạn mới bắt đầu đọc hướng dẫn và tìm hiểu JavaScript, có thể vấn đề này chưa chạm đến bạn, nhưng nó khá phổ biến.

Ví dụ: một số người dùng của chúng ta có địa chỉ, nhưng một số ít người không cung cấp chúng. Sau đó chúng ta không thể đọc một cách an toàn bằng cách như sau: user.address.street:

let user = {}; // the user happens to be without address

alert(user.address.street); // Error!

Hoặc, trong quá trình phát triển web, chúng ta muốn nhận thông tin về một yếu tố trên trang, nhưng nó có thể không tồn tại:

// Error if the result of querySelector(...) is null
let html = document.querySelector('.my-element').innerHTML;

Trước khi ?.xuất hiện trong ngôn ngữ, toán tử&& đã được sử dụng để làm việc xung quanh nó.

Ví dụ:

let user = {}; // user has no address

alert( user && user.address && user.address.street ); // undefined (no error)

&& toàn bộ đường dẫn đến thuộc tính đảm bảo rằng tất cả các thành phần tồn tại, nhưng viết cách này khá dài.

2. Chuỗi các tùy chọn

Chuỗi tùy chọn ?. sẽ dừng đánh giá và trả về undefinednếu phần trước ?.undefinedhoặc null.

Hơn nữa trong bài viết này, để cho ngắn gọn, chúng ta sẽ nói rằng một cái gì đó có tồn tại, nếu không nullvà không undefined.

Đây là cách an toàn để truy cập user.address.street:

let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)

Đọc địa chỉ với user?.address ngay cả khi userđối tượng không tồn tại:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/


let user = null;

alert( user?.address ); // undefined

alert( user?.address.street ); // undefined
alert( user?.address.street.anything ); // undefined

Xin lưu ý: cú pháp ?. hoạt động chính xác nơi nó được đặt.

Trong hai dòng cuối cùng, việc đánh giá dừng ngay sau đó user?., không bao giờ truy cập vào các thuộc tính khác. Nhưng nếu userthực sự tồn tại, thì các thuộc tính trung gian hơn nữa, chẳng hạn như user.addressphải tồn tại.

Đừng lạm dụng chuỗi tùy chọn(?.)

Chúng ta chỉ nên sử dụng ?.khi có thứ gì đó không tồn tại.

Ví dụ, nếu theo logic của đối tượng user thì nó luôn tồn tại, nhưng addresslà tùy chọn(có hay không gì cũng được), thì user.address?.streetsẽ tốt hơn.

Vì vậy, nếu usermà không xác định được do nhầm lẫn gì đó, chúng tôi sẽ biết về nó và sửa nó. Mặt khác, lỗi code có thể bị không xuất hiện khi dùng ?. không đúng chỗ và nó làm cho việc gỡ lỗi trở nên khó hơn.

Biến trước ?.phải tồn tại

Nếu không có biến user, thì user?.anythingsẽ gây ra lỗi:

// ReferenceError: user is not defined
user?.address;

Chuỗi tùy chọn(?.) chỉ kiểm tra null/undefined, không can thiệp vào bất kỳ cơ chế nào khác của ngôn ngữ.

3. Đoản mạch

Như đã nói trước đây, việc ?.dừng ngay lập tức (ngắt mạch) đánh giá nếu phần bên trái không tồn tại.

Vì vậy, nếu có thêm bất kỳ lệnh gọi hàm hoặc ngoặc vuông nào, chúng sẽ không xảy ra:

let user = null;
let x = 0;

user?.sayHi(x++); // nothing happens

alert(x); // 0, value not incremented

4. Các trường hợp khác:?. (),?. []

Chuỗi tùy chọn ?.không phải là một toán tử, mà là một cấu trúc cú pháp đặc biệt, cũng hoạt động với các hàm và dấu ngoặc vuông.

Ví dụ, ?.()được sử dụng để gọi một hàm có thể không tồn tại.

Trong code bên dưới, một số người dùng của chúng ta có hàm admin và một số thì không:

let user1 = {
  admin() {
    alert("I am admin");
  }
}

let user2 = {};

user1.admin?.(); // I am admin
user2.admin?.();

Ở đây, trong cả hai dòng đầu tiên chúng ta sử dụng dấu chấm .để lấy thuộc tínhadmin, vì đối tượng người dùng phải tồn tại, vì vậy nó được đọc an toàn.

Sau đó ?.()kiểm tra phần bên trái: nếu người dùng tồn tại, thì nó chạy (với user1). Mặt khác (với user2) đánh giá dừng lại mà không có lỗi.

Các ?.[]cú pháp cũng làm việc như vậy, nếu chúng ta muốn sử dụng dấu ngoặc []để tính truy cập thay vì chấm .. Tương tự như các trường hợp trước, nó cho phép đọc một thuộc tính một cách an toàn từ một đối tượng có thể không tồn tại.

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/


let user1 = {
  firstName: "John"
};

let user2 = null; // Imagine, we couldn't authorize the user

let key = "firstName";

alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined

alert( user1?.[key]?.something?.not?.existing); // undefined

Ngoài ra chúng ta có thể sử dụng ?.với delete:

delete user?.name; // delete user.name if user exists

Chúng ta có thể sử dụng ?.để đọc và xóa an toàn, nhưng việc gán thì không dùng

Chuỗi tùy chọn ?.không sử dụng ở phía bên trái của lệnh gán:

// the idea of the code below is to write user.name, if user exists

user?.name = "John"; // Error, doesn't work
// because it evaluates to undefined = "John"

5. Tóm lược

Các cú pháp ?. có ba hình thức:

  1. obj?.prop– trả về obj.propnếu objtồn tại, nếu không undefined.
  2. obj?.[prop]– trả về obj[prop]nếu objtồn tại, nếu không undefined.
  3. obj?.method()– gọi hàm obj.method()nếu objtồn tại, nếu không trả lại undefined.

Như chúng ta có thể thấy, tất cả chúng đều đơn giản và dễ sử dụng. Việc ?.kiểm tra phần bên trái null/undefinedvà cho phép đánh giá có nên truy cập tiếp không nếu không thì dùng.

Một chuỗi ?.cho phép truy cập một cách an toàn các thuộc tính lồng nhau.

Tuy nhiên, chúng ta nên áp dụng một ?.cách cẩn thận, chỉ khi nào phần bên trái không tồn tại. Vì nó sẽ có thể che giấu lỗi lập trình từ chúng ta, nếu chúng xảy ra.

Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!