I'm building the concept of a family for a product, with members being of different types (accountHolder
, payingCustomer
, student
, and so on). Originally I built these as sub-classes of FamilyMember
, but I ended up with some repeated code and eventually bumped into a significant problem: a student
of our platform can also be the sole payingCustomer
and accountHolder
.
Given how object composition is widely touted as a good idea in JS, I decided to go that route. However, the methods of a particular object type (e.g. accountHolder
) can't access properties of the instantiated object, if the property belong to another object type (e.g. student
).
To make this more objective I've decided to replicate the behaviour using the following code:
const person = (props) => {
let state = {
name: props.name,
}
state.isOfAge = () => {
// state.isAdult is always undefined because
// isAdult doesn't exist in this object
return state.isAdult === true
}
return state
}
const adult = (props) => {
return {
isAdult: true,
}
}
const factory = (props) => {
return Object.assign({}, person(props), adult(props))
}
const john = factory({
name: 'John',
})
console.clear()
console.log(john) // { isAdult: true, name: "John", isOfAge... }
console.log(john.isOfAge()) // false
I was expecting john
's method isOfAge
to be able to access the property isAdult
, since it's in the object. However, conceptually I understand why it doesn't work: isOfAge
is a method of state
, not the resulting adult
instance.
If I were using classes or even a traditional prototype/constructor mechanism I knew how to make it work (e.g. attaching to prototype
). With object composition I've no idea how to get there, probably due to lacking experience with FP.
Thanks for the help!
You can use this
instead of state
inside isOfAge
. That way, the this
will be deduces when the method isOfAge
gets called, it will be bound to whatever object it is called on. Though, you'll have to use a regular function instead of an arrow one for that to work (arrow functions don't have a this
):
const person = (props) => {
let state = {
name: props.name,
}
state.isOfAge = function() { // use a regular function
return this.isAdult === true // use this here instead of state
}
return state
}
const adult = (props) => {
return {
isAdult: true,
}
}
const factory = (props) => {
return Object.assign({}, person(props), adult(props))
}
const john = factory({
name: 'John',
})
console.log(john);
console.log(john.isOfAge()); // returns 'true' because 'this' inside 'isOfAge' will be 'john'