liveBook · Manning

Money

As explained in The Joy of JavaScript

An immutable value object representing an amount of currency identified by its quantity and currency unit.

Money is an immutable value object representing an amount together with its currency unit. Its identity comes from those values: changing either creates a different Money entity. section 4.4.2 section 4.5

A Money object typically provides:

  • Readable fields such as amount and currency. section 4.5
  • Value comparison through equals and numeric ordering through compareTo. section 4.5
  • Arithmetic that returns new values: plus, minus, times, rounding, and negation. section 4.5
  • Conversion and display hooks through valueOf and toString. 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 factory curries currency and amount, builds a null-prototype object, adds value-oriented operations, then freezes and seals the result. section 4.5

Currency-specific construction

const USD = Money('$'); USD(3.0).amount; // 3 USD(7.0).currency; // $ USD(3.0).plus(USD(7.0)).equals(USD(10)); // true Money.zero('$'); section 4.5

Mutation fails

const threeDollars = USD(3.0); threeDollars.amount = 5; // TypeError: Cannot assign to read only property 'amount' Object.isFrozen(threeDollars); // true section 4.5

Symbol.toStringTag can label instances as Money(currency amount), making errors from attempted mutation easier to identify during debugging. section 7.5.1

How The Joy of JavaScript covers it

How deep it goes
Defines value-object identity and implements freezing, sealing, comparison, arithmetic, rounding, coercion, and formatting.
Examples
Uses Bitcoin and dollar values, currency factories, addition, zero values, mutation errors, and frozen or sealed object checks.
What it assumes
Assumes JavaScript objects and functions, while relying on object creation, property descriptors, freezing, and sealing.
What you can do after
Build it from scratch as a JavaScript factory that returns immutable currency values with domain-specific operations.
How it connects
Connects immutable objects with value objects, pure functions, object descriptors, arithmetic coercion, and Symbol.toStringTag.

This explainer was generated by Manning's knowledge graph from the text of The Joy of JavaScript. The section links point to the parts of the book it draws on.