【问题标题】:Unable to access the object using `this`. `this` points to `window` object无法使用 `this` 访问对象。 `this` 指向 `window` 对象
【发布时间】:2013-03-08 01:39:59
【问题描述】:

我有这个 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 对象。为什么会这样?

【问题讨论】:

标签: javascript jquery oop object


【解决方案1】:

this 指针可以根据上下文指向许多事物之一:

  1. 在构造函数中(函数调用前面有newthis 指向新创建的构造函数实例。
  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 很神秘。
【解决方案2】:

这是使用 javascript 进行 OOP 的一种巧妙方法:

//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 类中。

【讨论】:

    【解决方案3】:

    试试这个:

    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 被执行时,this 内部的start 函数指向window 对象。并且窗口对象没有任何名为fooBar 的方法,您会收到错误消息。

    匿名函数方法:

    最好将anonymous function 传递给setInterval 并从中调用您的函数。如果您的函数使用this,这将很有用。

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

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

    绑定方法:

    Bind 将使您的生活更轻松,并提高代码的可读性。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-05
      相关资源
      最近更新 更多