Bài viết này là phần thứ năm trong chuỗi phân tích mã nguồn lodash. Các bài viết tiếp theo sẽ được cập nhật vào kho lưu trữ này, mời bạn sao chép: pocket-lodash.
Tài liệu GitBook cũng sẽ được đồng bộ với các cập nhật từ kho lưu trữ, truy cập tại: pocket-lodash.
Bài viết này sẽ phân tích hàm eq.
Mục đích và Cách sử dụng
Hàm eq được sử dụng để so sánh hai giá trị xem chúng có bằng nhau hay không. Nó tuân thủ quy tắc SameValueZero.
var obj1 = {test: 1};
var obj2 = {test: 1};
var obj3 = obj1;
_.eq(1,1); // true
_.eq(+0, -0); // true
_.eq(obj1, obj3); // true
_.eq(obj1, obj2); // false
_.eq(NaN, NaN); // false
Các quy tắc so sánh
SameValueNonNumber
Quy tắc này áp dụng khi cả hai giá trị x và y không phải là kiểu Number. Cụ thể:
- Kiểu của
xkhông phải làNumber. - Kiểu của
ygiống như kiểu củax. - Nếu kiểu của
xlàUndefined, trả vềtrue. - Nếu kiểu của
xlàNull, trả vềtrue. - Nếu kiểu của
xlàStringvà cả hai chuỗi có độ dài và mã hóa giống nhau, trả vềtrue; ngược lại trả vềfalse. - Nếu kiểu của
xlàBooleanvà cả hai đều làtruehoặc cả hai đều làfalse, trả vềtrue; ngược lại trả vềfalse. - Nếu kiểu của
xlàSymbolvà cả hai có cùng giá trịSymbol, trả vềtrue; ngược lại trả vềfalse. - Nếu cả hai trỏ đến cùng một đối tượng, trả về
true; ngược lại trả vềfalse.
Strict Equality Comparison
Quy tắc này tương ứng với toán tử so sánh nghiêm ngặt (===) trong JavaScript:
- Nếu kiểu của
xvàykhác nhau, trả vềfalse. - Nếu kiểu của
xlàNumber:- Nếu
xlàNaN, trả vềfalse. - Nếu
ylàNaN, trả vềfalse. - Nếu giá trị số của
xvàygiống nhau, trả vềtrue. - Nếu
xlà+0vàylà-0, trả vềtrue. - Nếu
xlà-0vàylà+0, trả vềtrue. - Trả về
false.
- Nếu
- Theo kết quả của quy tắc
SameValueNonNumber.
SameValue
Quy tắc này định nghĩa như sau:
- Nếu kiểu của
xvàykhác nhau, trả vềfalse. - Nếu kiểu của
xlàNumber:- Nếu cả
xvàyđều làNaN, trả vềtrue. - Nếu
xlà+0vàylà-0, trả vềfalse. - Nếu
xlà-0vàylà+0, trả vềfalse. - Nếu giá trị số của
xvàygiống nhau, trả vềtrue. - Trả về
false.
- Nếu cả
- Theo kết quả của quy tắc
SameValueNonNumber.
SameValueZero
Đây là quy tắc mà hàm eq tuân thủ:
- Nếu kiểu của
xvàykhác nhau, trả vềfalse. - Nếu kiểu của
xlàNumber:- Nếu cả
xvàyđều làNaN, trả vềtrue. - Nếu
xlà+0vàylà-0, trả vềtrue. - Nếu
xlà-0vàylà+0, trả vềtrue. - Nếu giá trị số của
xvàygiống nhau, trả vềtrue. - Trả về
false.
- Nếu cả
- Theo kết quả của quy tắc
SameValueNonNumber.
Phân tích mã nguồn
Xem xét mã nguồn của hàm eq:
function eq(giaTri, khacGiaTri) {
return giaTri === khacGiaTri || (giaTri !== giaTri && khacGiaTri !== khacGiaTri);
}
Hàm này chỉ có một dòng duy nhất.
Vì eq tuân thủ quy tắc SameValueZero, ta sẽ phân tích từng phần của nó.
Phần đầu tiên:
giaTri === khacGiaTri
Đoạn mã này tuân thủ quy tắc Strict Equality Comparison. So sánh giữa Strict Equality Comparison và SameValueZero, sự khác biệt nằm ở cách xử lý NaN.
Theo quy tắc Strict Equality Comparison, nếu cả x và y đều là NaN, kết quả sẽ là false. Trong khi đó, theo quy tắc SameValueZero, kết quả sẽ là true.
Phần tiếp theo xử lý trường hợp NaN:
(giaTri !== giaTri && khacGiaTri !== khacGiaTri)
Trong JavaScript, chỉ có NaN mới không bằng chính nó. Khi cả hai giá trị cần so sánh đều không bằng chính chúng, điều đó cho thấy cả hai đều là NaN, do đó hàm sẽ trả về true.
Có thể sử dụng Object.is() không?
Object.is(NaN, NaN) trả về true, nên hàm eq cũng có thể được viết lại như sau:
function eq(giaTri, khacGiaTri) {
return giaTri === khacGiaTri || Object.is(giaTri, khacGiaTri);
}
Tuy nhiên, Object.is tuân thủ quy tắc SameValue, vì vậy Object.is(+0, -0) sẽ trả về false. Do đó, không thể thay thế trực tiếp Object.is cho eq.
Có thể sử dụng isNaN() không?
Có phương thức toàn cục isNaN dùng để kiểm tra xem một giá trị có phải là NaN hay không. Ví dụ, isNaN(NaN) sẽ trả về true. Tuy nhiên, hàm eq không thể được viết lại như sau:
function eq(giaTri, khacGiaTri) {
return giaTri === khacGiaTri || (isNaN(giaTri) && isNaN(khacGiaTri));
}
Lý do là isNaN có hành vi kỳ lạ: nếu tham số đầu vào không phải là kiểu Number, nó sẽ cố gắng chuyển đổi thành kiểu Number trước khi kiểm tra. Điều này dẫn đến kết quả không mong muốn, ví dụ isNaN('notNaN') sẽ trả về true vì chuỗi 'notNaN' bị chuyển đổi thành NaN.
Có thể sử dụng Number.isNaN() không?
Để khắc phục hạn chế của isNaN, ES6 đã mở rộng phương thức isNaN trên đối tượng Number, chỉ trả về true khi giá trị là NaN. Do đó, việc sử dụng Number.isNaN để kiểm tra là an toàn và hàm eq cũng có thể được viết lại như sau:
function eq(giaTri, khacGiaTri) {
return giaTri === khacGiaTri || (Number.isNaN(giaTri) && Number.isNaN(khacGiaTri));
}