通过合成创建的对象上的方法无法访问所有属性

时间:2019-01-14 17:45:12

标签: javascript functional-programming object-composition

我正在建立产品家族的概念,成员具有不同的类型(accountHolderpayingCustomerstudent等)。最初,我将它们构建为FamilyMember的子类,但最终我得到了一些重复的代码,最终遇到了一个重大问题:我们平台的student也可以是唯一的payingCustomeraccountHolder

鉴于在JS中如何广泛宣传对象组合是一个好主意,我决定走这条路。但是,如果某个特定对象类型(例如accountHolder)的方法属于另一种对象类型(例如student),则无法访问实例化对象的属性。

为了更加客观,我决定使用以下代码来复制行为:

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

我期望john的方法isOfAge能够访问属性isAdult,因为它位于对象中。但是,从概念上讲,我理解为什么它不起作用:isOfAgestate的一种方法,而不是结果的adult实例。

如果我使用的是类或什至是传统的原型/构造函数机制,我就会知道如何使之工作(例如,附加到prototype上)。对于对象合成,我不知道如何到达那里,可能是由于缺乏FP的经验。

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

您可以在this中使用state代替isOfAge。这样,将在调用方法this时推论isOfAge,它将绑定到调用它的任何对象。但是,您必须使用常规函数而不是箭头来起作用(箭头函数没有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'

答案 1 :(得分:1)

对象组成

  
      
  • 由其他对象和语言原语构成的所有对象都是复合对象。

  •   
  • 创建复合对象的动作称为“组合”。   
    ...

  •   
  • 串联通过扩展具有新属性(例如Object.assign(destination, a, b), {...a, ...b})的现有对象来构成对象。   
    ...
    The Hidden Treasures of Object Composition
  •   

从您的模式和使用factory function来看,它看起来像是串联的吗?下面的演示是串联组成。请注意,括号用payment括起来:

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

这允许payment作为对象而不是函数返回。如果您拥有的数据更加灵活,则将需要更少的方法。我认为name: stringage: number是实用的属性,或者在您的情况下使用name: stringadult: boolean


演示

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());