简体   繁体   中英

Methods on an object created via composition can't access all properties

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 (eg accountHolder ) can't access properties of the instantiated object, if the property belong to another object type (eg 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 (eg 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'

Object Composition

  • All objects made from other objects and language primitives are composite objects.

  • The act of creating a composite object is known as composition.
    ...

  • Concatenation composes objects by extending an existing object with new properties, eg, Object.assign(destination, a, b), {...a, ...b} .
    ...
    The Hidden Treasures of Object Composition


So from your pattern and use of a factory function it looks like concatenation? The demo below is a concatenation composition. Note the parenthesis wrapped around the brackets of payment :

 const payment = (status) => ({...})

this allows payment to be returned as an object instead of a function. If you have data that's a little more flexible, you'll need less methods. name: string and age: number are the properties I used considering it practical or in your case name: string and adult: boolean .


Demo

 const payment = (status) => ({ adult: () => status.age > 17 ? true : false, account: () => status.adult() ? 'holder' : 'student' }); const member = (name, age) => { let status = { name, age }; return Object.assign(status, payment(status)); }; const soze = member('Kaiser Soze', 57); console.log(soze); console.log(soze.adult()); console.log(soze.account()); const jr = member('Kaiser Soze Jr.', 13); console.log(jr); console.log(jr.adult()); console.log(jr.account());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM