- 3.1 Declaring Functions
- 3.2 Higher-Order Functions
- 3.3 Function Literals
- 3.4 Arrow Functions
- 3.5 Functional Array Processing
- 3.6 Closures
- 3.7 Hard Objects
- 3.8 Strict Mode
- 3.9 Testing Argument Types
- 3.10 Supplying More or Fewer Arguments
- 3.11 Default Arguments
- 3.12 Rest Parameters and the Spread Operator
- 3.13 Simulating Named Arguments with Destructuring
- 3.14 Hoisting
- 3.15 Throwing Exceptions
- 3.16 Catching Exceptions
- 3.17 The finally Clause
- Exercises
3.7 Hard Objects
Let’s say we want to implement bank account objects. Each bank account has a balance. We can deposit and withdraw money.
We want to keep the object state private, so that nobody can modify it except through methods that we provide. Here is an outline of a factory function:
const createAccount = () => { . . . return { deposit: amount => { . . . }, withdraw: amount => { . . . }, getBalance: () => . . . } }
Then we can construct as many accounts as we like:
const harrysAccount = createAccount() const sallysAccount = createAccount() sallysAccount.deposit(500)
Note that an account object contains only methods, not data. After all, if we added the balance to the account object, anyone could modify it. There are no “private” properties in JavaScript.
Where do we store the data? It’s simple—as local variables in the factory function:
const createAccount = () => { let balance = 0 return { . . . } }
We capture the local data in the methods:
const createAccount = () => { . . . return { deposit: amount => { balance += amount }, withdraw: amount => { if (balance >= amount) balance -= amount }, getBalance: () => balance } }
Each account has its own captured balance variable, namely the one that was created when the factory function was called.
You can provide parameters in the factory function:
const createAccount = (initialBalance) => { let balance = initialBalance + 10 // Bonus for opening the account return { . . . } }
You can even capture the parameter variable instead of a local variable:
const createAccount = (balance) => { balance += 10 // Bonus for opening the account return { deposit: amount => { balance += amount }, . . . } }
At first glance, this looks like an odd way of producing objects. But these objects have two significant advantages. The state, consisting solely of captured local variables of the factory function, is automatically encapsulated. And you avoid the this parameter, which, as you will see in Chapter 4, is not straightforward in JavaScript.
This technique is sometimes called the “closure pattern” or “factory class pattern,” but I like the term that Douglas Crockford uses in his book How JavaScript Works. He calls them “hard objects.”