Đối tượng `Math`

Đối tượng `Math`

Bài viết này giải thích về đối tượng Math.

Nó giải thích từng bước, từ cách sử dụng cơ bản đến các mẫu thực tiễn phổ biến, cũng như những cạm bẫy và biện pháp phòng tránh.

YouTube Video

Đối tượng Math

Đối tượng Math của JavaScript cung cấp một bộ công cụ tích hợp sẵn cho các phép tính số học.

Hằng số

Các hằng số được định nghĩa trong Math rất hữu ích khi xử lý các giá trị có độ chính xác cao.

Mã dưới đây cho thấy một số hằng số tiêu biểu.

1// Show common Math constants
2console.log("Math.PI:", Math.PI);         // 3.141592653589793
3console.log("Math.E:", Math.E);           // Euler's number
4console.log("Math.LN2:", Math.LN2);       // Natural log of 2
5console.log("Math.SQRT2:", Math.SQRT2);   // Square root of 2
  • Những hằng số này có thể được sử dụng trực tiếp cho các hàm lượng giác, chuyển đổi logarit, chuẩn hóa, và nhiều hơn nữa. Ví dụ, Math.PI cần thiết để chuyển đổi góc sang radian.

Làm tròn cơ bản và giá trị tuyệt đối (abs, floor, ceil, round, trunc)

Làm tròn số và xử lý dấu được sử dụng thường xuyên, vì vậy việc hiểu rõ sự khác biệt giữa Math.floor, Math.ceilMath.round là rất quan trọng. Đặc biệt khi xử lý các số âm, kết quả có thể khác với trực giác, do đó cần chú ý cách hoạt động của từng quy tắc làm tròn và sử dụng chúng một cách phù hợp.

Các ví dụ dưới đây minh họa sự khác biệt giữa các hàm làm tròn.

 1// Examples of rounding and absolute value functions
 2const pos = 3.7;
 3const neg = -3.7;
 4
 5// --- floor ---
 6console.log("Math.floor(3.7):", Math.floor(pos));  // 3
 7console.log("Math.floor(-3.7):", Math.floor(neg)); // -4
 8// (rounds toward smaller value)
 9
10// --- ceil ---
11console.log("Math.ceil(3.7):", Math.ceil(pos));  // 4
12console.log("Math.ceil(-3.7):", Math.ceil(neg)); // -3
13// (rounds toward larger value)
14
15// --- round ---
16console.log("Math.round(3.5):", Math.round(3.5));   // 4
17// (.5 goes up for positive numbers)
18console.log("Math.round(-3.5):", Math.round(-3.5)); // -3
19// (.5 moves toward zero for negative numbers)
20
21// --- trunc ---
22console.log("Math.trunc(3.7):", Math.trunc(pos));  // 3
23console.log("Math.trunc(-3.7):", Math.trunc(neg)); // -3
24// (fraction removed toward zero)
25
26// --- abs ---
27console.log("Math.abs(-3.7):", Math.abs(neg)); // 3.7
  • Math.floor làm tròn về phía nhỏ hơn đối với các số âm, vì vậy -3.7 trở thành -4.
  • Math.ceil làm tròn về phía lớn hơn đối với các số âm, vì vậy -3.7 trở thành -3.
  • Math.round làm tròn 0.5 lên đối với số dương, còn đối với số âm thì làm tròn về phía 0.
  • Các số âm có thể cho ra kết quả khác với trực giác, vì vậy rất quan trọng để hiểu rõ số đang được làm tròn về phía nào.
  • Các hàm làm tròn nên được lựa chọn dựa trên trường hợp sử dụng. Ví dụ, dùng Math.floor để tính chỉ số, Math.ceil để lấy giới hạn trên, và Math.trunc khi chỉ muốn loại bỏ phần thập phân.

Nhân, lũy thừa và căn bậc (pow, **, sqrt, cbrt, hypot)

Phép lũy thừa và căn bậc hai có thể thực hiện bằng Math.pow hoặc toán tử **. Math.hypot tính căn bậc hai của tổng các bình phương (khoảng cách) một cách an toàn.

