Lesson 06 / 21
Primitive Types
The seven primitive types and the distinction from objects; the single number type's floating-point representation, special number values, string, boolean, the two absence values, and symbol.
Contents
The previous topic established how names are declared and resolved. This topic takes up the values names are bound to. The Basic Data Types lesson in the Programming Fundamentals course defined a type as “a set of values and the operations defined on those values.” This lesson applies that definition to JavaScript’s value model.
The model is small: seven primitive types and one object class. The price of that smallness is the weight each type carries in its decisions. For example, there is a single number type and no separate type for integers; this one decision sets the topic for several lessons ahead.
Seven Primitive Types and Objects
A value is either primitive or an object. Primitive values cannot have their content changed and do not carry properties on their own. Objects are sets of properties, and their content can be changed.
The typeof operator gives a value’s type as text:
console.log(typeof 21.5); console.log(typeof "A1"); console.log(typeof true); console.log(typeof undefined); console.log(typeof null); console.log(typeof 9007199254740993n); console.log(typeof Symbol("measurement")); console.log(typeof { station: "A1" }); console.log(typeof [1, 2]); console.log(typeof function () {});
number string boolean undefined object bigint symbol object object function
The output does not correspond to the type list one to one at two points. In the fifth
line, null is a primitive value, but the result is object; this is the trace of the
backward compatibility discussed in the Language Versions and Standardization lesson. In
the last line, function is not a separate type; functions are callable objects, and
typeof reports this with a distinct value.
The seven primitive types are: number, BigInt, string, boolean, undefined, null, and
symbol. This lesson’s subject is six of these; BigInt is the next lesson’s subject.
Number: A Single Type
JavaScript has no separate types for integers and real numbers. The whole of the number
type is the IEEE 754 double-precision representation defined in the Floating-Point
Numbers lesson of the How Computers Work course: 64 bits, 1 sign bit, 11-bit biased
exponent, 52-bit stored mantissa.
This produces two direct consequences.
First, writing an integer literal does not produce a separate type:
console.log(Number.isInteger(23)); console.log(23 === 23.0);
true true
23 and 23.0 are the same value; both correspond to the same bit pattern. This is why
the first lesson’s Math.max result was written as 23 — a floating-point number whose
fractional part is zero is written without showing the fraction when converted to text.
Second, decimal values that cannot be written as a finite binary fraction are held approximately:
console.log(0.1 + 0.2); console.log(0.1 + 0.2 === 0.3);
0.30000000000000004 false
This result is not a surprise, it is a direct consequence of the representation. The model from the Floating-Point Numbers lesson applies here exactly: the values and cannot be written as a finite binary fraction, each is rounded to the nearest representable value, and the addition is performed on these rounded values. The result falls not on ’s representation, but on another value closest to it.
The practical rule established in the same lesson applies here too: floating-point values are compared not with equality but with tolerance. Quantities requiring precision, like money, are kept as integers carrying the smallest unit.
The safe range of integers also comes from the mantissa’s width. Because the mantissa provides 53 bits of precision, integers whose absolute value exceeds become indistinguishable from one another:
console.log(Number.MAX_SAFE_INTEGER); console.log(9007199254740992 === 9007199254740993);
9007199254740991 true
Two different integers coming out equal is because both round to the same representable value. Cases where this boundary is crossed are the next lesson’s subject.
Special Number Values
The representation carries three special values, and all three are directly visible in the language:
console.log(1 / 0, -1 / 0, 0 / 0); console.log(Object.is(0, -0), 1 / -0);
Infinity -Infinity NaN false -Infinity
Division by zero does not throw an error; it produces an infinity value. Undefined
operations give NaN (not a number). NaN is a number value and its type is
number; its name says that what it represents is not a number.
Signed zero is also part of the representation: 0 and -0 are different bit patterns
but are considered equal in equality comparisons. Object.is is the operation that sees
the distinction. The -0 value is invisible in most computations; it only becomes
significant because it determines the sign in division by zero.
String
The string type carries text. Strings are written with single quotes, double quotes, or backticks; there is no behavioral difference between the first two. The backtick form has separate capabilities and will be covered in the Strings lesson.
const name = "measurement"; console.log(name.length); console.log(name[0]); console.log(name.toUpperCase()); console.log("A1".codePointAt(0));
11 m MEASUREMENT 65
String length is not the character count, it is the count of 16-bit code units. The
surrogate pair concept defined in the Character Encodings lesson of the How Computers
Work course becomes visible here: a code point outside the basic multilingual plane is
represented by two code units, and length counts it as two.
console.log("😀".length);
2
This is the key to understanding the string type: a string is not a sequence of code points, it is a sequence of code units. The distinction changes the result in character counting and slicing operations.
Boolean
The boolean type has two values: true and false. They are produced by comparisons and
are the input of conditional constructs. JavaScript also considers non-boolean values
usable in a condition; which value is considered true and which is considered false is
the subject of the Truthy and Falsy Values lesson.
Two Absence Values
Most languages have a single absence value. JavaScript has two, and their meanings are separate.
undefined is a value never having been given at all. A declared but unassigned
variable, a nonexistent object property, and a parameter with no corresponding argument
carry this value. The language produces it on its own.
null is a deliberately placed absence. The language does not produce it on its own;
the program’s author assigns it to say “there is no value here.”
const record = { station: "A1", temperature: 21.5 }; console.log(record.humidity); console.log("humidity" in record); let nextReading; console.log(nextReading); const calibration = null; console.log(calibration);
undefined false undefined null
The distinction is directly useful in measurement data. If a record’s humidity field
does not exist at all, reading it gives undefined; if the field exists but the
sensor produced no value, writing null preserves the information “not measured.” The
two are not the same thing: the first concerns the data’s shape, the second concerns the
data’s content.
Symbol
Symbol is the primitive type that produces a unique value on every call. Two symbols produced with the same description text are not equal to each other:
console.log(Symbol("source") === Symbol("source"));
false
Its use case is adding properties to objects with no risk of collision. Symbol-keyed properties are invisible in ordinary key listings and in serialization:
const source = Symbol("source"); const record = { station: "A1", temperature: 21.5, [source]: "local file" }; console.log(Object.keys(record)); console.log(JSON.stringify(record)); console.log(record[source]);
[ 'station', 'temperature' ]
{"station":"A1","temperature":21.5}
local file
The provenance information added to the measurement record was carried without polluting the record’s data content. Symbols’ second role in the language — adapting objects’ specific behaviors — is the subject of the next course.
Primitive Values Are Immutable
A primitive value’s content cannot be changed. Attempting an indexed assignment on a string throws an error in strict mode:
const name = "A1"; try { name[0] = "B"; } catch (error) { console.log(error.name + ": " + error.message); } console.log(name);
TypeError: Cannot assign to read only property '0' of string 'A1' A1
The same operation throws no error in a script not in strict mode; it is silently ignored, and the string still does not change. This is the second example of the strict-mode difference discussed in the Ways to Run Code lesson: the behavior is the same, the feedback is different.
This is why string methods never modify the original value, they always produce a new string. The immutable–mutable distinction from the Programming Fundamentals course is strict here by type: all seven primitive types are immutable, objects are mutable.
The Measurement Record’s Types
The data that is this course’s axis is a combination of these types:
const record = { station: "A1", temperature: 21.5, humidity: null, verified: false, }; for (const key of Object.keys(record)) { console.log(key, "->", typeof record[key]); } console.log("missing field:", typeof record.pressure);
station -> string temperature -> number humidity -> object verified -> boolean missing field: undefined
The third line is a reminder: because the humidity field carries null, the typeof
result comes out as object. Whether a field is truly empty is tested not with typeof
but with a direct comparison. Which rule these comparisons follow is the subject of the
Equality Comparisons lesson.
Summary
- Values are either primitive or objects; there are seven primitive types and all of them are immutable.
typeof nullgivesobject, and functions givefunction; neither corresponds to the type list one to one.- The single number type is the IEEE 754 double-precision representation:
23and23.0are the same value, decimal fractions are approximate, and the safe integer range is bounded by . Infinity,-Infinity,NaN, and-0are part of the representation;NaN’s type isnumber.- String length is the code unit count, not the character count.
undefineddenotes a value never given,nulla deliberately placed absence.
Next Step
Two different integers above the safe integer range coming out equal is the one point this lesson leaves unresolved. The next lesson takes up the cases where this boundary is actually crossed and the language’s answer to it: a separate primitive type carrying an integer of arbitrary size, its behavior in operators, and the error that arises when it is mixed with the number type.
To keep your progress and take notes, Log in
My notes
Log in to take notes.