Search code examples
javascriptthisself

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.


Solution

  • this returns the [[ThisValue]] property of the EnvironmentRecord of the LexicalEnvironment of the ExecutionContext of the running function (see the spec).

    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.