【问题标题】:ES6 composition. How to define some kind of getterES6 组成。如何定义某种吸气剂
【发布时间】:2017-04-19 01:03:40
【问题描述】:

我写了一些代码。我正在创建 x1 对象,我希望它使用组合(而不是对象继承)来获取所有内容。当涉及到变量时,一切都很完美,但是当我想创建方法时(如果有可能制作 getter 会更好)它总是返回相同的值,即使我更改了变量。

代码说明一切。 如何让它按我想要的方式工作?

const CELLSIZE = 16;

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: () => {
        return state.posx * CELLSIZE //???
    } 
})

const wall = (posx, posy)  => {
    let setup = {
        //Later there will be some not-inherited variables 
    }
    let state = {
        posx,
        posy,
    }
    return Object.assign(
        {},
        defaultObject(state),
        setup
    )
}

const x1 = wall(2, 5)

console.log(x1.firstx()) // Returns 32

x1.posx = 1

console.log(x1.firstx()) // Still returns 32, I want it to return 16

【问题讨论】:

  • 我在答案中添加了解决吸气剂的问题。
  • 我通常不认为将属性从一个对象复制到另一个对象是“组合”。拥有一个对象,该对象引用了它用来提供其功能的从属对象(例如,wall 有一个 defaultObject 它使用),是的,但不是复制...

标签: javascript json ecmascript-6


【解决方案1】:

该方法正在读取您作为state 传入的对象:

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: () => {
        return state.posx * CELLSIZE // <== Note you're reading from `state`
    } 
})

相反,您想从您创建的对象中读取;但它比这更复杂,因为虽然您没有使用继承,但您正在复制一些东西,包括将该函数从一个对象复制到另一个对象(通过Object.assign)。因为您正在这样做,所以您不能为 firstx 函数使用箭头函数;您必须使用普通函数并依赖 this 在调用时正确设置:

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: function() {
        return this.posx * CELLSIZE; // <== Note reading from `this`
    } 
})

现场示例:

const CELLSIZE = 16;

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: function() {
        return this.posx * CELLSIZE; // <== Note reading from `this`
    } 
})

const wall = (posx, posy)  => {
    let setup = {
        //Later there will be some not-inherited variables 
    }
    let state = {
        posx,
        posy,
    }
    return Object.assign(
        {},
        defaultObject(state),
        setup
    )
}

const x1 = wall(2, 5)

console.log(x1.firstx()) // Returns 32

x1.posx = 1

console.log(x1.firstx()) // Returns 16

您可以使用函数表示法(如上)或方法表示法来定义firstx

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx() {
        return this.posx * CELLSIZE; // <== Note reading from `this`
    } 
})

在这种情况下你使用哪个并不重要,因为你没有在firstx 中使用super


你说你想要一个吸气剂;如果你愿意,你可以为它定义一个吸气剂:

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    get firstx() {
        return this.posx * CELLSIZE; // <== Note reading from `this`
    } 
})

但是,当您使用Object.assign 时,它将读取该属性的 并将其分配给新对象,而不是定义getter 的属性描述符.如果您愿意,您可以稍后将该属性描述符复制到自己身上,请参阅*** cmets:

现场示例:

const CELLSIZE = 16;

const defaultObject = (state) => ({
  posx: state.posx,
  posy: state.posy,
  get firstx() {                                          // ***
    return this.posx * CELLSIZE;                          // ***
  }                                                       // ***
})

const wall = (posx, posy) => {
  let setup = {
    //Later there will be some not-inherited variables 
  }
  let state = {
    posx,
    posy,
  }
  const original = defaultObject(state);                  // ***
  const obj = Object.assign({},
    original,                                             // ***
    setup
  )
  Object.defineProperty(                                  // ***
    obj,                                                  // ***
    "firstx",                                             // ***
    Object.getOwnPropertyDescriptor(original, "firstx")   // ***
  );                                                      // ***
  return obj;                                             // ***
}

const x1 = wall(2, 5)

console.log(x1.firstx) // Returns 32

x1.posx = 1

console.log(x1.firstx) // Returns 16

但是,如果您不同意让wall 复制对象而不是扩充它,那么Oriol's approach 更改wall 的工作方式要简单得多。

【讨论】:

    【解决方案2】:

    defaultObject 只能访问state。它使用firstx 方法创建一个对象,该方法使用state 数据。

    但是在wall 中,你得到那个对象,复制它的属性,然后把它扔掉。

    最后修改属性时,defaultObject 看不到,因为state 没有改变。

    相反,wall 应该返回由defaultObject 返回的相同对象(不是副本),defaultObject 的方法应该从它返回的对象中获取属性。

    const CELLSIZE = 16;
    
    const defaultObject = (state) => {
      let obj = {
        posx: state.posx,
        posy: state.posy,
        firstx: () => {
          return obj.posx * CELLSIZE
        }
      };
      return obj;
    };
    
    const wall = (posx, posy)  => {
      let setup = {};
      let state = {posx, posy};
      return Object.assign(
        defaultObject(state),
        setup
      );
    }
    
    const x1 = wall(2, 5)
    console.log(x1.firstx()) // Returns 32
    
    x1.posx = 1
    console.log(x1.firstx()) // Returns 16

    【讨论】:

    • 因为它是一种自己的方法,所以我个人的偏好是不像 T.J.Crowder 的回答那样使用this。考虑将this 与继承一起使用。
    • ...并且执行上述操作使使用 getter 变得更加简单(基本上只需将 firstx: () =&gt; { 更改为 get firstx() {),因为您没有将 getter 从一个对象复制到另一个对象。如果改变 wall 的工作方式是一种选择,这就是这样做的方式。
    • 最后一个问题。有没有可能for ex。覆盖defaultObject的方法?我想,let setup 会这样,但同样,我无法访问let obj 中的变量,如果我希望我的方法为特定对象返回不同的东西(不是使用墙创建的)
    • @MuFFes: defaultObject 每次调用都会返回一个 new 对象。你可以对那个对象做你喜欢的事情,它不会影响defaultObject创建的后续对象或你设置对象上的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 2023-02-02
    • 2018-11-16
    • 1970-01-01
    • 2018-04-05
    • 1970-01-01
    相关资源
    最近更新 更多