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

javascript - Unable to access the object using `this`. `this` points to `window` object - Stack Overflow

programmeradmin7浏览0评论

I have this Javascript constructor-

function TestEngine() {
    this.id='Foo';
}

TestEngine.prototype.fooBar = function() {
    this.id='bar';
    return true;
}

TestEngine.prototype.start = function() {
    this.fooBar();
}

TestEngine.prototype.startMethod = function() {
    inter = setInterval(this.start, 200);
}

var test = new TestEngine();
test.startMethod();

Gives me this error -

Uncaught TypeError: Object [object global] has no method 'fooBar' 

I tried console.log and found out that when I call this.start from within setInterval, this points to the window object. Why is this so?

I have this Javascript constructor-

function TestEngine() {
    this.id='Foo';
}

TestEngine.prototype.fooBar = function() {
    this.id='bar';
    return true;
}

TestEngine.prototype.start = function() {
    this.fooBar();
}

TestEngine.prototype.startMethod = function() {
    inter = setInterval(this.start, 200);
}

var test = new TestEngine();
test.startMethod();

Gives me this error -

Uncaught TypeError: Object [object global] has no method 'fooBar' 

I tried console.log and found out that when I call this.start from within setInterval, this points to the window object. Why is this so?

Share Improve this question asked Mar 19, 2013 at 11:41 ShuklaSannidhyaShuklaSannidhya 8,9869 gold badges35 silver badges45 bronze badges 2
  • 1 setInterval is window.setInterval so this refers to window – kidwon Commented Mar 19, 2013 at 11:49
  • 1 @kidwon - That's not the reason. See my answer below for the correct reason: stackoverflow./a/15587589/783743 – Aadit M Shah Commented Mar 23, 2013 at 14:05
Add a ment  | 

3 Answers 3

Reset to default 5

The this pointer can point to one of many things depending upon the context:

  1. In constructor functions (function calls preceded by new) this points to the newly created instance of the constructor.
  2. When a function is called as a method of an object (e.g. obj.funct()) then the this pointer inside the function points to the object.
  3. You can explicitly set what this points to by using call, apply or bind.
  4. If none of the above then the this pointer points to the global object by default. In browsers this is the window object.

In your case you're calling this.start inside setInterval. Now consider this dummy implementation of setInterval:

function setInterval(funct, delay) {
    // native code
}

It's important to understand that start is not being called as this.start. It's being called as funct. It's like doing something like this:

var funct = this.start;
funct();

Now both these functions would normally execute the same, but there's one tiny problem - the this pointer points to the global object in the second case while it points to the current this in the first.

An important distinction to make is that we're talking about the this pointer inside start. Consider:

this.start();           // this inside start points to this
var funct = this.start;
funct();                // this inside funct (start) point to window

This is not a bug. This is the way JavaScript works. When you call a function as a method of an object (see my second point above) the this pointer inside the function points to that object.

In the second case since funct is not being called as a method of an object the fourth rule is applied by default. Hence this points to window.

You can solve this problem by binding start to the current this pointer and then passing it to setInterval as follows:

setInterval(this.start.bind(this), 200);

That's it. Hope this explanation helped you understand a little bit more about the awesomeness of JavaScript.

Here is a neat way to do OOP with javascript:

//Global Namespace:
var MyNamespace = MyNamespace || {};

//Classes:
MyNamespace.MyObject = function () {

    this.PublicVar = 'public'; //Public variable
    var _privatVar = 'private'; //Private variable

    //Public methods:
    this.PublicMethod = function () {
    }

    //Private methods:
    function PrivateMethod() {
    }

}

//USAGE EXAMPLE:
var myObj = new MyNamespace.MyObject();
myObj.PublicMethod();

This way you encapsulate your methods and variables into a namespace/class to make it much easier use and maintain.

Therefore you could write your code like this:

    var MyNamespace = MyNamespace || {};

    //Class: TestEngine
    MyNamespace.TestEngine = function () {

        this.ID = null;
        var _inter = null;

        //Public methods:
        this.StartMethod = function (id) {
            this.ID = id;
            _inter = setInterval(Start, 1000);
        }

        //Private methods:
        function Start() {
            FooBar();
            console.log(this.ID);
        }

        function FooBar() {
            this.ID = 'bar';
            return true;
        }

    }

    //USAGE EXAMPLE:
    var testEngine = new MyNamespace.TestEngine();
    testEngine.StartMethod('Foo');
    console.log(testEngine.ID);

Initially, the ID is set to 'Foo' After 1 second the ID is set to 'bar'

Notice all variables and methods are encapsulated inside the TestEngine class.

Try this:

function TestEngine() {
    this.id='Foo';
}

TestEngine.prototype.fooBar = function() {
    this.id='bar';
    return true;
}

TestEngine.prototype.start = function() {
    this.fooBar();
}

TestEngine.prototype.startMethod = function() {
    var self = this;
    var inter = setInterval(function() {
       self.start();
    }, 200);
}

var test = new TestEngine();
test.startMethod();

setInterval calls start function with window context. It means when start gets executed, this inside start function points to window object. And window object don't have any method called fooBar & you get the error.

Anonymous function approach:

It is a good practice to pass anonymous function to setInterval and call your function from it. This will be useful if your function makes use of this.

What I did is, created a temp variable self & assigned this to it when it is pointing your TestEngine instance & calling self.start() function with it.

Now inside start function, this will be pointing to your testInstance & everything will work as expected.

Bind approach:

Bind will make your life easier & also increase readability of your code.

TestEngine.prototype.startMethod = function() {
  setInterval(this.start.bind(this), 200);
}
发布评论

评论列表(0)

  1. 暂无评论