Menu

Toán tử so sánh bằng trong JavaScript: == và === và Object.is

So sánh bằng trong JavaScript hoạt động thế nào: nghiêm ngặt và lỏng lẻo, những điểm kỳ quặc của ==, cách NaN và đối tượng hoạt động, khi nào dùng Object.is.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Hai cách hỏi "hai thứ này có bằng nhau không?"

JavaScript có hai toán tử so sánh bằng: === (nghiêm ngặt) và == (lỏng lẻo). Trông chúng gần như giống hệt nhau. Nhưng hành vi thì rất khác.

=== kiểm tra xem hai toán hạng có cùng kiểu và cùng giá trị không. == chuyển hai toán hạng về một kiểu chung trước, rồi mới so sánh. Việc chuyển đổi đó, gọi là ép kiểu (type coercion), chính là nguồn gốc của phần lớn tiếng xấu về phép so sánh bằng trong JavaScript.

Bản ngắn gọn: mặc định hãy dùng ===. Bản đầy đủ đáng đọc một lần để bạn biết mình đang tránh được những gì.

So sánh nghiêm ngặt: thứ bạn thường cần

=== chỉ trả về true khi hai vế khớp cả kiểu lẫn giá trị. Không ép kiểu, không bất ngờ:

Nếu kiểu khác nhau, kết quả là false ngay lập tức. Nếu cùng kiểu, JavaScript so sánh giá trị. Với kiểu nguyên thủy, đó là so sánh giá trị; với đối tượng, đó là so sánh tham chiếu (sẽ nói rõ hơn ngay sau đây).

!== là toán tử khác nghiêm ngặt và tuân theo cùng quy tắc theo chiều ngược lại.

So sánh lỏng lẻo: ép kiểu trá hình

== cho phép hai vế khác kiểu. Trước khi so sánh, nó chạy một màn ép kiểu để cố làm chúng khớp nhau:

Quy tắc chính xác nằm trong đặc tả, và chúng không quá tệ, nhưng nhớ được chúng giữa lúc đang debug lại là chuyện khác. "0" == false là true khiến cả lập trình viên nhiều kinh nghiệm cũng vấp. [] == false cũng vậy (cũng là true, vì mảng bị ép thành "", rồi "" bị ép thành 0).

Đó là lý do hầu hết các hướng dẫn phong cách code, và quy tắc eqeqeq của ESLint, khuyên bạn mặc định dùng ===. Bạn gõ thêm một ký tự để đổi lấy những quy tắc thật sự nhớ được.

Mẫu == duy nhất hữu ích

Chỉ có một cách viết so sánh lỏng lẻo đáng biết: x == null trả về true khi x là null hoặc undefined, và false với mọi giá trị khác.

Cách viết nghiêm ngặt tương đương là x === null || x === undefined, chạy đúng nhưng đọc rườm rà hơn. Nhiều codebase cho phép == null như ngoại lệ duy nhất được chấp nhận. Hãy chọn một quy tắc và áp dụng nhất quán.

Đối tượng được so sánh theo tham chiếu

Với đối tượng, mảng và hàm, cả === và == đều hỏi cùng một câu: "Hai vế có trỏ tới cùng một đối tượng trong bộ nhớ không?" Không phải "chúng có cùng nội dung không?"

Hai đối tượng literal có nội dung giống hệt nhau vẫn là hai đối tượng khác nhau. Ai cũng từng vấp phải điều này ít nhất một lần.

Để so sánh theo giá trị, bạn tự viết hàm hỗ trợ, dùng thư viện (lodash.isequal), hoặc tuần tự hóa bằng JSON.stringify với các đối tượng đơn giản:

JSON.stringify chỉ dùng được cho dữ liệu đơn giản: nó bỏ qua hàm, undefined, symbol, và không đảm bảo thứ tự khóa giữa các engine với mọi dạng dữ liệu. Đó là cách kiểm tra nhanh, không phải giải pháp tổng quát.

NaN không bằng bất cứ thứ gì

