Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/361.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 为什么通过setTimeout调用原型函数时会丢失实例信息?_Javascript - Fatal编程技术网

Javascript 为什么通过setTimeout调用原型函数时会丢失实例信息?

Javascript 为什么通过setTimeout调用原型函数时会丢失实例信息?,javascript,Javascript,我想我缺少了一些关于JavaScript中对象和原型函数的关键概念 我有以下资料: function Bouncer(ctx, numBalls) { this.ctx = ctx; this.numBalls = numBalls; this.balls = undefined; } Bouncer.prototype.init = function() { var randBalls = []; for(var i = 0; i < this.

我想我缺少了一些关于JavaScript中对象和原型函数的关键概念

我有以下资料:

function Bouncer(ctx, numBalls) {
    this.ctx = ctx;
    this.numBalls = numBalls;
    this.balls = undefined;
}

Bouncer.prototype.init = function() {
    var randBalls = [];
    for(var i = 0; i < this.numBalls; i++) {
        var x = Math.floor(Math.random()*400+1);
        var y = Math.floor(Math.random()*400+1);
        var r = Math.floor(Math.random()*10+5);
        randBalls.push(new Ball(x, y, 15, "#FF0000"));
    }
    this.balls = randBalls;
    this.step();
}

Bouncer.prototype.render = function() { 
    this.ctx.clearRect(0, 0, 400, 400);  
    for(var i = 0; i < this.balls.length; i++) {
        this.balls[i].render(this.ctx);
    }
}

Bouncer.prototype.step = function() {
    for(var i = 0; i < this.balls.length; i++) {
        this.balls[i].yPos -= 1;
    }      
    this.render();
    setTimeout(this.step, 1000);
}
init()函数将调用具有setTimeout的步骤来循环step函数

这适用于对步骤()的第一次调用。但是,在第二次调用中(当setTimeout触发步骤时),实例变量“balls”未定义。因此,在我的step函数中,第二个调用将爆炸,表示未定义的没有“length”属性

为什么从setTimeout()调用步骤时会丢失实例信息


我如何重新构造它,以便通过超时循环,仍然可以访问这些实例变量?

调用Javascript函数时,this的值由调用站点决定

当您将
this.step
传递到
setTimeout
时,
this
不会神奇地保留下来;它只通过
步骤
函数本身。
setTimeout
使用
this
作为
window
调用其回调

您需要创建一个在右侧对象上调用
步骤的闭包:

var me = this;
setTimeout(function() { me.step(); }, 500);

有关
this
和闭包之间的区别的更多信息,请在调用
setTimeout(this.step,1000)时使用
,当您传递对
步骤
方法的引用时,
步骤
方法将丢失其所需的
上下文。按照您现在执行的方式,当通过
setTimeout
调用
this.step
时,
this===window
而不是
Bouncer
实例

这很容易修复;只需使用匿名函数,并保留对该
的引用:

Bouncer.prototype.step = function() {
    var that = this; // keep a reference
    for(var i = 0; i < this.balls.length; i++) {
        this.balls[i].yPos -= 1;
    }      
    this.render();
    setTimeout(function () { 
        that.step()
    }, 1000);
}
Bouncer.prototype.step=function(){
var that=this;//保留一个引用
for(var i=0;i
这是一个关闭问题,如@SLaks所示

试试这个:

Bouncer.prototype.step = function() {
    for(var i = 0; i < this.balls.length; i++) {
        this.balls[i].yPos -= 1;
    }      
    var self = this;
    this.render();
    setTimeout(function() {self.step();}, 1000);
}
Bouncer.prototype.step=function(){
for(var i=0;i
这是相当标准的“此”范围问题。关于在执行函数时错误理解“this”的上下文,有很多问题。我建议你仔细阅读一下

但是,为了回答您的问题,它之所以有效,是因为您正在调用this.step(),而在该上下文中,“this”是您所需的Bouncer实例

第二次(以及后续)它不起作用,因为当您指定要由setTimeout调用的函数时,“窗口”上下文将调用该函数。这是因为您正在传递对step函数的引用,而该引用中不包含上下文

相反,您可以通过在匿名方法中从正确的作用域调用上下文来维护上下文:

var self = this;
setTimeout(function(){ self.step(); }, 1000);

我相当肯定setTimeout执行的任何操作都是在全局范围内执行的,因此对
this
的引用不再指向您的函数,而是指向
窗口

要解决此问题,只需将此
缓存为步骤中的局部变量,然后在setTimeout调用中引用该变量:

Bouncer.prototype.step = function() {
    for(var i = 0; i < this.balls.length; i++) {
        this.balls[i].yPos -= 1;
    }      
    this.render();
    var stepCache = this;
    setTimeout(function () { stepCache.step() }, 1000);
}
Bouncer.prototype.step=function(){
for(var i=0;i
其他人指出了调用上下文问题,但这里有一个不同的解决方案:

setTimeout( this.step.bind( this ), 1000 );
它使用方法发送一个函数,该函数的调用上下文绑定到作为第一个参数传递的任何内容


如果需要支持不支持
.bind()
的JS环境,我提供的文档链接提供了一个解决方案,在大多数情况下都足够了

从文档中:


这将通过
Function.prototype
向所有函数添加
.bind()
垫片(如果它还不存在)。

比我快20秒,并用一个更简洁的示例引导。干得好
setTimeout( this.step.bind( this ), 1000 );
if (!Function.prototype.bind) {
    Function.prototype.bind = function (oThis) {
        if (typeof this !== "function") // closest thing possible to the ECMAScript 5 internal IsCallable function
        throw new TypeError("Function.prototype.bind - what is trying to be fBound is not callable");
        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this,
            fNOP = function () {},
            fBound = function () {
                return fToBind.apply(this instanceof fNOP ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments)));
            };
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    };
}