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.
Nội dung chính
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ề undefined
nếu phần trước ?.
là undefined
hoặ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 null
và 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 user
thự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.address
phả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 address
là tùy chọn(có hay không gì cũng được), thì user.address?.street
sẽ tốt hơn.
Vì vậy, nếu user
mà 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?.anything
sẽ 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:
obj?.prop
– trả vềobj.prop
nếuobj
tồn tại, nếu khôngundefined
.obj?.[prop]
– trả vềobj[prop]
nếuobj
tồn tại, nếu khôngundefined
.obj?.method()
– gọi hàmobj.method()
nếuobj
tồn tại, nếu không trả lạiundefined
.
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/undefined
và 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!