2

I am building a counter example using an object just for practice and ran into a problem. I am trying to update an object property using a created method within my object method. I can get it to work when I declare the objects name and increment the property's value, but if I try to use the this keyword to represent the object my problem returns NaN. I think I know why without fully understanding. Can anyone help me to understand?

Example that works:

let counter = {
  num: 0,
  increase(){
    counter.num++;
    document.querySelector("#value").textContent = counter.num;
  },
  decrease(){
    counter.num--;
    document.querySelector("#value").textContent = counter.num;
  },
  reset(){
    counter.num = 0;
    document.querySelector("#value").textContent = counter.num;
  }
}

let x = document.querySelectorAll("button");
x[0].addEventListener("click", counter.increase);
x[1].addEventListener("click", counter.decrease);
x[2].addEventListener("click", counter.reset);
<div class="container">
  <div class="display">
    <h2>Counter</h2>
    <br>
    <span id="value">0</span>
  </div>
  <div class="btn-container">
    <button id="increase">Increase</button>
    <button id="decrease">Decrease</button>
    <button id="reset">Reset</button>
  </div>
</div>

Example that doesn't work:

let counter = {
  num: 0,
  increase(){
    this.num++;
    document.querySelector("#value").textContent = this.num;
  },
  decrease(){
    this.num--;
    document.querySelector("#value").textContent = this.num;
  },
  reset(){
    this.num = 0;
    document.querySelector("#value").textContent = this.num;
  }
}

let x = document.querySelectorAll("button");
x[0].addEventListener("click", counter.increase);
x[1].addEventListener("click", counter.decrease);
x[2].addEventListener("click", counter.reset);
<div class="container">
  <div class="display">
    <h2>Counter</h2>
    <br>
    <span id="value">0</span>
  </div>
  <div class="btn-container">
    <button id="increase">Increase</button>
    <button id="decrease">Decrease</button>
    <button id="reset">Reset</button>
  </div>
</div>

Shouldn't the this keyword point to the object (counter) making this example equivalent to the one that works?

ggorlen
  • 26,337
  • 5
  • 34
  • 50
Storm
  • 45
  • 4
  • “this” changes based on how the function is called. So “this” will be the element instead of the counter. – evolutionxbox Jan 08 '21 at 01:04
  • 1
    Does this answer your question? [How does the "this" keyword work?](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – evolutionxbox Jan 08 '21 at 01:07
  • 1
    @evolutionxbox is this because the this keyword is taking ownership of the button or method that it is attached to in this situation. I ask this because if I were to solely return this.num++; and then call the function without attaching it to an event. The increment will adhere. Also, is there a way in which to used the this keyword still while attaching the method to an event? – Storm Jan 08 '21 at 01:11
  • The event listener is changing the value of “this” to be the element. You must manually bind the value of “this” before giving it to the event listener – evolutionxbox Jan 08 '21 at 01:22
  • 2
    @evolutionxbox. Awesome. I figured that what the issue was, but really couldn't fully comprehend. Thx so much for you help. I will read the post that you recommended to get a clearer understanding – Storm Jan 08 '21 at 01:31
  • you can also manually call the methods yourself when the callback runs: `addEventListener("click", () => counter.increase());` rather than getting the event listener to call them for you, this way, you're invoking `.increase()` and the other methods on the `counter` object, which will keep the `this` as `counter` – Nick Parsons Jan 08 '21 at 01:34
  • @NickParsons originally that was an idea that I entertained. Is there any difference in calling the method manually than binding ?? Is it more or less efficient? Are there any side effect or using one over the other? – Storm Jan 08 '21 at 01:39
  • @Storm With `.bind()` you can set the `this` to be anything that you pass to `.bind()`, it doesn't just have to be `counter`. It also allows you to partially apply arguments, but that isn't really relevant to what you're doing here. So `.bind()` is more flexible. Using the callback/method approach, the `this` is implicitly set based on the object that you invoke the method on (ie: it gets set to `counter` and you can't change that). In terms of efficiency, I don't see there being much of a difference. Worrying about efficiency for small things like this isn't usually worth the time anyway :P – Nick Parsons Jan 08 '21 at 01:45
  • 1
    @NickParsons your 100% correct. Thx once again for the clarification. – Storm Jan 08 '21 at 01:47

1 Answers1

3

try this

x[0].addEventListener("click", counter.increase.bind(counter));
x[1].addEventListener("click", counter.decrease.bind(counter));
x[2].addEventListener("click", counter.reset.bind(counter));

you must bind with the object counter

Ari Firmanto
  • 304
  • 1
  • 14