Squid Dev Squid Dev - 4 months ago 8
Javascript Question

Problems with calling function inside function using `this`

window.onerror = function(e){alert(e)};
function main(){
this.work = [];
this.start_working = function() {
try{
if(this.work.length > 0){
var y = this.work.shift();
y.func(y.args);
}
}
catch(e){alert(e)};
};
this.add_work = function(f, a){
this.work.push({func:f, args:a});
};
this.foo = function(){
function footoo(){alert("bar");}
this.add_work(footoo);
};
this.foothree = function(){
this.add_work(this.foo);
};
this.start = function(){
setInterval(function(){this.start_working();}.bind(this), 1);
};
};
x = new main();
x.start();
x.foothree();


This is the watered down version of a function I am using elsewhere to run animations sequentially.

Expected behavior:

this.foothree
is processed by the interval adding foo to the interval.
this.foo
is then processed adding
footoo
to the interval which is finally processed alerting "bar".

Problem:

when
this.foothree
is processed, an error is thrown:


TypeError: this.add_work is not a function.





Why don't I use something simpler:

Basically I need a function which allows me to compose a more complex animation made of simpler animations to the queue to be processed so I can reuse that animation.
Foothree
in this instance is just simulating a call which would add the real animation,
footoo
, to the queue to be processed.
Footoo
would be composed of simpler animations,
foo
, which would be executed sequentially.

Answer

this points at the execution context. If it is called inside a function its value depends on how the function is called. If you call

this.foo = function(){
    function footoo(){alert("bar");}
    this.add_work(footoo);
};

in the function being declared there is no add_work method.

You should adopt var _self = this; pattern in order to point the correct calling context.

Basically the code should be rewritten as follows:

function main(){
    var _self = this;

    this.work = [];
    this.start_working = function() {
        try{
            if(_self.work.length > 0){
                var y = _self.work.shift();
                y.func(y.args);
            }
        }
        catch(e){alert(e)};
    };
    this.add_work = function(f, a){
        _self.work.push({func:f, args:a});
    };
    this.foo = function(){
        function footoo(){alert("bar");}
        _self.add_work(footoo);
    };
    this.foothree = function(){
        _self.add_work(_self.foo);
    };
    this.start = function(){
        setInterval(function(){_self.start_working();}, 1);
    };
};

Edit:

removed .bind(this) from original code.