An immutable collection keeps its existing state intact when a computation produces a result. Instead of changing an array or object in place, create a new result. This matters when another part of an application still holds the original reference: an unexpected change there can make later behavior hard to predict, especially around asynchronous work. section 4.1
In JavaScript, the choice depends on what kind of state you are handling:
- For an array pipeline, use filter and map to derive results while leaving the original array intact; reduce can combine those results into a value. Do not substitute reverse or sort when the original order must survive: those methods mutate the array in place. section 4.1
- For an object that represents a value, prevent its fields from being changed after creation. An operation such as adding two Money values returns a new Money value rather than changing either operand. section 4.5
- For a changed object shape, spread syntax can copy an object's owned properties and override a field in a new object: `{ ...frog, name: 'Toad' }`. This is a shallow clone, not a general guarantee that everything reachable through the copy is immutable. section 3.4.3
A value that stays put
`const USD = Money('$'); USD(3).plus(USD(7)).equals(USD(10)); // true` treats the sum as another value. Assigning `USD(3).amount = 5` instead attempts to change what that value means; the frozen Money object rejects the assignment. Amount and currency together describe its identity. section 4.5
Immutability is also a rule for how functions advance application state: take the data they need as inputs, leave observable existing state unchanged, and return a new state. Combined with predictable results for the same inputs, this supports pure functions. It is a design choice, not a requirement that every domain object be frozen: mutable Block and Transaction objects can accommodate hash recalculation and extension, while Money is suited to value-object behavior. section 4.1.2 section 4.5
Listing 4.13 Details of the Money value object
const BTC = 'B|';
const Money = curry((currency, amount) =>
compose( #1
Object.seal,
Object.freeze
)(Object.assign(Object.create(null), #2
{
amount,
currency,
equals: other => currency === other.currency &&
amount === other.amount,
round: (precision = 2) =>
Money(currency, precisionRound(amount, precision)),
minus: m => Money(currency, amount - m.amount),
plus: m => Money(currency, amount + m.amount),
times: m => Money(currency, amount * m.amount),
compareTo: other => amount - other.amount,
asNegative: () => Money(currency, amount * -1),
valueOf: () => precisionRound(amount, 2), #3
toString: () => `${currency}${amount}`
}
))
);
// Zero
Money.zero = (currency = BTC) => Money(currency, 0); #4
// Static helper functions
Money.sum = (m1, m2) => m1.add(m2);
Money.subtract = (m1, m2) => m1.minus(m2);
Money.multiply = (m1, m2) => m1.times(m2);
function precisionRound(number, precision) {
const factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
The Money implementation freezes its constructed object and defines operations that construct another Money value. section 4.5























