prototype to create a Value Object type, and managing the object’s internal state via
this. Here’s how one might build the basics of a
Money Value Object in that style:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Of note is that we’re implementing a custom value equality method. Equality-by-value is an important aspect of the Value Object pattern.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
equals(...) method we need some way to compare our state to another Money’s state, which means we have to expose that state somehow. In this example I enabled this by adding a
_getState() method. The leading underscore is a common (if ugly) convention used to indicate that this is not part of the type’s public API. We saw a similar convention in our first class-based example too.
Hope is not a strategy
Having to use a leading underscore is exactly the sort of thing that I dislike about
A big reason for me prefering closures over
this is that we can mostly demarcate internal state and behaviour by using lexical scoping. This prevents any access to internals from outside the scope of the closure which instantiates an object. We can usually avoid reliance on hope as a strategy. Unfortunately this protection is too strong in this case - it prevents other instances of the same type from accessing a private API, since they are outside the lexical scope of the closure which created the instance. And thus I had to fall back to exposing state but marking it as internal with that leading underscore.
When a property is added to an object using a Symbol as a key - a symbol-keyed property - it doesn’t show up in things like
for .. in loops, or in calls to
Here’s how we leverage this to implement a better version of our Money type:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Note that within this file anything can access any Money instance’s state using the
stateFrom() function, which in turn uses the
stateAccessor symbol. You can see that we use
equals(other) to access the other Money instance’s state. However outside of this file there is no way to access either
stateAccessor (due to lexical scoping), and therefore no way to access any Money instance’s internal state.
Technically in some cases you can access a symbol-keyed property without access to the original symbol, but it’s pretty dirty:
1 2 3 4
In my opinion this is gross enough that anyone violating an object’s privacy this way would be aware that they are really going against the grain of that object’s API and should be responsible for the consequences.