Dưới đây là các ví dụ về lũy thừa và hypot. Math.hypot giúp giảm thiểu tác động của tràn số và hụt số.

1// Power, root and hypot examples
2console.log("2 ** 10:", 2 ** 10);                // 1024
3console.log("Math.pow(2, 10):", Math.pow(2, 10));// 1024
4console.log("Math.sqrt(16):", Math.sqrt(16));    // 4
5console.log("Math.cbrt(27):", Math.cbrt(27));    // 3
6console.log("Math.hypot(3, 4):", Math.hypot(3, 4));// 5 (3-4-5 triangle)
  • Math.hypot rất hữu ích khi xử lý các cạnh của tam giác hoặc độ dài các vector nhiều chiều.

Các hàm mũ và logarit (exp, log, log10, log2)

Các hàm mũ và logarit thường được sử dụng trong xác suất, phân rã hàm mũ và tỷ lệ.

Tiếp theo là các ví dụ cơ bản về exp và logarit. Hãy chắc chắn sử dụng đúng hàm phù hợp với cơ số logarit.

1// Exponential and logarithm examples
2console.log("Math.exp(1):", Math.exp(1));     // e^1
3console.log("Math.log(Math.E):", Math.log(Math.E)); // natural log, 1
4console.log("Math.log10(100):", Math.log10(100));   // 2
5console.log("Math.log2(8):", Math.log2(8));         // 3
  • Logarit tự nhiên (Math.log) là tiêu chuẩn trong thống kê và phương trình vi phân. Logarit cơ số 10 và logarit cơ số 2 cũng được lựa chọn tùy theo ứng dụng.

Các hàm lượng giác (sin, cos, tan, asin, acos, atan2)

Các hàm lượng giác rất cần thiết cho tính toán góc, xoay và chuyển đổi tọa độ. Math.atan2(y, x) hữu ích để tính toán góc khi xét đúng phần tư của mặt phẳng tọa độ.

Dưới đây là các ví dụ cơ bản và cách sử dụng atan2. Các góc phải được xử lý ở đơn vị radian.

 1// Trigonometry examples (angles in radians)
 2const degToRad = deg => deg * (Math.PI / 180);
 3const radToDeg = rad => rad * (180 / Math.PI);
 4
 5console.log("Math.sin(PI/2):", Math.sin(Math.PI / 2));          // 1
 6console.log("Math.cos(0):", Math.cos(0));                      // 1
 7console.log("Math.tan(Math.PI / 4):", Math.tan(Math.PI / 4));  // ~1
 8
 9// Using atan2 to compute angle of vector (x, y)
10const x = -1, y = 1;
11const angle = Math.atan2(y, x); // returns angle in radians taking quadrant into account
12console.log("atan2(1, -1) in degrees:", radToDeg(angle));      // 135
  • atan2 có thể xác định an toàn góc của một vector, phù hợp cho các phép xoay và tính toán hướng đi.

Sinh số ngẫu nhiên (Math.random và các mẫu thực tiễn)

Math.random() trả về một số ngẫu nhiên phân phối đều trong khoảng [0, 1). Bạn có thể chuyển đổi hoặc mở rộng số ngẫu nhiên này cho các dải số nguyên hoặc phân phối chuẩn khi cần thiết.

Dưới đây là các ví dụ về hàm tiện ích sinh số ngẫu nhiên. Nếu bạn cần số ngẫu nhiên an toàn cho mã hóa, hãy sử dụng crypto.getRandomValues.

 1// Random utilities using Math.random
 2
 3// Get integer in [min, max] inclusive
 4function randomInt(min, max) {
 5  // Returns integer between min and max (inclusive)
 6  return Math.floor(Math.random() * (max - min + 1)) + min;
 7}
 8console.log("randomInt(1, 6):", randomInt(1, 6)); // dice roll example
 9
