Skip to content

Commit 4366576

Browse files
committed
types-grammar, ch1: added discussion of parsing-conversion vs coercive-conversion
1 parent 2d93ee3 commit 4366576

File tree

1 file changed

+90
-5
lines changed

1 file changed

+90
-5
lines changed

types-grammar/ch1.md

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,49 @@ Number.isInteger(42.000000); // true
439439
Number.isInteger(42.0000001); // false
440440
```
441441
442+
#### Parsing vs Coercion
443+
444+
If a string value holds numeric-looking contents, you may need to convert from that string value to a `number`, for mathematical operation purposes.
445+
446+
However, it's very important to distinguish between parsing-conversion and coercive-conversion.
447+
448+
We can parse-convert with JS's built-in `parseInt(..)` or `parseFloat(..)` utilities:
449+
450+
```js
451+
someNumericText = "123.456";
452+
453+
parseInt(someNumericText,10); // 123
454+
parseFloat(someNumericText); // 123.456
455+
456+
parseInt("42",10) === parseFloat("42"); // true
457+
458+
parseInt("512px"); // 512
459+
```
460+
461+
Parsing is a character-by-character (left-to-right) operation, that pulls out numeric-looking characters from the string, and puts them into a `number` value. Parsing stops once it encounters a character that's non-numeric (e.g., not `-`, `.` or `0`-`9`). If parsing fails on the first character, both utilities return the special `NaN` value (see "Invalid Number" below).
462+
463+
When `parseInt(..)` encounters the `.`, it stops, leaving only the `123` in the result `number` value. `parseFloat(..)` by contrast accepts this character, and keeps right on parsing.
464+
465+
The `parseInt(..)` utility specifically, takes as a second argument the `radix`: the numeric base to assume for parsing the string characters into a `number`. `10` is for standard base-10 numbers, `8` is for octal, and `16` is hexadecimal. Any other `radix`, like `23`, assumes `0` - `9` followed by `a` - `z` (case insensitive) character ordination.
466+
467+
If `radix` is omitted, the behavior of `parseInt(..)` is rather nuanced and confusing, in that it attempts to make a best-guess based on what it sees in the first character. This leads to lots of subtle bugs, so never rely on the default auto-guessing; always specify an explicit radix (like `10` in the calls above).
468+
469+
`parseFloat(..)` always parses with a radix of `10`.
470+
471+
In contrast to parsing-conversion, coercive-conversion is an all-or-nothing sort of operation. Either the entire contents of the string are recognized as numeric (integer or floating-point), or the whole conversion fails (resulting in `NaN` -- again, see "Invalid Number" later in this chapter).
472+
473+
Coercive-conversion can be done explicitly with the `Number(..)` function (no `new` keyword) or with the unary `+` operator in front of the value:
474+
475+
```js
476+
someNumericText = "123.456";
477+
478+
Number(someNumericText); // 123.456
479+
+someNumericText; // 123.456
480+
481+
Number("512px"); // NaN
482+
+"512px"; // NaN
483+
```
484+
442485
#### IEEE-754 Bitwise Binary Representations
443486
444487
IEEE-754[^IEEE754] is a technical standard for binary representation of decimal numbers. It's widely used by most computer programming languages, including JS, Python, Ruby, etc.
@@ -671,9 +714,15 @@ Without having a signed zero value, you couldn't tell which direction such an it
671714
672715
#### Invalid Number
673716
674-
Mathematical operations can sometimes produce an invalid result. For example, if you try to divide a number by a string (`42 / "Kyle"`), that's an invalid mathematical operation.
717+
Mathematical operations can sometimes produce an invalid result. For example:
718+
719+
```js
720+
42 / "Kyle"; // NaN
721+
```
722+
723+
It's probably obvious, but if you try to divide a number by a string, that's an invalid mathematical operation.
675724
676-
Another type of invalid numeric operation is trying to convert/coerce a non-numeric type of value to a `number`. We can do so with either the `Number(..)` function (no `new` keyword) or with the unary `+` operator in front of the value:
725+
Another type of invalid numeric operation is trying to coercively-convert a non-numeric resembling value to a `number`. As discussed earlier, we can do so with either the `Number(..)` function or the unary `+` operator:
677726
678727
```js
679728
myAge = Number("just a number");
@@ -683,7 +732,7 @@ myAge; // NaN
683732
+undefined; // NaN
684733
```
685734
686-
All such invalid operations (mathematical or numeric) produce the special `number` value called `NaN`.
735+
All such invalid operations (mathematical or coercive/numeric) produce the special `number` value called `NaN`.
687736
688737
The historical root of "NaN" (including from the IEEE-754[^IEEE754] specification) is as an acronym for "Not a Number". Unfortunately, that meaning produces confusion, since `NaN` is *absolutely* a `number`.
689738
@@ -749,13 +798,45 @@ myBigInt = 9007199254740991n;
749798
myBigInt + 2n; // 9007199254740993n -- phew!
750799
```
751800
801+
As you can see, the `bigint` value-type is able to do precise arithmetic above the integer limit of the `number` value-type.
802+
752803
| WARNING: |
753804
| :--- |
754805
| Notice that the `+` operator required `.. + 2n` instead of just `.. + 2`? You cannot mix `number` and `bigint` value-types in the same expression. This restriction is annoying, but it protects your program from invalid mathematical operations that would give non-obvious unexpected results. |
755806
756-
As shown, the `bigint` value-type is able to do precise arithmetic above the integer limit of the `number` value-type.
807+
A `bigint` value can also be created with the `BigInt(..)` function; for example, to convert a whole (integer) `number` value to a `bigint`:
757808
758-
// TODO
809+
```js
810+
myAge = 42n;
811+
812+
inc = 1;
813+
814+
myAge += BigInt(inc);
815+
816+
myAge; // 43n
817+
```
818+
819+
| WARNING: |
820+
| :--- |
821+
| Though it may seem counter-intuitive to some readers, `BigInt(..)` is *always* called without the `new` keyword. If `new` is used, an exception will be thrown. |
822+
823+
That's definitely one of the most common usages of the `BigInt(..)` function: to convert `number`s to `bigint`s, for mathematical operation purposes.
824+
825+
But it's not that uncommon to have large integer values represented as strings, especially if those values are coming to the JS environment from other locations, or via exchange formats, which themselves do not support `bigint`-style values.
826+
827+
As such, `BigInt(..)` is useful to parse those string values and convert them to `bigint`s:
828+
829+
```js
830+
myBigInt = BigInt("12345678901234567890");
831+
832+
myBigInt; // 12345678901234567890n
833+
```
834+
835+
Unlike `parseInt(..)`, if any character in the string is non-numeric (`0-9` digits or `-`), including `.` or even a trailing `n` suffix character, an exception will be thrown. In other words, `BigInt(..)` is all-or-nothing in its parsing/conversion.
836+
837+
| NOTE: |
838+
| :--- |
839+
| I think it's absurd that `BigInt(..)` won't accept the trailing `n` character while string parsing (and effectively ignore it). I lobbied vehemently for that in the TC39 process, but was ultimately denied. In my opinion, it's now a tiny little wart on JS, but a wart nonetheless. |
759840
760841
### Symbol Values
761842
@@ -765,6 +846,10 @@ The `symbol` type contains special opaque values called "symbols". These values
765846
secret = Symbol("my secret");
766847
```
767848
849+
| WARNING: |
850+
| :--- |
851+
| Just as with `BigInt(..)`, the `Symbol(..)` function must be called without the `new` keyword. |
852+
768853
The `"my secret"` string passed into the `Symbol` is *not* the symbol value itself, even though it seems that way. It's an optional descriptive label, used only for debugging purposes for the benefit of the developer.
769854
770855
The underlying value returned from `Symbol(..)` is a special kind of value that resists the program/developer inspecting anything about its underlying representation. That's what I mean by "opaque".

0 commit comments

Comments
 (0)