0

If '_position' is a Point object member of Sprite, with Number members x and y, and its own get/set definitions, how can I apply a higher level 'set' so this will work (EDIT: I would like to reach the 'set' function in Sprite, rather than the 'set' function in Point):

// EDIT: example calling line, embedding context should be unimportant
// 'this' is a Sprite instance
this.position.set(x, y);


Object.defineProperties(Sprite.prototype, {

// a bunch of other properties excised here

    position: {
        get: function() {
            return this._position;
        },

        set: function(_pos) {
            this._position.x = this.transform.x = _pos.x;
            this._position.y = this.transform.y = _pos.y;
            this.transform.updateTransform();
        }
    }
};

What happens currently is that "this.position" goes to the "position.get" property, and then "this.position.set" goes through to the Point set function. It seems like attempts to 'set' any object with it's own get/set will drop down to the lowest level.

It feels like there must be a way to change this behaviour, but my google and stackoverflow searches are not coming up with an answer. It's likely there's a proper term for what I want to do here, without which I'm struggling the phrase the question correctly.

For instance: this is close, but doesn't quite work in my situation

EDIT: I don't control the source file for Point, or the function where the this.position.set(x,y); is held. Although I may be able to get permission for a change to the calling syntax if I can prove that it won't break the other objects which actually want to use the Point set function.

Community
  • 1
  • 1
PeteB
  • 372
  • 1
  • 10
  • First time poster here... please let me know if I've missed anything or phrased the question poorly. – PeteB Nov 03 '15 at 01:56
  • Did you mean to do `this.position = {x:x, y:y};`? Unless you assign to the property, your setter won't get executed. – Bergi Nov 03 '15 at 01:59
  • We cannot see your `Sprite` Constructor. Is all this code inside it? Bad coding practice. `Object.hasOwnProperty(propString)` will test to see if the property belongs to the specific Object. – StackSlave Nov 03 '15 at 02:03
  • "*a way to change this behaviour*" - to what? It might be obvious to you, but I'm missing the expected behavior. *How* should the code you've shown "work"? You only told us (correctly) how the code currently works. – Bergi Nov 03 '15 at 02:04
  • @PHPglue: No, none of this code is inside the Sprite constructor. Can you give an example on how to use hasOwnProperty to access the 'set' function in Sprite rather than the one in Point? – PeteB Nov 03 '15 at 02:14
  • @Bergi: "this.position.set(x, y);" the *desired* (not expected) behaviour is that I can access the set for position in Sprite, instead of dropping down to the set for Point. The code is definitely working as expected, what I would like is an alternative. – PeteB Nov 03 '15 at 02:16
  • 1
    @PeteB: If you cannot change the code that does `….position.set(…)`, then your only choice is to return a custom object from your `.position` getter, with a `set` method that does what you want it to do. – Bergi Nov 03 '15 at 02:19
  • @Bergi: ah yes, that should do it I think. I did have a fleeting thought about adding a MyPoint object but got distracted before trying that approach. I'll give it a go and if it works maybe you could add that as an answer. – PeteB Nov 03 '15 at 02:22
  • 1
    It will work; but seriously, you should fix the function that calls `sprite.position.set()`, which basically breaks the [law of demeter](https://en.wikipedia.org/wiki/Law_of_Demeter). Make it use a `sprite.setPosition()` method or so. – Bergi Nov 03 '15 at 02:30
  • Outside a Constructor `this` is probably `window`. Don't use `this` outside a Constructor. Please rewrite code and get back to us. – StackSlave Nov 03 '15 at 02:52
  • @PHPglue: I fear you're going off on a bit of a tangent here, the embedding context of the example call should not affect the question as stated, provided that the code does what has been described. If it were using window, it wouldn't work at all, and if it's in a c'tor it shouldn't matter (apart from stylistic concerns which are beyond the remit of the question). – PeteB Nov 04 '15 at 08:42

2 Answers2

1

Since Bergi didn't post an answer, I will, since this is a good question.

If you don't control any of the surrounding code then, unfortunately, there are only ugly ways to do it.

A) Return a proxy object that will do whatever you want, as Bergi suggested.

The downside is that the surrounding code may be expecting the object it sent to match the object received. It's not inconceivable that the developer would compare object references instead of values, and then such a comparison would fail. But that would happen in your code already, so if you're using it already and nothing breaks you're good to go.

Also, if you go this way, you should make MyPoint.prototype = new Point() or something so that if new methods are added to Point, they will be available in your class as well.

B) Override this._position.set and/or setters and getters manually for each object in the constructor or wherever you set it. This is slow and extremely dirty and is an idea so bad that I wrote a snippet and then deleted it :)

Here are some options on what you should actually do.

A) Agree with the user of Sprite to use sprite.position = newPos or sprite.setPosition(newPos). Then you should return a Object.freeze()d copy of _position in the getter to make sure it's not used inappropriately.

B) Agree with the author of Point to emit some event when it's changed which could then be listened on by Sprite

a sad dude
  • 2,775
  • 17
  • 20
  • Accepted as answer because it is the most comprehensive reply. I have used the ugly solution of a custom object for now while I try to get changes made further up the line. I hadn't considered the possibility of comparing references, thank you for that warning! – PeteB Nov 06 '15 at 12:45
0

You don't understand Object.defineProperties. Don't feel bad, neither did I until I researched it further. You are confusing the set method of an individual property of the Object with a property called set which could really have it's own set method. The value of your property is where you can create a method, if you like. I'm guessing this is what you need to see:

Object.defineProperties(Sprite.prototype, {
  set: {
    // can also have get and set here
    value: function(){
      // inside Sprite.prototype.set
    }
  },
  position: {
    get: function() {
      return this._position;
    },
    set: function(_pos) {
      this._position.x = this.transform.x = _pos.x;
      this._position.y = this.transform.y = _pos.y;
      this.transform.updateTransform();
    }
  }
});

I think it's cool. This kind of thing must have been used behind the scene so length properties are really methods.

StackSlave
  • 10,613
  • 2
  • 18
  • 35
  • I don't know why this answer was marked down, it seems like a valid approach to a solution. I believe the example syntax might be a bit wonky though e.g. the use of 'value' as a property inside set. – PeteB Nov 06 '15 at 12:49