无法使用“this”访问对象。 “this”指向“window”对象。

4
我有一个JavaScript构造函数 -
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();

给我报错信息 -
Uncaught TypeError: Object [object global] has no method 'fooBar' 

我尝试使用console.log,发现当我在setInterval中调用this.start时,this指向window对象。为什么会这样?


1
setInterval ÊòØ window.setIntervalÔºåÂõÝÊ≠§ this ÊåáÁöÑÊòØ window„ÄÇ - kidwon
1
@kidwon - 那不是原因。请看下面我的答案,了解正确的原因:https://dev59.com/XW_Xa4cB1Zd3GeqP5thL#15587589 - Aadit M Shah
3个回答

5
this指针根据上下文的不同可以指向多种不同的对象:
  1. 在构造函数中(使用new关键字调用函数),this指向新创建的实例。
  2. 当一个函数作为对象的方法被调用时(例如obj.funct()),函数内部的this指向该对象。
  3. 你可以通过使用callapplybind显式设置this指向的对象。
  4. 如果以上情况都不符合,则默认情况下this指针指向全局对象。在浏览器中,这是window对象。
在你的代码中,在setInterval内部调用了this.start。现在考虑下面这个setInterval的虚拟实现:
function setInterval(funct, delay) {
    // native code
}

重要的是要理解,start并不是作为this.start被调用的。它是作为funct被调用的。这就像做这样的事情:

var funct = this.start;
funct();

现在这两个函数通常会执行相同的操作,但有一个微小的问题 - 在第二种情况下,this指针指向全局对象,而在第一种情况下它指向当前的this
需要注意的是,我们正在谈论start内部的this指针。请考虑:
this.start();           // this inside start points to this
var funct = this.start;
funct();                // this inside funct (start) point to window

这不是一个错误。这是JavaScript的工作方式。当您将函数作为对象的方法调用(请参见我上面的第二点),函数内部的this指针指向该对象。
在第二种情况中,由于funct没有作为对象的方法调用,因此默认应用第四条规则。 因此,this指向window。
您可以通过将start绑定到当前的this指针,然后按如下所示将其传递给setInterval来解决此问题:
setInterval(this.start.bind(this), 200);

就是这样。希望这个解释可以帮助您更好地理解JavaScript的强大之处。


我已经想出了十几种方法来“解决”这个问题。但是我想知道为什么我的代码不起作用。你解释得非常好。非常感谢。this很神秘。 - ShuklaSannidhya

3

以下是使用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();

通过将方法和变量封装到命名空间/类中,可以使其更易于使用和维护。

因此,您可以像这样编写代码:

    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);

最初,ID设置为“Foo”。 1秒后,ID设置为“bar”。

请注意,所有变量和方法都封装在TestEngine类中。


2

试试这个:

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调用带有窗口上下文的start函数。这意味着当执行start时,start函数内部的this指向window对象。而window对象没有任何名为fooBar的方法,因此会出现错误。

匿名函数方法:

最好使用匿名函数传递给setInterval并从其中调用函数。如果您的函数使用this,则这将非常有用。

我所做的是,创建了一个临时变量self,并在它指向您的TestEngine实例时将this分配给它,然后使用它调用self.start()函数。

现在,在start函数内部,this将指向您的testInstance,并且一切都将按预期工作。

绑定方法:

Bind会使您的生活更轻松,同时还会提高代码的可读性。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接