首页 > 解决方案 > 较大对象中的 this.objectName 在执行期间变得未定义

问题描述

我用 HTML Canvas 创建了一个项目,它运行良好。一开始是这样的:

let canvas = document.querySelector('canvas');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let ctx = canvas.getContext('2d');

function Line(x, y, context, length, speed) {
    this.x = x;
    this.y = y;
    this.ctx = context;
    this.length = length;
    this.speed = speed;
    this.isDrawingNow = false;
    this.initialCoordinates = {
        x: this.x,
        y: this.y
    }
    console.log(this.initialCoordinates);

    this.ctx.beginPath();
    this.ctx.moveTo(this.x, this.y);

    this.draw = function() {
        this.ctx.lineTo(this.x, this.y);
        this.ctx.stroke();
    }

    this.updateRight = function() {
        this.x += this.speed;
        this.draw();
    }

    // Same functions as the above one, but for the other directions
}

let line = new Line(50, 50, ctx, 30, 1);

function animateRight() {
    if (line.initialCoordinates.x + line.length <= canvas.width) {
        line.isDrawingNow = true;
        if (line.x < line.initialCoordinates.x + line.length) {
            requestAnimationFrame(animateRight);
            line.updateRight();
        } else {
            line.initialCoordinates.x = line.x;
            line.isDrawingNow = false;
        }
    }
}

// Same functions as the above one, but for left, up, down directions

window.addEventListener('keydown', keyDown); // Event listener for the arrow keys

function keyDown(e) {
    if (e.key === 'ArrowLeft' && !line.isDrawingNow) {
        animateLeft();
    } else if (e.key === 'ArrowRight' && !line.isDrawingNow) {
        animateRight();
    // the other keys
    }
}

然后我认为我必须在 Line 对象中包含 animateRight() (animateDirection 函数)。我这样做了:

// Same as the old object plus:
this.animateRight = function() {
    console.log(this.initialCoordinates);
    if (this.initialCoordinates.x + this.length <= canvas.width) {
        this.isDrawingNow = true;
        if (this.x < this.initialCoordinates.x + this.length) {
            requestAnimationFrame(this.animateRight);
            this.updateRight();
        } else {
            this.initialCoordinates.x = this.x;
            this.isDrawingNow = false;
        }
    }
}

// The other directions...
// Now I have to call the animateDirection on arrowkey press like this (line is the new Line): 

function keyDown(e) {
    if (e.key === 'ArrowLeft' && !line.isDrawingNow) {
        line.animateLeft();
    } else if (e.key === 'ArrowRight' && !line.isDrawingNow) {
        line.animateRight();
}

不幸的是,新代码不起作用,当我按右箭头时,我收到错误“this.initialCoordinates is undefined”。我使用firefox调试器来查找问题。我看到 animateRight 被调用了一次,requestAnimationFrame 再次调用 animateRight ,但是这次 this.initialCoordinates 是未定义的,对于 this.length 也是如此,所以程序停止了。我不明白有什么问题。请帮我!我是 OOP 的新手...

标签: javascriptoop

解决方案


在 JavaScript 中, 的值this取决于函数的调用方式。如果函数像方法一样被调用,obj.func(),this将是obj。如果函数被正常调用,func(),this将是undefined(或非严格模式下的全局对象)。

您正在将一个函数 ( this.animateRight) 传递给requestAnimationFrame,它像普通函数一样调用它,因此在其中this无法正常工作。

在过去的 JS 中,这通常通过分配this给一个变量(通常称为self)来解决。但是,现在有更好的方法来解决这个问题:

1.箭头功能

箭头函数是上述规则的一个例外this。它们从定义它们的范围中获取值。

// [...]
if (this.x < this.initialCoordinates.x + this.length) {
    requestAnimationFrame(() => {
        this.animateRight();
    });
    this.updateRight();
} else {
// [...]

2.Function.prototype.bind()

函数的bind方法返回一个新函数,它将调用具有指定值的函数this。(在函数中绑定this到指定值。)

// [...]
if (this.x < this.initialCoordinates.x + this.length) {
    requestAnimationFrame(this.animateRight.bind(this));
    this.updateRight();
} else {
// [...]

这种方法不如箭头函数灵活,因为你只能直接调用一个函数。


推荐阅读