this.method Is not function with setInterval

社会主义新天地 提交于 2021-02-19 04:34:05

问题


I have this simple code:

var Modules = (function() {
    'use strict';

    return {
        
        TIMER: function (){
            var timer = null;
            
            return {
                time: 100,
                init: function() {
   
                    this.counter();
                    this.timer = window.setInterval(this.counter, 1000);
                },
                counter: function() {
                    this.time -= 1;

                    if (this.time <= 0) {
                        window.clearInterval(this.timer);
                        alert('Time expired');
                    }
                    console.log(this.time);
                    this.viewer();
                    
                },
                viewer: function() {
                    document.getElementById('timer').innerHTML = this.time;
                }
            }
        }
    };
}());

Modules.TIMER().init();
<div id="timer"></div>

And something is wrong because I got 2 errors:

this.viewer is not a function

and

NaN of this.time

What it wrong with my design pattern running over interval?

After extend TIMER to reset method:

reset: function() {
                    this.time = 100;
                }

and call it outside as: Modules.TIMER().reset(); ?

I got

this.time is not defined

.

Or inside init:

jQuery("body").on('keyup mouseup', function (e) {
                        this.reset();
                    });

I got error:

this.reset() is not s function.


回答1:


Your issue is coming from this line:

this.timer = window.setInterval(this.counter, 1000);

When you are invoking the callback in the setInterval method, the this in the callback function no longer refers to your TIMER object, but the window.


Solution A: Use .bind(this) to bind lexical this to callback

You will need to bind the current context to the callback:

this.timer = window.setInterval(this.counter.bind(this), 1000);

var Modules = (function() {
    'use strict';

    return {
        
        TIMER: function (){
            var timer = null;
            
            return {
                time: 100,
                init: function() {
   
                    this.counter();
                    this.timer = window.setInterval(this.counter.bind(this), 1000);
                },
                counter: function() {
                    this.time -= 1;

                    if (this.time <= 0) {
                        window.clearInterval(this.timer);
                        alert('Time expired');
                    }
                    console.log(this.time);
                    this.viewer();
                    
                },
                viewer: function() {
                    document.getElementById('timer').innerHTML = this.time;
                }
            }
        }
    };
}());

Modules.TIMER().init();
<div id="timer"></div>

Solution B: Use ES6 arrow function in setInterval callback

Note: Personally, I prefer this solution because it uses ES6, but if you are still supporting legacy browsers and do not want to transpile your JS, this might not be the best solution.

Another alternative will be using an arrow function in the callback of setInterval, instead of assigning the this.counter function as the callback directly:

this.timer = window.setInterval(() => this.counter(), 1000);

The arrow function preserves the lexical this, so when this.counter() is invoked it will be using the same context, i.e. the internal this will be referencing your TIMER object.

var Modules = (function() {
    'use strict';

    return {
        
        TIMER: function (){
            var timer = null;
            
            return {
                time: 100,
                init: function() {
   
                    this.counter();
                    this.timer = window.setInterval(() => this.counter(), 1000);
                },
                counter: function() {
                    this.time -= 1;

                    if (this.time <= 0) {
                        window.clearInterval(this.timer);
                        alert('Time expired');
                    }
                    console.log(this.time);
                    this.viewer();
                    
                },
                viewer: function() {
                    document.getElementById('timer').innerHTML = this.time;
                }
            }
        }
    };
}());

Modules.TIMER().init();
<div id="timer"></div>



回答2:


I found answer for reset from outside. I changed TIMER like this:

TIMER: (function (){
    // all stuff
}())

Then call is like: Modules.TIMER.reset(); and then it works as expected.



来源:https://stackoverflow.com/questions/56661554/this-method-is-not-function-with-setinterval

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!