Có điều gì đặc biệt ở toán tử optional chaining (?.) trong JS

Người ta nói rằng để code giỏi hơn, bạn cần code nhiều nhiều hơn nữa để nâng cao tay nghề. Nhưng cá nhân mình nghĩ chừng đó thôi chưa đủ. Cần phải luôn xem xét, cập nhập thêm thông tin về thứ mình đang xài, phải hiểu thứ mình đã xài mới giỏi hơn được chứ phải không nào. Lấy Javascript là một ví dụ, code JS đã lâu, việc cập nhập hiểu biết về các tính năng mới đã giúp mình code ổn hơn khá nhiều, ổn đây là hạn chế số lượng line of code và hạn chế bugs.

Dạo gần đây JS tung ra rất nhiều tính năng mới, phần là để trở nên mạnh mẽ, phần là để bắt kịp xu hướng với các ngôn ngữ khác. Một trong số đó là toán tử optional chaining (?.). Việc thêm nhiều tính năng mỗi năm, khiến mình lười viết bài cập nhập tính năng luôn ấy :v.

Thực ra ngoài JS mình còn kiếm cơm bằng C# nữa, mà C# đã hỗ trợ toán tử này từ rất lâu, và đến bây giờ (thật ra cũng cách đây lâu lâu chút rồi) JS mới hỗ trợ nè. Nên hôm này chúng ta sẽ phân tích xem toán tử này có gì đặc biệt không nhé. Let’s go.

Mục đích tồn tại của optional chaining (?.)

Vậy mục đích của toán tử ?. là gì, dùng trường hợp nào? Nếu bạn đã code JS chắc hẳn cái lỗi Cannot read property '<tên property>' of undefined quen lắm đúng không. Lỗi này xảy ra khi bạn gọi một thuộc tính của một object mà object đó bị undefined hoặc null. Trường hợp phổ biến là object lồng nhau và bạn gọi kiểu “liên tục” như student.info.name.middleName..... Ví dụ:

1
2
3
4
5
6
7
8
9
var student = {
info: {
name: {
y: 10
}
}
};

console.log(student.info.fullname.middleName); // throw lỗi Cannot read property 'middleName' of undefined

fullname không có, nên khi “truy cập” fullname để lấy giá trị thuộc tính middleName thì sẽ gặp lỗi.

Ngày xưa để giải quyết vấn đề này người ta sẽ check if, (thường sẽ kết hợp dùng toán tử logical AND (&&)). Giả dụ:

  • student là object có thể null hoặc undefined
  • Nếu student có giá trị thì đó sẽ là một object và object LUÔN có property info
  • info cũng LUÔN LUÔN có giá trị là một object
  • Trong info CÓ THỂ sẽ có property name, và name có thể có chứa các property khác như firstName, lastName, và middleName.

Để đọc giá trị middleName làm như sau:

1
2
3
4
5
6
7
8
9
10
11
var student = {
info: {
name: {
middleName: 'Van'
}
}
};

if (student && student.info.name && student.info.name.middleName) {
console.log(`Student's middle name is ${student.info.name.middleName}`); // Student's middle name is Van
}

Dài dòng đúng không, lê thê dài dòng. Vậy là từ đó người ta quyết định xây dựng một toán tử để khi “truy cập” một object bị null/undefined thì “trình biên dịch” bỏ qua, không “truy cập” nữa tránh bị lỗi. Đó là mục đích của toán tử optional chaining (?.)

Ví dụ:

1
2
3
4
5
6
7
8
9
10
11
var student = {
info: {
name: {
middleName: 'Van'
}
}
};

// không cần check if nữa, nếu ? student có giá trị không,
// không có thì return undefined, không truy cập nữa, tránh lỗi
console.log(student?.info.fullname?.middleName); // undefined

Đinh nghĩa

Thôi không cần định nghĩa nữa ha, bạn quan tâm thì truy cập MDN để đọc nè.

Giải thích rõ hơn cơ chế

Trên ví dụ trên mình đã có nêu rồi, nhưng nếu bạn cần hiểu rõ hơn, hãy đọc đoạn này nha.

Đầu tiên hãy xem xét dòng code này student?.address?.street?.name. Trình biên dịch sẽ check giá trị của biến đằng trước dấu ? xem có giá trị không, nếu có tiếp tục “truy cập” (truy cập là dùng dấu . đó) tới property tiếp theo. Trình biên dịch ra như sau:

1
2
3
4
5
6
7
8
// mã giả thôi nha
if (student) {
if (student.address) {
if (student.address.street) {
// là gì đó với student.address.street.name, nó chắc chắn không bị lỗi
}
}
}

Nó sẽ đi theo từng bậc như vậy. Nếu biến đằng trước dấu ? không có giá trị thì chương trình sẽ không . (truy cập) đằng sau nữa, chương trình trả về undefined.

Tất cả các bước này gói gọi lại ở hai dấu ?. thần thánh. Tránh để lỗi xảy ra.

Ngoài ra (phần quan trọng)

Ngoài giúp truy cập biến object an toàn tránh bị lỗi. Nó còn hỗ trợ arrayfunction nữa nha. Cơ chế cũng tương tự. Check array có, thì cho truy cập index. Check function có tồn tại thì gọi. Ví dụ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var obj = {
a: {
b: 'b'
},
arr: [1, 2, 3],
func: function () {
return 'function called';
},
funcWithParam: function (param1) {
return param1;
},
};

obj.d?.e;
obj.a?.['b'];
obj.arr?.[1];
obj.func?.();
obj.funcWithParam?.('test');

// console.log kết quả lần lượt là
// undefined
// 'b'
// 2
// 'function called'
// 'test'

Nhưng lưu ý, nó chỉ check giá trị của biến đằng trước ? khác null hoặc undefined thôi nha, chứ không check đó là kiểu gì có phải typeof === 'function' hoặc array hay không đâu nha.

Bạn thử obj.arr?.() sẽ bị lỗi obj.arr is not a function liền à.

Toán tử ?. với delete

Ngoài ra ?. còn sử dụng được với toán tử delete. Cũng giống khi “truy cập”, delete sẽ check property đó có tồn tại không, nếu có thì xóa cho property. Ví dụ:

1
delete student?.address?.street; // xóa thuộc tính street nếu student.address tồn tại

Một lưu ý nữa là toán tử ?. chỉ có thể đọc và xóa không thể set giá trị. Ví dụ:

1
2
3
4
5
6
7
8
9
10
11
var obj = {
number: 1,
count: 3,
name: 'obj'
};

obj?.number; // 1

delete obj?.count; // true

obj?.name = 'test'; // throw ra lỗi invalid assignment left-hand side

Tóm lại

Mình nghĩ đây là cải tiến quan trọng mà JS có được, đây là toán tử cool ngầu mà khi code mình hay dùng nhất. Nó mà kết hợp với toán tử nullish coalescing (??) thì rất xịn. Về toán tử toán tử nullish coalescing (??) mình sẽ giới thiệu ở bài sau nha. Bye bye.

Tham khảo

 Comments
Comment plugin failed to load
Loading comment plugin