10// Get float in [min, max)
11function randomFloat(min, max) {
12  return Math.random() * (max - min) + min;
13}
14console.log("randomFloat(0, 5):", randomFloat(0, 5));
15
16// Fisher-Yates shuffle
17function shuffle(array) {
18  for (let i = array.length - 1; i > 0; i--) {
19    const j = Math.floor(Math.random() * (i + 1));
20    // swap array[i] and array[j]
21    [array[i], array[j]] = [array[j], array[i]];
22  }
23  return array;
24}
25console.log("shuffle([1,2,3,4,5]):", shuffle([1,2,3,4,5]));
  • Math.random tiện lợi cho các mục đích chung, nhưng đối với trò chơi có tính lại hoặc mã hóa, cần có biện pháp tăng khả năng tái tạo hoặc bảo mật.

Các hàm tiện ích thực tiễn (clamp, lerp, mapRange, chuẩn hóa góc)

Viết các hàm tiện ích nhỏ cho những tác vụ toán học thường dùng rất hữu ích.

Dưới đây là các ví dụ về cách cài đặt các hàm tiện ích thường dùng.

 1// Utility functions: clamp, lerp, mapRange, normalizeAngle
 2
 3// Clamp value between min and max
 4function clamp(v, min, max) {
 5  return Math.max(min, Math.min(max, v));
 6}
 7console.log("clamp(10, 0, 5):", clamp(10, 0, 5)); // 5
 8
 9// Linear interpolation: t in [0,1]
10function lerp(a, b, t) {
11  return a + (b - a) * t;
12}
13console.log("lerp(0, 10, 0.25):", lerp(0, 10, 0.25)); // 2.5
14
15// Map value from one range to another
16function mapRange(v, inMin, inMax, outMin, outMax) {
17  const t = (v - inMin) / (inMax - inMin);
18  return lerp(outMin, outMax, t);
19}
20console.log("mapRange(5, 0, 10, 0, 100):", mapRange(5, 0, 10, 0, 100));
21// -> 50
22
23// Normalize angle to [-PI, PI)
24function normalizeAngle(rad) {
25  return Math.atan2(Math.sin(rad), Math.cos(rad));
26}
27console.log("normalizeAngle(3*PI):", normalizeAngle(3 * Math.PI));
  • Đoạn mã này tập hợp các hàm tiện ích nhỏ thường được sử dụng cho các phép toán số như giới hạn giá trị, nội suy, chuyển đổi dải giá trị và chuẩn hóa góc. clamp giới hạn giá trị trong một dải nhất định, trong khi lerpmapRange thực hiện nội suy mượt hoặc chuyển đổi sang dải giá trị khác. Ngoài ra, normalizeAngle luôn chuẩn hóa góc về dải [-π, π) để ổn định quá trình tính toán xoay.
  • Các hàm tiện ích này thường được sử dụng trong đồ họa, trò chơi và lập trình tương tác. normalizeAngle rất quan trọng để so sánh sự khác biệt góc và nội suy góc.

Lưu ý về số thực và làm tròn (độ chính xác và so sánh)

Các số trong JavaScript là số thực dạng dấu phẩy động độ chính xác kép theo chuẩn IEEE-754 (64 bit), vì vậy cần cẩn trọng khi so sánh hoặc làm tròn các số này. Ví dụ, 0.1 + 0.2 !== 0.3.

Dưới đây là một ví dụ về xử lý lỗi làm tròn. Việc sử dụng so sánh cho phép sai số nhỏ, hoặc các hàm làm tròn phù hợp là rất quan trọng.

1// Floating point precision example and safe equality
2console.log("0.1 + 0.2 === 0.3 :", 0.1 + 0.2 === 0.3); // false
3
4function nearlyEqual(a, b, eps = 1e-12) {
5  return Math.abs(a - b) <= eps;
6}
7console.log("nearlyEqual(0.1+0.2, 0.3):", nearlyEqual(0.1 + 0.2, 0.3));
8// -> true
  • Để kiểm tra độ bằng nhau của số một cách chính xác, hãy sử dụng sai số tuyệt đối (eps) hoặc sai số tương đối.

Mẹo về hiệu năng

