this.objectName в большем объекте становится неопределенным во время выполнения - PullRequest
0 голосов
/ 24 апреля 2020

Я создаю проект с 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
    }
}

Затем я подумал, что мне нужно включить animateRight () (функции animateDirection) в объект Line. Я сделал так:

// 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 ...

Ответы [ 2 ]

1 голос
/ 24 апреля 2020

I 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 {
// [...]

Этот метод не так гибок, как функции стрелок, потому что вы можете напрямую вызывать только одну функцию.

0 голосов
/ 24 апреля 2020

Сохраните указатель на сам класс как переменную в классе и используйте его внутри внутренней функции вместо this, потому что this внутри внутренней функции относится к самой функции, а не к классу:

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);

   var self = this; // <------------------------ HERE
   this.draw = function() {
        self.ctx.lineTo(this.x, this.y);
        self.ctx.stroke();
    }

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

    // Same functions as the above one, but for the other directions
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...