简体   繁体   English

访问实例方法时的JavaScript OO问题

[英]JavaScript OO issue when accessing instance methods

I've used a few tutorials on OOP in JavaScript. 我使用过一些有关JavaScript中OOP的教程。 It seemed to go well, until I met the following... 一切顺利,直到我遇到了以下问题...

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

Ok. 好。 I'm using prototype and make use of the this keyword. 我正在使用prototype并使用this关键字。

See my app.js page here... 在这里查看我的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

You need to change the definition of initLoop to be the following: 您需要将initLoop的定义更改为以下内容:

AbstractEngine.prototype.initLoop = function() {
    var that = this;

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

This is because the resolution of this is delayed until execution time and when the interval is executed, this points to window , rather than your instance of AbstractEngine . 这是因为该决议this被延迟,直到执行时间和执行的时间间隔时, this指向window ,而不是你的实例AbstractEngine

By wrapping the call to tick in an anonymous function, we create a closure which allows us to capture that (which we set to this ). 通过将对tick的调用包装在一个匿名函数中,我们创建了一个闭包,它允许我们捕获that闭包(将其设置为this )。 By calling the method tick on the instance that (which is the old this), we can restore the value of "this"). 通过在实例旧的 this)上调用方法 tick ,我们可以恢复“ this”的值。

This: 这个:

setInterval(this.tick, 1000 / 30);

Should be: 应该:

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

or alternately: 或者:

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

Explanation: when you pass simply this.tick , that is the same as doing the following: 说明:当您简单地传递this.tick ,与执行以下操作相同:

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

But now, when temp is called, JavaScript doesn't know what the this pointer should be; 但是现在,当调用temp时,JavaScript不知道this指针应该是什么。 that information gets lost, and it ends up getting bound to the global object ( window ), or to null if you are in strict mode. 该信息会丢失,最终会绑定到全局对象( window ),如果处于严格模式下则将绑定为null

So you need to somehow ensure that this.tick is called as a method with the appropriate this pointer. 因此,您需要以某种方式确保使用适当的this指针作为一个方法来调用this.tick There are two ways to do this: 有两种方法可以做到这一点:

  1. By wrapping it in a closure that captures var that = this , you can properly call that.tick as a method on the original this pointer. 通过将其包装在捕获var that = this的闭包中,您可以正确地调用that.tick作为原始this指针上的方法。
  2. Or by bind() ing the this.tick function to this , you ensure it is called as a method with the appropriate this every time: in other words, you could even do var temp = this.tick.bind(this) and setInterval(temp, 1000 / 30) . 或通过bind()this.tick函数this进行调用,可以确保每次都使用适当的this调用该方法:换句话说,您甚至可以执行var temp = this.tick.bind(this)setInterval(temp, 1000 / 30)

Note that bind is not available in older browsers (notably IE <= 8 and all Safaris so far up to and including 5.1), in which case you'd need something like es5-shim . 请注意, bind在较旧的浏览器中不可用(特别是IE <= 8以及到目前为止(包括5.1)及以下的所有Safari ),在这种情况下,您将需要es5-shim之类的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM