Đối tượng Math của JavaScript giống như một bộ dụng cụ tiện ích thầm lặng của ngôn ngữ này. Bạn không bao giờ khởi tạo nó. Bạn cũng không bao giờ phải import nó. Nó đơn giản là tồn tại trong mọi môi trường thực thi (runtime), sẵn sàng xử lý mọi thứ từ việc làm tròn một giá trị pixel đến việc tạo ra một kết quả tung xúc xắc ngẫu nhiên. Vì Math là một đối tượng tĩnh (static object), mọi phương thức và thuộc tính đều nằm trực tiếp trong namespace. Bạn viết Math.PI, chứ không phải new Math().PI. Lựa chọn thiết kế đó giúp mọi thứ đơn giản hơn, nhưng nó cũng có nghĩa là đối tượng này sở hữu một vài "góc cạnh sắc nhọn" (những điểm cần lưu ý) mà bạn nên hiểu rõ trước khi tin dùng nó cho các logic trong môi trường production.

Một đối tượng tĩnh không có Constructor

Hầu hết các đối tượng tích hợp sẵn trong JavaScript đều yêu cầu bạn tạo một instance. Bạn gọi new Date() hoặc new Array(). Nhưng Math thì từ chối. Nó được thiết kế như một namespace chứa các hàm và hằng số liên quan, hoạt động trên kiểu dữ liệu nguyên thủy Number. Gọi Math.sqrt(16) và bạn nhận được 4. Nếu thử dùng từ khóa new, engine sẽ ném ra một lỗi TypeError. Mô hình này giúp bộ nhớ luôn sạch sẽ và báo hiệu rằng Math thuần túy là functional. Nó nhận đầu vào, trả về đầu ra và không làm thay đổi (mutate) bất cứ thứ gì.

Có một giới hạn nghiêm ngặt cần nhớ: Math làm việc với các số chuẩn IEEE 754. Nó không hoạt động với BigInt. Truyền một BigInt vào Math.round() hoặc Math.abs() và bạn sẽ nhận được lỗi TypeError. Nếu ứng dụng của bạn xử lý các số nguyên 64-bit hoặc lớn hơn, bạn cần chuyển đổi chúng xuống hoặc xử lý các phép tính bằng các toán tử BigInt gốc thay thế.

Những hằng số bạn thực sự sử dụng

Đối tượng Math cung cấp một số hằng số số học giúp bạn tránh việc phải hard-code các "magic numbers" (các con số không rõ nguồn gốc).

  • Math.PI là số 3.14159... quen thuộc, thiết yếu cho bất kỳ thứ gì liên quan đến hình tròn, lượng giác, hoặc chuyển đổi độ sang radian.
  • Math.E là số Euler, xấp xỉ 2.718. Nó xuất hiện trong các đường cong tăng trưởng lũy thừa, tính toán lãi suất kép và các thang đo logarit.
  • Math.SQRT2 là căn bậc hai của hai, khoảng 1.414. Nó rất hữu ích khi bạn cần tính đường chéo của một hình vuông hoặc muốn chuẩn hóa các vector trong hình học đơn giản.
  • Math.LN2 là logarit tự nhiên của 2, xấp xỉ 0.693. Bạn sẽ thấy điều này trong các thuật toán xử lý cây nhị phân, entropy thông tin, hoặc phân tích độ phức tạp thời gian liên quan đến toán học logarit cơ số 2.

Các giá trị này là các số thực dấu phẩy động độ chính xác kép (double-precision floats). Vì chúng là các thuộc tính chứ không phải phương thức, bạn truy cập chúng mà không cần dấu ngoặc đơn.

Làm tròn, Dấu và Giá trị tuyệt đối

Các tác vụ Math phổ biến nhất thường liên quan đến việc làm sạch một con số. JavaScript cung cấp cho bạn bốn phương thức làm tròn, và chúng không hoạt động giống hệt nhau, đặc biệt là khi bạn bước ra khỏi vùng số dương.

Math.abs(x) trả về độ lớn (magnitude) của một số. Truyền vào -42 và bạn nhận được 42. Nó hữu ích khi bạn quan tâm đến khoảng cách thay vì hướng. Hãy nghĩ đến việc so sánh các mốc thời gian (timestamps), tính toán độ chênh lệch (deltas) trong UI, hoặc kiểm tra xem một dự đoán có nằm trong phạm vi sai số hay không.

Math.round(x) là phương thức làm tròn kiểu tiểu học. Nó lấy số nguyên gần nhất, làm tròn các số có phần thập phân là 0.5 lên số nguyên dương tiếp theo. Vì vậy, 4.4 trở thành 4, và 4.5 trở thành 5.

Math.ceil(x) luôn làm tròn lên số nguyên tiếp theo. 4.1 trở thành 5, và -4.9 trở thành -4. Điều cuối cùng thường gây ngạc nhiên cho mọi người. Bởi vì trên trục số, -4 lớn hơn -4.9, nên hàm ceiling sẽ di chuyển về phía số 0 đối với các số âm.

Math.floor(x) thì ngược lại. Nó luôn làm tròn xuống. 4.9 trở thành 4, và -4.1 trở thành -5. Nếu bạn đang phân trang kết quả tìm kiếm hoặc cấp phát các khối bộ nhớ có kích thước cố định, floor thường là thứ bạn cần.

Math.trunc(x) loại bỏ hoàn toàn phần thập phân, chỉ để lại phần nguyên. Về cơ bản, nó di chuyển về phía số 0 bất kể dấu của số đó. 4.9 trở thành 4, và -4.9 trở thành -4. Nếu bạn đang chuyển đổi logic từ các ngôn ngữ thực hiện ép kiểu float sang int bằng cách cắt bỏ phần thập phân, thì đây chính là "người bạn" của bạn.

Cuối cùng, Math.sign(x) cho bạn biết giá trị đó nằm ở phía nào của số 0. Nó trả về 1 cho số dương, -1 cho số âm, 0 cho số 0 dương, -0 cho số 0 âm, và NaN cho bất kỳ thứ gì không phải là số. Đây là một cách ngắn gọn để phân nhánh theo hướng mà không cần viết một chuỗi if-else.

Tạo số ngẫu nhiên

Math.random() tạo ra một số thực dấu phẩy động giả ngẫu nhiên (pseudo-random float) từ 0 (bao gồm) đến 1 (không bao gồm). Nó không an toàn về mặt mật mã (not cryptographically secure), vì vậy đừng sử dụng nó để tạo mật khẩu, tạo token, hoặc bất cứ thứ gì liên quan đến tiền bạc thực tế.