最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Can't use 'this' inside setInterval - Stack Overflow

programmeradmin7浏览0评论

It seems that I can't use this inside of a setInerval function. Why is that? What is a elegant solution?

<html>
<script>
var something = function(tMessage){
    this.message = tMessage;
};

something.prototype = {
    start : function(counter){
       document.getElementById('result').innerHTML += this.message + "+++<br />";
        var looper = setInterval(
            function(){
                // This is printing "undefined"
                document.getElementById('result').innerHTML += this.message + "<br />";
                if(!counter--)
                    clearInterval(looper);
            },
            20
        );
    }
};


window.onload = function(){
    var e = new something("hi");
    e.start(2);
}
</script>
<body>
<div id="result"></div>
</body>
</html>

Edit

Thanks for the answers!! But can anyone explain the difference between sending an argument and setting and extra variable? Any memory issues?

It seems that I can't use this inside of a setInerval function. Why is that? What is a elegant solution?

<html>
<script>
var something = function(tMessage){
    this.message = tMessage;
};

something.prototype = {
    start : function(counter){
       document.getElementById('result').innerHTML += this.message + "+++<br />";
        var looper = setInterval(
            function(){
                // This is printing "undefined"
                document.getElementById('result').innerHTML += this.message + "<br />";
                if(!counter--)
                    clearInterval(looper);
            },
            20
        );
    }
};


window.onload = function(){
    var e = new something("hi");
    e.start(2);
}
</script>
<body>
<div id="result"></div>
</body>
</html>

Edit

Thanks for the answers!! But can anyone explain the difference between sending an argument and setting and extra variable? Any memory issues?

Share Improve this question edited Nov 21, 2012 at 6:58 Ramzi Khahil asked Nov 17, 2012 at 12:30 Ramzi KhahilRamzi Khahil 5,0525 gold badges38 silver badges72 bronze badges 1
  • 1 Because this == window inside the setInterval() callback function – Ja͢ck Commented Nov 17, 2012 at 12:33
Add a comment  | 

4 Answers 4

Reset to default 8

The problem here is that when your function is invoked this refers to the global object. To preserve the current scope, you could make a closure:

    var looper = setInterval(
        (function(scope){
            return function(){
                // This will no longer be printing "undefined"
                document.getElementById('result').innerHTML += scope.message + "<br />";
                if(!counter--)
                    clearInterval(looper);
            };
         })(this),
        20
    );

Instead of handwaving and trying to explain closures (which I am still in the process of fully mastering), I will direct you to this excellent answer: https://stackoverflow.com/a/111200/1726343

You cannot use this because you are in a new function block. I'm always creating a local variable (I'm sure there's a better way):

var c = this;
setInterval(function(
   c.variable = 1;
), 100);

Because question is tagged [prototypejs] but no one answer uses Prototype, I decided to write an answer which really uses Prototype (jsfiddle).

var Something = Class.create({
    initialize: function(tMessage) {
        this.message = tMessage;
    },

    start: function(counter) {
        this.counter = counter;
        $("result").innerHTML += this.message + "+++<br />";
        this.looper = setInterval(this.addMessage.bind(this), 20);
    },

    addMessage: function() {
        $("result").innerHTML += this.message + "<br />";
        if (!this.counter--) {
            clearInterval(this.looper);
        }
    }
});

document.observe("dom:loaded", function() {
    var e = new Something("hi");
    e.start(2);
});
  1. Use Class.create() to make more elegant classes.
  2. Use $() instead of document.getElementById().
  3. Use document.observe("dom:loaded") instead of window.onload
  4. Use bind(this) to bind function to context.

The key point of question is in execution context of function. Wherever you use this, it just points to the current execution context. When you invoke someObject.someFunction() then someFunction() is executed in context of someObject, and this inside of someFunction() will point to someObject.

But you can make assignment someOtherObject.someFunction = someObject.someFunction, and then in someOtherObject.someFunction() this will point to someOtherObject.

Also you can pass reference to function into another function, as you did in setInterval(), and then execution context will be defined by setInterval() (actually it will be global context, i.e. this===window).

In order to bind execution context to the function (predefine context, override invoker's context) you need to use .bind() method. It returns new function, which will invoke your original function with your desired context no matter which context will be at the run time.

Add

var self = this;

just before your call to setInterval and use self instead of this into the function defined in set*interval

发布评论

评论列表(0)

  1. 暂无评论