Lớp `Number` trong JavaScript

Lớp `Number` trong JavaScript

Bài viết này giải thích về lớp Number trong JavaScript.

Chúng tôi sẽ giải thích cẩn thận các đặc điểm của Number, các lỗi phổ biến, API hữu ích và ví dụ thực tiễn từng bước một.

YouTube Video

Lớp Number trong JavaScript

Number là kiểu số cơ bản trong JavaScript và được biểu diễn theo chuẩn số thực dấu phẩy động hai lần chính xác IEEE-754 (64 bit).

Cơ bản: Biểu diễn số và kiểm tra kiểu dữ liệu

Trong JavaScript, tất cả các số đều được biểu diễn dưới dạng kiểu nguyên thủy number.

 1// Primitive numbers and Number object
 2const a = 42;                 // primitive number
 3const b = 3.14;               // primitive float
 4const c = Number(10);         // primitive via constructor call (function form)
 5const d = new Number(10);     // Number object (wrapper)
 6
 7console.log(typeof a);        // "number"
 8console.log(typeof d);        // "object"
 9console.log(c == d);          // true  (value coerces)
10console.log(c === d);         // false (different types)
  • Đoạn mã này minh họa sự khác biệt giữa kiểu nguyên thủy number và đối tượng bao bọc Number. Thông thường, kiểu nguyên thủy được sử dụng thay cho đối tượng bao.

Giá trị đặc biệt: NaN, Infinity và các hằng số

Number cung cấp các giá trị đặc biệt (NaN, Infinity) và các hằng số.

 1// Special numeric values and constants
 2console.log(Number.NaN);                 // NaN
 3console.log(Number.POSITIVE_INFINITY);   // Infinity
 4console.log(Number.NEGATIVE_INFINITY);   // -Infinity
 5
 6console.log(Number.MAX_VALUE);           // largest positive representable
 7console.log(Number.MIN_VALUE);           // smallest positive representable (closest to 0)
 8console.log(Number.MAX_SAFE_INTEGER);    // 9007199254740991
 9console.log(Number.MIN_SAFE_INTEGER);    // -9007199254740991
10console.log(Number.EPSILON);             // smallest difference ~2.220446049250313e-16
  • Các số nguyên lớn hơn hoặc bằng MAX_SAFE_INTEGER có thể không được biểu diễn chính xác. Hãy cân nhắc sử dụng BigInt nếu cần độ chính xác cao. EPSILON được sử dụng cho so sánh số thực dấu phẩy động.

Chuyển đổi số: Number(), parseInt, parseFloat và toán tử +

Có nhiều cách để chuyển đổi chuỗi sang số, mỗi cách có hành vi khác nhau.

 1// Converting strings to numbers
 2console.log(Number("42"));        // 42
 3console.log(Number("  3.14 "));   // 3.14
 4console.log(+ "7");               // 7 (unary +)
 5
 6console.log(parseInt("42px"));    // 42
 7console.log(parseFloat("3.14abc"));// 3.14
 8
 9// Careful with parseInt and radix
10console.log(parseInt("08"));      // 8
11console.log(parseInt("08", 10));  // 8 (explicit radix is safer)
  • Number() trả về NaN trừ khi toàn bộ chuỗi hoàn toàn là số, còn parseIntparseFloat sẽ lấy phần có thể chuyển đổi thành số từ đầu chuỗi. Nên chỉ định rõ ràng cơ số khi sử dụng parseInt để đảm bảo an toàn.

Xử lý và kiểm tra NaN: isNaNNumber.isNaN

NaN (Not-A-Number) là giá trị đặc biệt vì NaN !== NaN. Có sự khác biệt về cách kiểm tra; hàm toàn cục isNaN thực hiện chuyển đổi kiểu và có thể cho kết quả sai, do đó nên sử dụng Number.isNaN.

1// NaN detection differences
2console.log(NaN === NaN);             // false
3
4console.log(isNaN("foo"));            // true  -> because "foo" coerces to NaN
5console.log(Number.isNaN("foo"));     // false -> "foo" is not the numeric NaN
6
7console.log(Number.isNaN(NaN));       // true
  • Sử dụng Number.isNaN để kiểm tra số và xác thực kết quả tính toán để tránh chuyển đổi kiểu không cần thiết.

Lỗi số thực dấu phẩy động và so sánh sử dụng Number.EPSILON

Do chuẩn IEEE-754, có thể xảy ra lỗi như 0.1 + 0.2 !== 0.3. Khi cần so sánh chặt chẽ, thường sử dụng hiệu số và Number.EPSILON.

1// Floating point precision example and EPSILON comparison
2const sum = 0.1 + 0.2;
3console.log(sum === 0.3);             // false
4
5function nearlyEqual(a, b, epsilon = Number.EPSILON) {
6  return Math.abs(a - b) <= epsilon * Math.max(1, Math.abs(a), Math.abs(b));
7}
8
9console.log(nearlyEqual(sum, 0.3));   // true
  • Khi cho phép lỗi nhỏ trong phép so sánh, nên sử dụng triển khai mạnh như nearlyEqual, vừa dùng phép so sánh theo tỷ lệ vừa dùng độ lệch tuyệt đối.

Kiểm tra số nguyên và số nguyên an toàn (isInteger, isSafeInteger)

Có các API để kiểm tra xem một giá trị có phải là số nguyên và nằm trong phạm vi số nguyên an toàn không.

1// Integer and safe integer checks
2console.log(Number.isInteger(3));                 // true
3console.log(Number.isInteger(3.0));               // true
4console.log(Number.isInteger(3.1));               // false
5
6console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)); // true
7console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false
  • Khi cần độ chính xác với số nguyên lớn, hãy sử dụng Number.isSafeInteger để kiểm tra, và cân nhắc sử dụng BigInt nếu vượt quá phạm vi đó.

Hiển thị và định dạng: toFixed, toPrecision, toString

Có những phương thức để kiểm soát cách hiển thị số. toFixed trả về chuỗi với số chữ số thập phân xác định, nhưng cần chú ý đến việc làm tròn số.

1// Formatting numbers for display
2const x = 1.23456;
3console.log(x.toFixed(2));         // "1.23"   (string)
4console.log(x.toPrecision(3));     // "1.23"   (string with total significant digits)
5console.log((255).toString(16));   // "ff"     (hexadecimal string)
  • toFixed trả về một chuỗi, nên cần chú ý sau khi chuyển đổi sang số để thực hiện phép tính.

Lưu ý khi nối chuỗi và sử dụng toán tử +

Toán tử + dùng để cộng số và nối chuỗi, nên có thể xảy ra kết quả không mong muốn tuỳ thuộc kiểu dữ liệu đầu vào.

1// Pitfall with + operator and type coercion
2console.log(1 + 2 + "px");    // "3px"
3console.log("px" + 1 + 2);    // "px12"
4console.log(1 + "2" - 0);     // 3  (string coerced to number for subtraction)
  • Khi trộn lẫn chuỗi và số, hãy sử dụng rõ ràng String(), Number() hoặc template literals để tránh chuyển đổi kiểu ngầm.

Trường hợp sử dụng nâng cao: Number.parseInt / Number.parseFloat, valueOf

Number.parseIntNumber.parseFloat là các bí danh của những hàm toàn cục. valueOf được dùng để lấy giá trị nguyên thủy từ đối tượng Number, nhưng thực tế rất ít khi cần sử dụng.

1// Number.parseInt/parseFloat aliases and valueOf example
2console.log(Number.parseInt("100", 10));   // 100
3console.log(Number.parseFloat("3.14"));    // 3.14
4
5const nObj = new Number(5);
6console.log(nObj.valueOf());               // 5
7console.log(nObj + 1);                     // 6  (object coerced to primitive)
  • Khi sử dụng Number.parseIntNumber.parseFloat, hãy chú ý đến cơ số và việc loại bỏ khoảng trắng ở đầu/cuối. Thông thường nên tránh sử dụng đối tượng bao.

Tham khảo: Ví dụ tiện ích nhỏ - Các hàm kiểm tra số

Dưới đây là một số hàm tiện ích nhỏ thu thập các kiểm tra thường dùng. Điều này giúp kiểm tra tính hợp lệ của dữ liệu đầu vào dễ dàng hơn.

 1// Small number utility functions
 2function toNumberStrict(value) {
 3  // Return a Number or throw for invalid numeric strings
 4  const n = Number(value);
 5  if (typeof value === "string" && value.trim() === "") {
 6    throw new Error("Empty string is not a valid number");
 7  }
 8  if (Number.isNaN(n)) {
 9    throw new Error(`Invalid number: ${value}`);
10  }
11  return n;
12}
13
14function almostEqual(a, b, eps = Number.EPSILON) {
15  // Robust relative comparison
16  return Math.abs(a - b) <= eps * Math.max(1, Math.abs(a), Math.abs(b));
17}
  • Chúng là những hàm đa năng có thể sử dụng nhiều lần cho việc xử lý đầu vào và so sánh. Các hàm ném lỗi rất hữu ích cho mục đích kiểm tra tính hợp lệ.

Tóm tắt về hiệu suất và các thực tiễn tốt nhất

Dưới đây là những điểm thực tế khi sử dụng Number.

  • Về cơ bản, hãy sử dụng kiểu số nguyên thủy number và tránh dùng new Number().
  • Tùy từng trường hợp mà chọn sử dụng Number() (nghiêm ngặt) hoặc parseInt/parseFloat (lấy một phần) để chuyển đổi chuỗi sang số.
  • Sử dụng Number.isNaNNumber.isFinite để kiểm tra an toàn.
  • Đối với so sánh số thực dấu phẩy động, hãy sử dụng Number.EPSILON hoặc tạo một hàm so sánh chuyên dụng.
  • Khi cần độ chính xác với số nguyên, hãy dùng Number.isSafeInteger, và nếu vượt quá phạm vi cho phép, hãy sử dụng BigInt.
  • Bạn có thể sử dụng toFixed hoặc toPrecision để làm tròn khi hiển thị, nhưng lưu ý rằng giá trị trả về là một chuỗi.

Tóm tắt

Number là kiểu cơ bản cho tất cả các số trong JavaScript, bao gồm số nguyên, số thập phân và các giá trị đặc biệt (NaNInfinity). Cần chú ý đến chuyển đổi kiểu dữ liệu và lỗi số thực dấu phẩy động, đồng thời sử dụng đúng các phương thức như Number.isNaNNumber.EPSILON. Nếu vượt quá phạm vi số nguyên an toàn, hãy sử dụng BigInt và dùng toFixed hoặc toPrecision để làm tròn và hiển thị.

Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.

YouTube Video