理解与参数有关的.bind()

时间:2017-06-07 05:58:27

标签: javascript this bind requestanimationframe

我正在审查一些游戏代码,我有一件我不太了解的内容。

var game = (function(){

   // Start with our constructor
   function Game(){
        this.viewport = document.getElementById('viewport');
        this.ctx = this.viewport.getContext('2d');
    };

    Game.prototype.updateAnimation = function(t){

        // work out the delta time
        this.dt = this.lastFrameTime ? ((t - this.lastFrameTime)/1000.0).fixed() : 0.016;

        // store last frame time
        this.lastFrameTime = t;

        // run relevent updates here

        // queue the next animation time.
        this.animationId = window.requestAnimationFrame( this.updateAnimation.bind(this), this.viewport );
    }

    // return game class
    return Game;

})();

然后

// call new game object
var clientGame = new game();

// call event loop
clientGame.updateAnimation(new Date().getTime());

请求window.requestAnimationFrame( this.updateAnimation.bind(this), this.viewport );运行时。它如何知道参数t的价值。它似乎每次更新,但我不明白为什么。有人可以解释一下发生什么事吗?感谢。

修改

Number原型链上附加了一个名为fixed()的函数。这是为了清晰起见

 Number.prototype.fixed = function(n) { n = n || 3; return parseFloat(this.toFixed(n)); };

2 个答案:

答案 0 :(得分:2)

首先,代码可以写得更好,每次都不需要再次绑定函数,只需要执行一次(例如在对象初始化期间)

其次,函数requestAnimationFrame只接受一个参数,因此对第二个参数没有意义,第一个参数是浏览器发送给回调的t

方法.bind不会更改函数参数(可能除了arguments.callee,但这是多余的)但只有函数的context,因此第一个参数保持t }(timestamp

更多信息https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

修改

因此用户正在使用一些polyfill,它允许向requestAnimationFrame发送两个参数

答案 1 :(得分:1)

所以requestAnimationFrame有一个参数,一个回调,它将时间戳作为参数。 bind函数返回原始函数但在不同的范围内。因此,requestAnimationFrame会使用时间戳调用updateAnimation,这是您的时间来源。

现在要了解bind调用背后的原因,您需要了解JavaScript中的范围。

当您定义一个函数并将其作为参数传递给另一个函数时,您实际上正在调用第二个函数中的第一个函数。这意味着如果你有一个类实例并且想要将一个实例方法作为回调传递给一个函数,你需要使用一个保留当前作用域的箭头函数,或者将该方法绑定到正确的作用域(你的类实例)。 / p>

我希望以下示例更好地展示我正在谈论的内容。

class OuterScope {
    constructor() {
        this.label = 'test';
    }

    print() {
        console.log(this.label);
    }
}

function innerScope(callback) {
    callback();
}

var scope = new OuterScope();
innerScope(scope.print); // throws error because the scope the function is called in doesn't have a property called label
innerScope(scope.print.bind(scope)); // prints label

现在将其应用于您的requestAnimationFrame问题:

class Game {
    updateAnimation(t) {
        // this.lastFrameTime would always be undefined when called without binding
        this.dt = this.lastFrameTime ? ((t - this.lastFrameTime)/1000.0).fixed() : 0.016;

        // store last frame time
        this.lastFrameTime = t;

        window.requestAnimationFrame(this.updateAnimation.bind(this)); // updateAnimation now always runs in the scope of the current Game instance
        // window.requestAnimationFrame(this.updateAnimation); // updateAnimation runs in the scope of the window
    }
}

window.requestAnimationFrame = function requestAnimationFrame(callback) {
    callback(new Date().getTime()); // calls the callback with one argument
}