3

我有这个 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发现,当我从 inside 调用时this.startsetInterval指向this对象window。为什么会这样?

4

3 回答 3

4

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

  1. 在构造函数中(以 . 开头的函数调用newthis指向新创建的构造函数实例。
  2. 当函数作为对象的方法被调用时(例如obj.funct()this,函数内部的指针指向该对象。
  3. 您可以使用 或 明确设置指向this的内容。callapplybind
  4. 如果以上都不是,则this指针默认指向全局对象。在浏览器中,这是window对象。

在您的情况下,您正在调用this.startinside setInterval。现在考虑这个虚拟实现setInterval

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

重要的是要理解它start不被称为this.start. 它被称为funct. 这就像做这样的事情:

var funct = this.start;
funct();

现在这两个函数通常会执行相同的操作,但是有一个小问题 -this在第二种情况下指针指向全局对象,而在第一种情况下它指向当前对象this

一个重要的区别是我们谈论的是this内部的指针start。考虑:

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 的美妙之处。

于 2013-03-23T13:56:46.740 回答
3

这是使用 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 类中。

于 2013-03-19T12:26:05.913 回答
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();

setIntervalstart使用窗口上下文调用函数。这意味着当start被执行时,this内部start函数指向window对象。并且 window 对象没有调用任何方法fooBar,您会收到错误消息。

匿名函数方法:

anonymous function传递给setInterval它并从中调用你的函数是一个好习惯。如果您的函数使用this.

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

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

绑定方法:

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

TestEngine.prototype.startMethod = function() {
  setInterval(this.start.bind(this), 200);
}
于 2013-03-19T11:47:44.320 回答