简体   繁体   中英

JavaScript counter increment on each click?

I need to correct this code in order to make it increment on each click.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Clicker</title>
    <meta name="description" content="">
    <style></style>
</head>
<body>
    <button>Click!</button>

<script>
    const counter = {
        cnt: 0,

        inc: function() {
            cnt++;
            console.log(cnt);
        }
    };

    const button = document.getElementsByTagName('button')[0];
    button.addEventListener('click', counter.inc, false);

</script>
</body>
</html>

The solution I have right now works fine, but not sure what are the concepts behind it, the solution is :

inc: function() {
    counter.cnt++;
    console.log(counter.cnt);
}

Its just a scoping issue,

 //Right approach would be,

 const counter = {
  cnt: 0,
  inc: function() {
   this.cnt++;
   console.log(this.cnt);
  }
 };

Your erroring code will look for a variable cnt declared in the current scope and traverse until the global one. It will throw error, if it doesn't find a reference.

Since you are passing the inc as an event listener, you have to bind the scope for it, otherwise, the this inside of that function will point to the element which triggers the event. Here in this case, the element would be a button .

button.addEventListener('click', counter.inc.bind(counter), false);

Or the most readable approach would be,

button.addEventListener('click', function() { 
 counter.inc() 
}, false);

Another reason for avoiding .bind is, once if you bind a context to a function using .bind . You cannot override the context of it after that. Even using .call/.apply

Use this.cnt to access the cnt variable inside the function.

inc: function() {
            this.cnt++;
            console.log(this.cnt);
        }

Also you need to bind the value of this to counter inside the function like this:

button.addEventListener('click', counter.inc.bind(counter), false);

Explaination:

Here we are providing a callback function as the second argument to addEventListener function. For this inside this callback to be equal to the counter object we are using bind .

According to MDN Web Docs

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

Therefore the value of this inside callback is set to counter and we can access its property cnt as this.cnt .

In the button.addEventListener() call you need to use either:

counter.inc.bind(counter)

or (since you appear to be using ES6)

() => counter.inc()

as the registered callback.

You also need to reference the correct cnt (ie this.cnt ) within the inc method.

The problem with your code is:

inc: function() {
   cnt++;
   console.log(cnt);
}

cnt will refer to a global variable, not the property of counter as you want. You need to set it using this.cnt++ .

The second things, is when you bind your addEventListener, it only passes the function, so this context when the function is called, is not actually the counter. You need to change it to:

button.addEventListener('click', function() {
      counter.inc();
}, false);

or button.addEventListener('click', () => counter.inc(), false);

or bind the counter.inc to counter like:

counter.inc = counter.inc.bind(counter);

The concept is that you are in is object literal, object literals encapsulate data, enclosing it in a tidy package, global This minimizes the use of global variables which can cause problems when combining code , in the first example you showed us you are retrieving global cnt variable and incrementing it which does'not exists in global scope, it is in counter scope, to retrieve it's properties and methods you need to call object first and then it's property like this object.(method or property)

inc: function() {
   counter.cnt++;//same as
   this.cnt++;//this
   console.log(counter.cnt);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM