JavaScript - “this” pointing to Window instead of

2019-08-28 19:46发布

问题:

I'm facing for the first time OOP in JavaScript and all the troubles that comes with it...

I have this function/Object/class/whatever which has a method mainLoop() that should display some falling text - just like in the movie The Matrix. When I call it though I get undefined variables errors and using the debugger I see that inside mainLoop() this is pointing to Window instead of the object that called the method.

Here's the code:

function Matrix(config) {

    return {
        //[...lots of other vars...],
        drops: [],
        lines: [],
        //final string to put in the container
        str: "",

        mainLoop: function(){
            var tmp = "";
            //randomly create a "character drop"
            //(not if there's already a drop)
            for(var i = 0; i < this.cols; i++){
                if(this.drops[i] == 0 && Math.random() < this.freq){
                    this.drops[i] = irandom(this.rows) + 1;//new drop
                    tmp += randomChar();//output drop
                }
                else tmp += lines[0].charAt(i);
            }
            this.lines[0] = tmp; // <-------------- ERROR

            //update already created drops
            tmp = "";
            for(var j = 0; j < this.cols; j++){
                if(this.drops[j] > 0){
                    tmp += this.randomChar();
                    this.drops[j]--;
                }
                else tmp += " ";
            }
            this.lines[this.rowIndex] = tmp;
            this.rowIndex = (this.rowIndex+1) % this.rows;

            //render the entire text
            this.str = "";
            for(var l in this.lines)
                this.str += l + "<br/>";

            $(container).html = this.str;

        },

        start: function(){
            for(var i = 0; i < this.cols; i++)
                this.drops[i] = 0;
            timer = setInterval(this.mainLoop ,this.delay);
        },

        stop: function(){
            clearInterval(this.timer);
        },

        randomChar: function(){
            return this.chars.charAt(irandom(this.chars.length));
        },

        irandom: function(x){
            return Math.floor(Math.random()*x);
        }
    }
};

And then I call this function like this:

var config = {
    container: "#container",
    rows: 20,
    cols: 20,
    delay: 2000
};
var m = Matrix(config);
m.start();

The browser console says:

TypeError: this.lines is undefined

(code comment shows the exact point of the error). Furthermore, the debugger says that, at that point, this points to Window, not to m as I would expect... what's wrong with my reasoning? Thanks in advance for any help.

回答1:

Alter your start function:

    start: function(){
        var self = this;
        for(var i = 0; i < this.cols; i++)
            this.drops[i] = 0;
        timer = setInterval(function() {
            self.mainLoop(); 
        }, this.delay);
    }

this was poiting at window because the scope has changed.



回答2:

Since JavaScript is prototype-based, maybe (if you haven't already) try doing it following this model:

function Matrix(config) {

    this.property = config.firstmember;
    this.property2 = config.secondmember;

    return function() { console.log('hello world') };

}

Matrix.prototype = {

    someMethod: function() {
        //do something
    },
    start: function() {
        //console.log('hello world');
    },
    stop: function() {
        //do something
    }

}

var config = {
    firstMember: 'foo',
    secondMember: 'bar'
}

var m = new Matrix(config);

//console output: "hello world"

/*var m = {
    property: 'foo',
    property2: 'bar',
    ____proto___: Matrix: {
            someMethod: function() {
                //do something
            },
            start: function() {
                //console.log('hello world');
            },
            stop: function() {
                //do something
            }
        }

}*/

Also, see the answer to this question regarding setInterval.

setInterval callback functions are members of the Window object; therefore, 'this' refers to the window. You will need to pass in a parameter of the current object to the callback that is inside setInterval. See the link above for more details.



回答3:

If you need a reference to the calling object, I'd suggest passing it down as a parameter to the function.