Chắc hẳn khi học hay làm việc với JS, các bạn cũng nghe giang hồ bảo là khi so sánh thì cứ ba dấu bằng ===
mà phang phải không nào. Thậm chí không được dùng ==
luôn cơ. Vậy các bạn có thắc mắc tại sao họ lại bảo như vậy không? Liệu điều đó có thực sự đúng trong tất cả trường hợp? Okie, bài viết hôm nay mình sẽ giải thích vì sao như vậy, và nên dùng ==
, ===
khi nào cho hợp lý nhé. Nếu bạn biết rồi thì cũng có thể đọc, có chi đóng góp, góp ý giúp mình nha. Let’s go.
Ví dụ mở màn
Các bạn xem qua ví dụ sau đây, thử đoán xem kết quả sẽ như thế nào nhé.
1 | // 1 |
Mở console trên Developer Tool ra test thử đi nào, kết quả của phép so sánh "0" == []
sẽ trả về false
đúng không nào. Kết quả không mấy bất ngờ, cơ mà lúc code không để ý thì sẽ gây ra bất ngờ đấy.

Vậy tại sao lại có kết quả như vậy, cơ chế so sánh đằng sau là gì hay nói cách khác, điều gì đã xảy ra?
Why?

Javascript khá kỳ quặc khó đoán cơ mà chính điều này tạo nên sự khác biệt, làm mình thấy thích ngôn ngữ này. Có nhiều người bảo rằng JS dễ học nhưng khó giỏi, mình cũng nghĩ vậy. Cho nên chỉ khi thực sự hiểu nó thì bạn mới thực sự thấy vẻ đẹp “bí ẩn” và dần dà ngôn ngữ này trở nên “dễ đoán” hơn.
Quay trở lại vấn đề, tại sao lại có kết quả không mong đợi như vậy. Trước hết chúng ta cùng tìm hiểu về “tự động ép kiểu”.
1. Tự ép kiểu

Nếu bạn chạy 0 == "0"
trong console, nó sẽ trả về true
.
Đầu tiên 0
là một số và "0"
là một chuỗi, chúng không bao giờ giống nhau! Nên hầu hết các ngôn ngữ lập trình đều “biết” và “tôn trọng” điều đó. Nếu so sánh 0 == "0"
trong C# nó sẽ trả về.Compilation error: Operator '==' cannot be applied to operands of type 'string' and 'int'
Tất nhiên ràng buộc rõ ràng như vậy thì khỏe đúng không? Nếu bạn muốn so sánh một int
và string
trong C#, trước tiên bạn phải chuyển đổi chúng thành cùng loại.
Nhưng đây là JavaScript và với phương châm “sống” rõ ràng quá đôi lúc không tốt, nên chúng sẽ tự động ép một trong các giá trị về cùng loại để mà so sánh. Mấu chốt ở đây là bạn không cần “tự mình” chuyển đổi kiểu, nó sẽ tự động lo cho bạn phần này.
Tự động
là từ khóa ở đây. Thay vì bạn chuyển đổi rõ ràng các loại của mình, JavaScript thực hiện điều đó cho bạn khi nó chạy.

Mình nghĩ điều này đôi lúc khá thuận tiện nếu cố tình khai thác (đôi lúc mình cố tình dùng ==
). Cơ mà điều này hơi nguy hiểm nếu bạn không hiểu rõ cỡ chế đằng sau nó.
Trong tài liệu đặc tả ngôn ngữ ECMAScript có đoạn viết về điều này:
If x is Number and y is String, return x == ToNumber(y)
Nếu x là số và y là chuỗi, return x == ToNumber(y)
Vì vậy, đối với trường hợp 0 == “0”:
Vì 0 là số và "0" là chuỗi, return 0 == ToNumber ("0")
Chuỗi "0"
được tự dộng chuyển đổi thành 0
và số 0
thì bằng số 0
đúng không nào.
1 | 0 == "0" // true |

Đó là tự ép kiểu trong JS đấy.
2. Array cũng sẽ bị ép kiểu tuốt

Thực ra không riêng chi array đâu boolean cũng bị đấy. Ai ai cũng bị ép cả hic.
1 | 0 == [] // true |
Trong tài liệu đặc tả kia có đoạn:
If x is String or Number and y is Object, return x == ToPrimitive(y)
Nếu x là chuỗi hoặc số và y là object thì lấy x so sánh với y sau khi y đã bị “ép kiểu”. Có ba vấn đề cần làm rõ ở đây.
Array là một object

Và mảng rỗng (empty array) sẽ trở thành chuỗi rỗng
Theo như tài liệu đặc tả thì nếu đem object
ra so sánh, JS sẽ tìm kiếm phương thức toString
trong object đó và chuyển nó về string.
Với array
nó toString
hoạt động giống như vầy.
1 | [1, 2, 3].toString() // "1,2,3" |
Như bạn thấy nó sẽ tự động nối tất cả phần tử trong mảng lại và chuyển thành string
. Vậy mảng rỗng sẽ như thế nào nhỉ.
1 | [].toString() // "" |

Muốn đọc kỹ hơn các bạn có thể vào đây và vào đây.
Chuỗi rỗng sau đó trở thành 0

Sau khi array đã thành chuỗi, JS tiếp tục convert chuỗi này sang kiểu số (array rỗng thành string rỗng). Sau đó thì ""
sẽ được convert thành????
Quay lại mục 1 If x is Number and y is String, return x == ToNumber(y)
. ""
sẽ thành số 0
. Quy trình sẽ như vầy []
-> ""
-> 0
. Cuối cùng nó sẽ là phép so sánh giữa 0 == 0
.
Do đó [] == 0 -> true
3. Tóm tắt lại
1 | 0 == "0" // true |
Bởi vì 0 == ToNumber("0")
.
1 | 0 == [] // true |
Sẽ có 2 bước xảy ra.
ToPrimitive([])
chuyển thành chuỗi rỗng- Sau đó ToNumber(“”) chuyển thành 0.
Nếu A = B và B = C vậy theo tính bắc cầu thì A sẽ bằng C (A = C) phải không nào. Từ đó suy ra "0" == []
-> true đúng không nhỉ.
4. Thực ra "0" == []
bằng false
Cũng sẽ dễ hiểu thôi áp dụng nguyên tắc ở trên If x is String or Number and y is Object, return x == ToPrimitive(y)
x
ở đây là"0"
(string)y
là[]
(array)
y
sẽ bị ép kiểu trở thành ""
lúc này vì y
và x
đã cũng kiểu với nhau (cả hai lúc này đều là string) nên "0" == ""
sẽ cho ra false
Kết luận

Cứ như giang hồ nói, cứ so sánh mang ===
ra mà xài, không lo sợ mất ngủ.
1 | 0 === "0" // false |
Cơ mà như mình đã nói, nếu bạn thực sự hiểu rõ cơ chế của nó thì có thể xài ==
để khỏi cần chuyển đổi kiểu thủ công mất thời gian. Ví dụ bạn biết x
lấy từ form về kiểu nào x cũng là số nhưng là kiểu string (“0”, “1”, …) thì bạn có thể cố tình lấy nó so sánh ==
với một số khác "1" == 1 //true
.
Bonus: +"1" === 1 //true
ép kiểu thủ công đó.
Cảm ơn đã đọc tới đây. Have a good day. :D