This book is currently a work in progress. Content is being actively developed.
Chapter 1: Primitive Values
In Chapter 1 of the “Objects & Classes” book of this series, we confronted the common misconception that “everything in JS is an object”. We now circle back to that topic, and again dispel that myth. Here, we’ll look at the core value types of JS, specifically the non-object types called primitives.Value Types
JS doesn’t apply types to variables or properties — what I call, “container types” — but rather, values themselves have types — what I call, “value types”. The language provides seven built-in, primitive (non-object) value types:undefined
Represents the absence of a value
null
Represents an intentional empty value
boolean
True or false values
string
Text and character data
number
Numeric values (IEEE-754 64-bit)
bigint
Arbitrarily large integers
symbol
Unique, opaque values
Type-Of
Any value’s value-type can be inspected via thetypeof operator, which always returns a string value representing the underlying JS value-type:
Non-objects?
What specifically makes the 7 primitive value types distinct from the object value types (and sub-types)? Why shouldn’t we just consider them all as essentially objects under the covers? Consider:nickname property to a primitive string. In strict-mode, JS enforces a restriction that disallows setting a new property on a primitive value:
Empty Values
Thenull and undefined types both typically represent an emptiness or absence of value.
Unfortunately, the null value-type has an unexpected typeof result:
undefined type is reported both for explicit undefined values and any place where a seemingly missing value is encountered:
The
typeof nonExistent expression is referring to an undeclared variable. Normally, accessing an undeclared variable would cause an exception, but the typeof operator is afforded the special ability to safely access even non-existent identifiers and calmly return "undefined" instead of throwing an exception.Null’ish
Semantically,null and undefined types both represent general emptiness, or absence of another affirmative, meaningful value.
JS provides a number of capabilities for helping treat the two nullish values as indistinguishable. For example, the == (coercive-equality comparison) operator specifically treats null and undefined as coercively equal to each other:
?? (nullish-coalescing) operator:
??, JS also added the ?. (nullish conditional-chaining) operator:
Distinct’ish
It’s important to keep in mind thatnull and undefined are actually distinct types. There are cases where null and undefined will trigger different behavior by the language.
For example, parameter defaults only trigger for undefined:
= .. clause on a parameter only kicks in and assigns its default value if the argument in that position is missing, or is exactly the undefined value.
Boolean Values
Theboolean type contains two values: false and true.
In the “old days”, programming languages would use 0 to mean false and 1 to mean true. So you can think of the boolean type as semantic convenience sugar on top of the 0 and 1 values:
The
! operator negates/flips a boolean value to the other one: false becomes true, and true becomes false.String Values
Thestring type contains any value which is a collection of one or more characters, delimited by quote characters:
"a" is a string just like "abc" is.
Strings can be delimited by double-quotes ("), single-quotes ('), or back-ticks (`). The ending delimiter must always match the starting delimiter.
Strings have an intrinsic length which corresponds to how many code-units they contain:
JS Character Encodings
What type of character encoding does JS use for string characters? You’ve probably heard of “Unicode” and perhaps even “UTF-8” or “UTF-16”. But it’s not that simple. You need to understand how a variety of aspects of Unicode work, and even consider concepts from UCS-2 (2-byte Universal Character Set).Unicode Code Points
Unicode Code Points
Unicode defines all the “characters” we can represent universally in computer programs, by assigning a specific number to each, called code-points. These numbers range from
0 all the way up to 1114111 (10FFFF in hexadecimal).The standard notation for Unicode characters is U+ followed by 4-6 hexadecimal characters. For example, the ❤ (heart symbol) is code-point 10084 (2764 in hexadecimal), notated as U+2764.Basic Multilingual Plane (BMP)
Basic Multilingual Plane (BMP)
The first group of 65,535 code points in Unicode is called the BMP (Basic Multilingual Plane). These can all be represented with 16 bits (2 bytes). When representing Unicode characters from the BMP, it’s fairly straightforward, as they can fit neatly into single UTF-16 JS characters.
Surrogate Pairs
Surrogate Pairs
All code points above the BMP require more than 16 bits to represent — 21 bits to be exact. JS stores these code-points as a pairing of two adjacent 16-bit code units, called surrogate halves (or surrogate pairs).For example,
🎆 (fireworks symbol, U+1F386) is stored as two surrogate-halve code units: U+D83C and U+DF86. This means a single visible character like 🎆 is counted as 2 characters for the purposes of string length!Escape Sequences
If" or ' are used to delimit a string literal, the contents are parsed for character-escape sequences: \ followed by one or more characters that JS recognizes.
For single-character escape sequences, the following characters are recognized after a \:
\b- backspace\f- form feed\n- new-line\r- carriage return\t- tab\v- vertical tab\0- null character\'- single quote\"- double quote\\- backslash
Multi-Character Escapes
Multi-character escape sequences may be hexadecimal or Unicode sequences.Hexadecimal Escape Sequences
Used to encode any of the base ASCII characters (codes 0-255), look like
\x followed by exactly two hexadecimal characters:Unicode Escape Sequences (BMP)
Can encode any characters from the Unicode BMP, look like
\u followed by exactly four hexadecimal characters:Template Literals
Strings can also be delimited with` back-ticks, which enables special features:
${ .. } in such a template literal is an arbitrary JS expression. It can be simple variables, complex JS programs, or even another template literal expression!
Template literals also support true multi-line strings:
Number Values
Thenumber type contains any numeric value (whole number or decimal), such as -42 or 3.1415926. These values are represented by the JS engine as 64-bit, IEEE-754 double-precision binary floating-point values.
JS numbers are always decimals; whole numbers (aka “integers”) are not stored in a different/special way. An “integer” stored as a number value merely has nothing non-zero as its fraction portion:
Parsing vs Coercion
If a string value holds numeric-looking contents, you may need to convert from that string value to anumber.
It’s very important to distinguish between parsing-conversion and coercive-conversion:
parseInt(string, radix)
Always specify an explicit radix (like
10 for base-10). Omitting it can lead to subtle bugs due to auto-guessing behavior.parseFloat(string)
Always parses with a radix of
10. Fully supports scientific notation like "1.23e+5".Other Numeric Representations
JS supports defining numbers in different bases:Always use lowercase prefixes (
0b, 0o, 0x) rather than uppercase (0B, 0O, 0X) for readability. The uppercase 0O is particularly easy to confuse at a glance._ digit separator for readability:
IEEE-754 Bitwise Binary Representations
IEEE-754 is a technical standard for binary representation of decimal numbers, widely used by most programming languages including JS, Python, and Ruby. In 64-bit IEEE-754, the 64 bits are divided into three sections:- 52 bits for the number’s base value (mantissa/significand)
- 11 bits for the exponent
- 1 bit for the sign
How 42 is Represented
How 42 is Represented
The number The sign bit is
42 would be represented by these bits:0 (positive). The exponent gives us 2^5 = 32. The mantissa represents 1.3125. Multiply them: 32 × 1.3125 = 42.Number Limits
The largest value that can accurately be stored in thenumber type:
Safe Integer Limits
The largest integer you can accurately store in thenumber type is 2^53 - 1:
Double Zeros
JS has two zeros:0, and -0 (negative zero). This is mandated by the IEEE-754 specification:
Invalid Number
Mathematical operations can sometimes produce an invalid result, represented by the specialnumber value called NaN:
The historical root of “NaN” is as an acronym for “Not a Number”. However,
NaN absolutely IS a number type! I prefer to define “NaN” as:- “iNvalid Number”
- “Not actual Number”
- “Not available Number”
- “Not applicable Number”
NaN is special in that it’s the only value in JS that lacks the identity property — it’s never equal to itself:
NaN, use one of these approaches:
BigInteger Values
As the maximum safe integer in JSnumbers is 9007199254740991, this can present a problem if a JS program needs to perform larger integer math, or hold values like 64-bit integer IDs.
For that reason, JS provides the alternate bigint type, which can store arbitrarily large integers:
bigint:
bigint value can be created with the BigInt() function:
Symbol Values
Thesymbol type contains special opaque values called “symbols”. These values can only be created by the Symbol() function:
"my secret" string is merely an optional descriptive label for debugging. The underlying value returned is a special kind of value that’s opaque and unique.
You could think of symbols as if they are monotonically incrementing integer numbers. But the JS engine will never expose any representation of a symbol’s underlying value in any way that you or the program can see.
Symbol Use Cases
Symbols are guaranteed by the JS engine to be unique and unguessable. Common use cases include:Symbol properties are still publicly visible on any object — they’re not actually private. But they’re treated as special and set-apart from the normal collection of object properties, similar to using
__privateProperty naming conventions.Well-Known Symbols (WKS)
JS pre-defines a set of symbols, referred to as well-known symbols (WKS), that represent certain special meta-programming hooks on objects:Symbol.toStringTag is a well-known symbol for accessing and overriding the default string representation of a plain object.
Global Symbol Registry
JS provides a global namespace to register symbols that should be accessible throughout all files in a program:Primitives Are Built-In Types
We’ve now dug deeply into the seven primitive (non-object) value types that JS provides automatically built-in:undefined- absence of valuenull- intentional empty valueboolean- true/falsestring- text datanumber- numeric values (IEEE-754)bigint- arbitrarily large integerssymbol- unique, opaque values
Continue to Chapter 2
Learn about how primitive values behave, including immutability, string operations, number behaviors, and more

