JavaScript, một ngôn ngữ lập trình linh hoạt và lắm “chiêu trò”, đôi khi có thể khiến bạn phải vò đầu bứt tai. Nếu đã từng làm việc với JS, chắc hẳn bạn đã gặp phải một vài hành vi bất ngờ, chẳng hạn như 0 == "0"
hay tự hỏi tại sao 0.1 + 0.2 !== 0.3
.
Trong bài viết này, chúng ta sẽ đi sâu vào một số tính năng khó hiểu nhất của JavaScript, giúp bạn hiểu cách ngôn ngữ hoạt động dưới “lớp vỏ” và tránh những cạm bẫy tiềm ẩn trong mã của mình. Sẵn sàng chưa? Hãy bắt đầu nào!
1. "False" == 0 nhưng "False" == "0" thì không hề nhé
Bạn có thể mong đợi "False" bằng "0", nhưng đây là điểm đáng chú ý: "False" == 0 là true, nhưng "False" == "0" thì lại sai.
Điều này xảy ra vì toán tử đẳng thức (==) của JavaScript kích hoạt ép kiểu. Khi bạn so sánh một chuỗi với một số, JS sẽ cố gắng chuyển đổi chuỗi thành số trước. Và vì "False" không thực sự có giá trị số, nó trở thành NaN. Nhưng "0" trở thành số 0, vì vậy khi chúng ta so sánh 0 == 0, nó là đúng!
Ví dụ:
console.log("False" == 0); // true (coerces "False" to NaN, NaN == 0 is false but coerces NaN to 0)
console.log("False" == "0"); // false
2. 0.1 + 0.2 không chính xác bằng 0.3 đâu nhé
Đây là một ví dụ kinh điển!
console.log(0.1 + 0.2 === 0.3); // false
Tại sao lại có điều này xảy ra? JavaScript sử dụng IEEE-754 cho số học dấu phẩy động, điều này có thể gây ra sự cố về độ chính xác. Vì vậy, 0.1 + 0.2 không chính xác bằng 0.3. Đây là một cạm bẫy phổ biến trong các ứng dụng tài chính chẳng hạn.
Giải pháp: Đối với những phép so sánh như thế này, hãy sử dụng một sai số nhỏ:
const isEqual = Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON;
console.log(isEqual); // true
3. typeof null
trả về "Object"
Điều này thật khó hiểu. Khi bạn kiểm tra kiểu của null, nó trả về "object".
console.log(typeof null); // object
Tại sao lại xảy ra điều này? Vâng, vì đó là một sai lầm lịch sử trong những ngày đầu của JavaScript. Kiểu của null đáng lẽ phải là "null", nhưng do một lỗi, nó lại thành "object". Và bây giờ, đã quá muộn để thay đổi nó mà không phá vỡ mã cũ.
4. [1,2,3] + [4,5,6] = "1,2,34,5,6"
Đây là một điều kỳ lạ nữa! Khi bạn cộng hai mảng lại với nhau, JavaScript sẽ chuyển đổi chúng thành chuỗi trước khi cộng chúng. Kết quả là một sự nối chuỗi kỳ lạ!
console.log([1, 2, 3] + [4, 5, 6]); // "1,2,34,5,6"
Điều này xảy ra vì các mảng được chuyển đổi thành chuỗi, dẫn đến "1,2,3" và "4,5,6", và khi chúng được cộng lại với nhau, nó sẽ trở thành "1,2,34,5,6".
5. NaN === NaN là False
Đúng vậy, NaN (Không phải số) là một giá trị đặc biệt trong JavaScript và nó không bao giờ bằng chính nó.
console.log(NaN === NaN); // false
Tại sao lại xảy ra điều này? Logic đằng sau điều này là NaN không phải là một số thực, và do đó nó không thể bằng bất cứ thứ gì, kể cả chính nó.
Mẹo: Để kiểm tra NaN, hãy sử dụng Number.isNaN():
console.log(Number.isNaN(NaN)); // true
6. Tại Sao [] == ![] là True Nhưng [] == [] là False
Trong JavaScript, [] == ![] là true, nhưng [] == [] là false. Làm thế nào?
- [] == [] là false vì hai mảng trống khác nhau không phải là cùng một tham chiếu đối tượng.
- [] == ![] hoạt động vì ![] ép kiểu thành false, và một mảng trống là một giá trị truthy. Do đó, [] == false trở thành true sau khi ép kiểu.
7. Math.max() trả về -Infinity
Khi bạn gọi Math.max() mà không có bất kỳ đối số nào, nó sẽ trả về -Infinity.
console.log(Math.max()); // -Infinity
Điều này xảy ra vì JavaScript coi việc không có đối số là một phép so sánh với âm vô cùng. Nếu bạn cần một giá trị dự phòng, hãy sử dụng:
const max = Math.max(...numbers) || 0;
8. 0 == "0" và 0 == [] Nhưng "0" != []
Toán tử == của JavaScript thực hiện một số điều kỳ lạ với ép kiểu. Ví dụ: 0 == [] là true, nhưng "0" == [] là false.
console.log(0 == "0"); // true
console.log(0 == []); // true
console.log("0" == []); // false
Điều này xảy ra vì [] trước tiên bị ép kiểu thành một chuỗi rỗng khi so sánh với "0", khiến phép so sánh thứ hai là sai.
9. undefined + 1 Trả Về NaN, nhưng null + 1 Trả Về 1
Đây là một điều kỳ lạ. Khi bạn cộng undefined với một số, bạn sẽ nhận được NaN. Nhưng cộng null với một số sẽ cho kết quả là 1.
console.log(undefined + 1); // NaN
console.log(null + 1); // 1
Tại sao? undefined không phải là một số, vì vậy nó trả về NaN, trong khi null được coi là 0 trong các phép toán số học.
10. New Array(3) so với [,,]
Sự khác biệt giữa new Array(3) và [,,] là gì?
- new Array(3) tạo một mảng trống với 3 vị trí (không có giá trị), trong khi [,,] tạo một mảng với 2 giá trị undefined.
console.log(new Array(3)); // [ <3 empty items> ]
console.log([,,]); // [undefined, undefined]
11. parseFloat("3.14abc") hoạt động, nhưng parseInt("3.14abc") thì không nhé
Khi phân tích cú pháp các chuỗi có chứa số và văn bản, parseFloat sẽ cố gắng đọc số cho đến ký tự không phải số đầu tiên. parseInt, mặt khác, dừng lại ở ký tự không phải số đầu tiên và không hoạt động đối với số thập phân.
console.log(parseFloat("3.14abc")); // 3.14
console.log(parseInt("3.14abc")); // 3
12. [] + so với + []`
Bạn có biết thứ tự các phép toán quan trọng trong JavaScript không? [] + dẫn đến một chuỗi, nhưng + [] không làm gì cả.
console.log([] + {}); // "[object Object]"
console.log({} + []); // "[object Object]"
13. 0.1 * 0.1 in ra "0.010000000000000002"
Số học dấu phẩy động của JavaScript đôi khi có thể cho kết quả bất ngờ, như trong trường hợp 0.1 * 0.1:
console.log(0.1 * 0.1); // 0.010000000000000002
Điều này xảy ra do lỗi chính xác dấu phẩy động nhị phân.
14. Giá trị Truthy và Falsy
Trong JavaScript, một số giá trị được coi là falsy: "", 0, null, undefined, NaN, và false. Bất kỳ thứ gì khác là truthy.
console.log(!!""); // false
console.log(!!0); // false
console.log(!!null); // false
console.log(!!1); // true
Giờ thì bạn đã hiểu sự biến thiên vạn hóa của JavaScript hay chưa nào!