NaN ("not a number") là giá trị JavaScript trả về khi một phép toán số không có kết quả có nghĩa: 0/0, Number("abc"), Math.sqrt(-1). Cả hai toán tử so sánh bằng đều trả về false khi một vế là NaN, kể cả khi cả hai vế đều là NaN:

Để phát hiện NaN, hãy dùng Number.isNaN(value). Đừng dùng hàm toàn cục cũ isNaN: nó ép kiểu đối số trước, nên isNaN("hello") trả về true, gần như không bao giờ là điều bạn muốn.

Object.is: gần như ===, với hai chỗ sửa

Object.is(a, b) hoạt động như ===, trừ hai trường hợp đặc biệt:

Phần lớn thời gian, === là thứ bạn cần. Hãy dùng Object.is khi bạn thật sự cần coi NaN bằng chính nó, hoặc phân biệt +0 với -0: cả hai đều hiếm, nhưng đôi khi quan trọng trong code tính toán số và bên trong các framework (React dùng Object.is để so sánh state).

Toán tử khác cũng chia làm hai

!== là nghiêm ngặt, != là lỏng lẻo, và lời khuyên vẫn như trên:

Mặc định dùng !==. Nếu bạn đã cho phép == null, bạn có thể cho phép luôn mẫu đối ứng != null để kiểm tra "không phải null và không phải undefined".

Danh sách kiểm tra khi so sánh

Khi cần so sánh hai giá trị, hãy đi qua các bước sau:

  • Kiểu nguyên thủy, mong đợi cùng kiểu? Dùng ===.
  • Kiểm tra null hoặc undefined? x == null được nếu hướng dẫn phong cách của bạn cho phép; nếu không thì dùng x === null || x === undefined.
  • Kiểm tra NaN? Number.isNaN(x).
  • So sánh đối tượng theo danh tính? === làm đúng điều bạn muốn.
  • So sánh đối tượng theo nội dung? Tự viết hàm hỗ trợ, dùng thư viện, hoặc tuần tự hóa. Các toán tử có sẵn không giúp được.

Gắn bó với ===, coi == là công cụ đặc biệt cho trường hợp == null, và bạn sẽ tránh được những điểm kỳ quặc về so sánh bằng vốn lấp đầy các danh sách câu hỏi thường gặp về JavaScript.

Tiếp theo: toán tử

So sánh bằng chỉ là một phần trong bộ toán tử của JavaScript. Bài tiếp theo đi qua phần còn lại: toán tử số học, logic, gán, và các toán tử viết tắt bạn sẽ dùng hằng ngày.

Câu hỏi thường gặp

== và === trong JavaScript khác nhau thế nào?

=== là so sánh bằng nghiêm ngặt: chỉ trả về true khi hai toán hạng cùng kiểu và cùng giá trị. == là so sánh bằng lỏng lẻo: nó ép hai toán hạng về cùng một kiểu rồi mới so sánh. 1 === '1' là false, nhưng 1 == '1' là true vì == chuyển chuỗi thành số trước.

Có nên luôn dùng === trong JavaScript không?

Mặc định là có. === dễ đoán và quy tắc của nó dễ nhớ. Ngoại lệ phổ biến duy nhất là x == null, tiện lợi vì khớp cả null lẫn undefined. Hầu hết linter (quy tắc eqeqeq của ESLint) bắt buộc dùng === và cho phép riêng mẫu đó nếu bạn bật nó lên.

Tại sao NaN === NaN lại trả về false?

Chuẩn IEEE 754 quy định NaN không bằng bất cứ thứ gì, kể cả chính nó. Vì vậy mọi toán tử so sánh bằng đều trả về false khi có NaN. Hãy dùng Number.isNaN(x) để kiểm tra một giá trị có phải NaN không, hoặc Object.is(NaN, NaN), hàm này trả về true.

Làm sao so sánh hai đối tượng trong JavaScript?

Cả == và === đều so sánh đối tượng theo tham chiếu, không theo nội dung. {a: 1} === {a: 1} là false vì đó là hai đối tượng khác nhau. Để so sánh theo giá trị, bạn cần tự viết hàm kiểm tra, dùng thư viện như isEqual của Lodash, hoặc tuần tự hóa bằng JSON.stringify với các đối tượng đơn giản.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu