【问题标题】:JavaScript OO issue when accessing instance methods访问实例方法时的 JavaScript OO 问题
【发布时间】:2011-12-04 13:42:50
【问题描述】:

我使用了一些关于 JavaScript 中 OOP 的教程。似乎很顺利,直到我遇到了以下...

Result of expression 'this.prepareFrame' [undefined] is not a function.

好的。我正在使用prototype 并使用this 关键字。

在此处查看我的app.js 页面...

// Plain "main" function called by the html page, like <script>main()</script>. This works nicely:

    function main() {
    engine = new AbstractEngine();
    engine.init();
    }

// Next creates a new instance of my AbstractEngine class. Seems to work so far:

    function AbstractEngine() {}

// The init() method we called is defined afterwards:

    AbstractEngine.prototype.init = function() {
    this.initLoop();
    }

// remark: I'm using "this", and according to the debugger, "this" refers to the AbstractEngine we made an instance of.

// Next, we define the initLoop method:

    AbstractEngine.prototype.initLoop = function() {
    setInterval(this.tick, 1000 / 30);
    }

// Fine, everything works fine so far. Now get to define the "tick" method:

    AbstractEngine.prototype.tick = function() {
    this.prepareFrame();
    this.update();
    }

// Ok, we're in trouble. The error message is output to the console and I don't understand why... The prepareFrame() and update() methods are defined right afterwards:

    AbstractEngine.prototype.update = function() {
    console.log('updating!');
    }

    AbstractEngine.prototype.prepareFrame = function() {
    console.log('preparing frame');
    }

// I read my code twice, but didn't find beginner's mistakes like typo or whatever. But well, cosnider I'm a beginner

【问题讨论】:

    标签: javascript oop this prototype-programming


    【解决方案1】:

    您需要将initLoop 的定义更改为:

    AbstractEngine.prototype.initLoop = function() {
        var that = this;
    
        setInterval(function () {
            that.tick();
        }, 1000 / 30);
    }
    

    这是因为this 的解析延迟到执行时间,并且在执行间隔时,this 指向window,而不是您的AbstractEngine 实例。

    通过在匿名函数中包装对tick 的调用,我们创建了一个闭包,它允许我们捕获that(我们将其设置为this)。通过调用 instance 上的 方法 tick that(这是 old this),我们可以恢复“this”的值) .

    【讨论】:

    • 我已经草拟了所有答案,但是您之前发布的答案甚至可以将其粘贴到答案框中。 :)
    【解决方案2】:

    这个:

    setInterval(this.tick, 1000 / 30);
    

    应该是:

    var that = this;
    setInterval(function () { that.tick(); }, 1000 / 30);
    

    或者:

    setInterval(this.tick.bind(this), 1000 / 30);
    

    解释:当你简单地传递this.tick时,这与执行以下操作相同:

    var temp = this.tick;
    setInterval(temp, 1000 / 30);
    

    但是现在,当temp 被调用时,JavaScript 不知道this 指针应该是什么;该信息会丢失,最终会绑定到全局对象 (window),如果您处于严格模式,则绑定到 null

    因此,您需要以某种方式确保 this.tick 被调用为具有适当 this 指针的方法。有两种方法可以做到这一点:

    1. 通过将其包装在捕获var that = this 的闭包中,您可以正确调用that.tick 作为原始this 指针上的方法。
    2. 或通过bind()this.tick 函数转换为this,确保每次都使用适当的this 作为方法调用它:换句话说,您甚至可以执行var temp = this.tick.bind(this) 和@987654341 @。

    请注意,bind 在较旧的浏览器中不可用(尤其是 IE es5-shim 的东西。

    【讨论】:

    • 您建议的解决方案将不起作用,因为 this 更改了传递给 setInterval 的匿名函数中的含义。请参阅马特的回答。
    • 您好,感谢您提供有效的答案和解释。我现在将消化信息;)谢谢!
    • 嗯...不确定那里发生了什么...要么发布了另一个答案,但后来我不小心评论了你的答案,或者我完全误读了你的答案。我可以发誓我看到了setInterval( function(){ this.tick(); }, 100/30 ); 的建议解决方案。对此感到抱歉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-21
    • 1970-01-01
    • 2016-05-06
    • 2015-07-09
    • 2017-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多