Các hàm của Math được tối ưu hóa ở mức hệ thống và thường nhanh hơn so với code tự viết tương đương. Nếu tính toán lặp lại (ví dụ: Math.PI / 180) trong một vòng lặp, gán nó vào biến trước sẽ giảm chi phí không cần thiết.

Dưới đây là ví dụ chuyển đổi độ sang radian bằng cách khai báo hằng số ngoài vòng lặp.

1// Cache conversion factor for performance
2const DEG_TO_RAD = Math.PI / 180;
3const RAD_TO_DEG = 180 / Math.PI;
4
5for (let deg = 0; deg < 360; deg += 10) {
6  // Use cached constant instead of computing each time
7  const rad = deg * DEG_TO_RAD;
8  // ... do work with rad
9}
  • Nên xác định chính xác các điểm nóng bằng profiler trước khi tối ưu hoá.
  • Việc lưu tạm các hằng số sử dụng nhiều là giải pháp hiệu quả.

Tương thích và polyfill

Math đã tồn tại từ lâu, nhưng một số hàm như Math.cbrt, Math.log10, Math.log2Math.hypot có thể không được hỗ trợ trong các môi trường cũ hơn. Hãy chuẩn bị một polyfill đơn giản nếu cần thiết.

Dưới đây là một ví dụ về polyfill đơn giản cho Math.log2.

1// Polyfill for Math.log2 if needed
2if (!Math.log2) {
3  Math.log2 = function(x) {
4    return Math.log(x) / Math.LN2;
5  };
6}
7console.log("Math.log2(8):", Math.log2(8));

Nhiều môi trường đã hỗ trợ các hàm này, nhưng hãy kiểm tra tính tương thích nếu cần.

Hãy kiểm tra trình duyệt hỗ trợ dựa theo môi trường mục tiêu của dự án.

Ví dụ thực tiễn: Cập nhật theo bước thời gian trong mô phỏng vật lý đơn giản

Cuối cùng, dưới đây là một ví dụ thực tiễn kết hợp nhiều hàm của Math. Đây là ví dụ rất đơn giản để cập nhật vị trí, vận tốc và kiểm soát góc.

Đây là một mô hình cực kỳ đơn giản hóa của động cơ vật lý.

 1// Clamp value between min and max
 2function clamp(v, min, max) {
 3  return Math.max(min, Math.min(max, v));
 4}
 5
 6// Normalize angle to [-PI, PI)
 7function normalizeAngle(rad) {
 8  return Math.atan2(Math.sin(rad), Math.cos(rad));
 9}
10
11// Simple physics step example using Math utilities
12function step(state, dt) {
13  // state: { x, y, vx, vy, angle, angularVelocity }
14  // dt: time delta in seconds
15  // Integrate linear motion
16  state.x += state.vx * dt;
17  state.y += state.vy * dt;
18
19  // Integrate angular motion and normalize angle
20  state.angle = normalizeAngle(state.angle + state.angularVelocity * dt);
21
22  // Keep position within bounds [0, width] x [0, height]
23  state.x = clamp(state.x, 0, state.width);
24  state.y = clamp(state.y, 0, state.height);
25  return state;
26}
27
28const state = { x: 10, y: 10, vx: 2, vy: 0.5, angle: 0, angularVelocity: 0.1, width: 100, height: 100 };
29console.log("before:", state);
30step(state, 0.016); // simulate ~1 frame at 60fps
31console.log("after:", state);
  • Bằng cách kết hợp các hàm tiện ích của Math như vậy, logic mô phỏng và hoạt hình có thể trở nên ngắn gọn hơn.

Tóm tắt

  • Math bao gồm từ các hằng số, hàm cơ bản đến nâng cao, đóng vai trò là nền tảng cho xử lý số liệu.
  • Hãy chọn các hàm dựa theo mục đích như số ngẫu nhiên, hàm lượng giác, mũ/logarit, hoặc làm tròn; và tự tạo các tiện ích nhỏ như clamplerp cho sử dụng thực tiễn.
  • Hãy chú ý đến độ chính xác của số thực và mức độ hỗ trợ của môi trường mục tiêu, đồng thời chuẩn bị cho sai số hoặc polyfill nếu cần.

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