Klasa `Number` w JavaScript

Klasa `Number` w JavaScript

Ten artykuł wyjaśnia klasę Number w JavaScript.

Szczegółowo wyjaśnimy cechy charakterystyczne klasy Number, typowe pułapki, przydatne API oraz praktyczne przykłady krok po kroku.

YouTube Video

Klasa Number w JavaScript

Number to podstawowy typ liczbowy w JavaScript i jest reprezentowany jako liczba zmiennoprzecinkowa podwójnej precyzji według standardu IEEE-754 (64-bitowa).

Podstawy: Reprezentacja liczb i sprawdzanie typu

W JavaScript wszystkie liczby są reprezentowane jako prymitywny typ 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)
  • Ten kod demonstruje różnicę pomiędzy prymitywnym number a obiektem opakowującym Number. Zazwyczaj używa się prymitywów zamiast obiektów opakowujących.

Wartości specjalne: NaN, Infinity i stałe

Number udostępnia wartości specjalne (NaN, Infinity) oraz stałe.

 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
  • Liczby całkowite większe lub równe MAX_SAFE_INTEGER mogą nie być reprezentowane dokładnie. Rozważ użycie BigInt, jeśli wymagana jest precyzja. EPSILON jest używany do porównywania liczb zmiennoprzecinkowych.

Konwersja liczb: Number(), parseInt, parseFloat oraz operator +

Istnieje kilka sposobów konwersji ciągu znaków na liczbę, z których każdy zachowuje się inaczej.

 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() zwraca NaN, chyba że cały ciąg jest ściśle w formacie liczbowym, podczas gdy parseInt i parseFloat analizują czytelną część liczbową od początku ciągu. Bezpieczniej jest zawsze jawnie określić podstawę systemu liczbowego (radix) przy użyciu parseInt.

Obsługa i sprawdzanie NaN: isNaN oraz Number.isNaN

NaN (Not-A-Number) jest wyjątkowy, ponieważ NaN !== NaN. Istnieją różnice w sposobie sprawdzania; globalna funkcja isNaN wykonuje konwersję typu i może dawać fałszywe pozytywy, dlatego zalecane jest użycie 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
  • Używaj Number.isNaN do sprawdzania liczb i walidacji wyników obliczeń, aby uniknąć niepotrzebnej konwersji typów.

Błędy zmiennoprzecinkowe i porównania z wykorzystaniem Number.EPSILON

Ze względu na standard IEEE-754 występują błędy takie jak 0.1 + 0.2 !== 0.3. Do ścisłych porównań często wykorzystuje się różnicę oraz 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
  • Przy dozwolonych błędach w porównaniu zaleca się użycie solidnej implementacji, takiej jak nearlyEqual, która uwzględnia skalowanie oraz różnice bezwzględne.

Sprawdzenie liczb całkowitych i bezpiecznych liczb całkowitych (isInteger, isSafeInteger)

Istnieją API pozwalające sprawdzić, czy wartość jest liczbą całkowitą oraz czy mieści się ona w bezpiecznym zakresie liczb całkowitych.

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
  • Gdy wymagana jest dokładność dużych liczb całkowitych, użyj Number.isSafeInteger do sprawdzania, a dla liczb przekraczających ten zakres rozważ zastosowanie BigInt.

Wyświetlanie i formatowanie: toFixed, toPrecision, toString

Istnieją metody pozwalające kontrolować sposób wyświetlania liczb. toFixed zwraca napis o określonej liczbie miejsc po przecinku, ale należy uważać na zasady zaokrąglania.

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 zwraca napis, więc trzeba uważać na wartości po ich zamianie na liczby w obliczeniach.

Ostrzeżenia dotyczące konkatenacji napisów i działania operatora +

Operator + jest używany zarówno do dodawania liczb, jak i do łączenia napisów, więc w zależności od typów danych mogą wystąpić nieoczekiwane rezultaty.

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)
  • W przypadku mieszania napisów i liczb bezpieczniej jest jawnie używać String(), Number() lub szablonów napisowych (template literals), aby uniknąć niejawnej konwersji typów.

Zaawansowane przypadki użycia: Number.parseInt / Number.parseFloat, valueOf

Number.parseInt i Number.parseFloat są aliasami funkcji globalnych. valueOf służy do pobierania prymitywnej wartości z obiektu Number, ale w praktyce rzadko się go używa.

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)
  • Przy używaniu Number.parseInt i Number.parseFloat należy uważać na podstawę systemu liczbowego oraz przycinanie (trimming) wejścia. Obiekty opakowujące są zazwyczaj unikane.

Referencja: Mały przykład pomocniczy – funkcje walidacji liczb

Oto małe funkcje pomocnicze, które zbierają najczęściej używane sprawdzenia. Ułatwia to takie zadania jak walidacja danych wejściowych.

 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}
  • Są to bardzo uniwersalne funkcje, które można wielokrotnie wykorzystywać do przetwarzania danych wejściowych i porównań. Implementacje, które rzucają błędy, są przydatne w celach walidacji.

Podsumowanie wydajności i najlepszych praktyk

Oto praktyczne wskazówki dotyczące używania Number.

  • Zasadniczo używaj prymitywnego typu number i unikaj new Number().
  • Wybierz między Number() (ścisłe) lub parseInt/parseFloat (częściowe wydobycie), zależnie od przypadku użycia przy konwersji napisu na liczbę.
  • Używaj Number.isNaN i Number.isFinite dla bezpiecznego sprawdzania.
  • Do porównań liczb zmiennoprzecinkowych używaj Number.EPSILON lub dedykowanej funkcji porównującej.
  • Gdy wymagana jest dokładność liczb całkowitych, użyj Number.isSafeInteger, a gdy przekraczają one zakres, użyj BigInt.
  • Możesz użyć toFixed lub toPrecision do zaokrąglania na potrzeby wyświetlania, ale pamiętaj, że wartość zwracana to łańcuch znaków.

Podsumowanie

Number to podstawowy typ wszystkich liczb w JavaScript, włączając liczby całkowite, dziesiętne i wartości specjalne (NaN i Infinity). Ważne jest, aby mieć świadomość konwersji typów i błędów zmiennoprzecinkowych, oraz poprawnie korzystać z metod takich jak Number.isNaN i Number.EPSILON. Jeśli przekroczony zostanie zakres bezpiecznych liczb całkowitych, użyj BigInt, natomiast do zaokrągleń i wyświetlania korzystaj z toFixed lub toPrecision.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video