-2

Consider this piece of code:

var states = {
  "default" : {
    foo: "foo",
    mouseReleased: function() {
      console.log("Mouse released");
      this.foo = "bar";
    }
  }
}

var canvas = function(states) {
  return {
    states: states,
    currentState: "default",
    draw: function() {
      if (this.states[this.currentState].mouseReleased != undefined) {
        window.mouseReleased = this.states[this.currentState].mouseReleased
      }
    }
  }
}

When I release the mouse, the console shows "Mouse released", but states.default.foo does not change to bar. Is there a way to not use window.mouseReleased's this? I've read a little bit about bind, but I am not sure what it means. Am I searching in the right place?

Simponic
  • 1
  • 2
  • 1
    This isn't valid javascript: `var canvas(states) = function() { /* ... */ }`. And this doesn't look at all like a valid p5.js sketch. You should include a minimal reproducible example in your question https://stackoverflow.com/help/minimal-reproducible-example – Paul Wheeler Jul 16 '21 at 22:33
  • 1
    Therefore, `this` does not refer to `states.default`. What does it refer to? See [How to access the correct `this` inside a callback?](/q/20279484/4642212) and [How does the “this” keyword work?](/q/3127429/4642212). – Sebastian Simon Jul 16 '21 at 22:36
  • This code is an example, it isn't meant to be run. Rather it is what I am trying to do. The actual code does not look like this. – Simponic Jul 16 '21 at 23:00

1 Answers1

0

Despite the shared code being otherwise invalid and very unusual, it does look like this is a classic function binding issue.

The problem is that the meaning of the this keyword is somewhat unusual in Javascript. By default in the body of a function, this will refer to the object to the left of the dot when the function was invoked. So, for example:

let objectA = {
  whoAmI: 'A',
  sayA: function() {
    console.log(this.whoAmI);
  }
};

let objectB = {
  whoAmI: 'B'
};

objectB.sayA = objectA.sayA;

// Logs 'A'
objectA.sayA();
// Logs 'B' because `this` now refers to `objectB`
objectB.sayA();

let defaultThis = objectA.sayA;
// This will log undefined because `this` defaults to the global scope, or `window`
defaultThis();

Where bind() comes in is when you want to lock in a particular value for this. So, if I have a class method that I want to use directly as an event handler, I will need call bind() with the object that I want this to refer to:

let obj = {
  state: 0,
  handler: function() { console.log(this.state); this.state++; }
}

// This logs undefined, NaN, NaN, NaN because `this` will be `window` which does not have a `state` property
window.addEventListener('click', obj.handler);
// This logs 0, 1, 2, 3
window.addEventListener('click', obj.handler.bind(obj));

The same principle will apply when it comes to p5.js event handler functions.

Paul Wheeler
  • 18,988
  • 3
  • 28
  • 41
  • I was able to solve it with `window.mouseReleased = () => this.states[this.currentState].mouseReleased.apply(this.states[this.currentState]);` Trying to use `window.mouseReleased = this.states[this.currentState].mouseReleased.bind(this)` did not work. – Simponic Jul 16 '21 at 23:07
  • Well that's because 1) arrow functions create closures when `this` is referenced so they don't suffer from the same nuances when referencing `this`, and 2) you would have had to use `this.states[this.currentState].mouseReleased.bind(this.states[this.currentState])` in order to bind the `mouseReleased` function to the correct object. – Paul Wheeler Jul 16 '21 at 23:34