无法读取未定义的 javascript 和 OOP 的 属性

cannot read property of undefined javascript and OOP

我有一个名为 'Game' 的对象(函数),它有一个名为 'gameLoop.' 的原型方法 我需要在一个时间间隔内调用这个循环,所以我尝试这样做:

setInterval(game.gameLoop,setIntervalAmount);

但收到 "TypeError: Cannot read property 'clearRect' of undefined(…)"

原型方法如下:

Game.prototype.gameLoop = function()
{
    this.context.clearRect(0,0,this.canvas.width, this.canvas.height);
    this.context.save();


    this.context.translate(this.canvas.width/2, this.canvas.height/2);
    this.context.scale(this.camera.scale,this.camera.scale);
    this.context.rotate(this.camera.rotate);
    this.context.translate(this.camera.x,this.camera.y);


    for(var i=0;i<this.objects.length;i++)
    {
        this.objects[i].updateSprite();
        this.objects[i].drawSprite(this.context);         
    }
    this.context.restore();
}

我仍然难以理解 Javascript 中的面向对象编程。我有一个工作版本,其中函数只是一个常规函数,我传入了一个游戏对象。有什么想法吗?

顺便说一句,这里有一些额外的代码可能会有帮助。

function Sprite(imgg,w,h)
{
    this.img = imgg;
    this.x = 350;//Math.random()*700;
    this.y = 350;//Math.random()*700;
    this.vx = 0;//Math.random()*8-4;
    this.vy = 0;//Math.random()*8-4;
    this.width = w;
    this.height = h;
    this.rotatespeed = 0.01;
    this.rotate = 40;

}
Sprite.prototype.drawSprite = function(ctx)
{

    ctx.save();


    ctx.translate(this.x,this.y);
    ctx.rotate(this.rotate);


    ctx.drawImage(this.img,0,0,this.img.width,this.img.height,-this.width/2,-this.height/2,this.width,this.height);


    ctx.restore();
}
Sprite.prototype.updateSprite = function()
{
    this.x += this.vx;
    this.y += this.vy;

    this.rotate += this.rotatespeed;


    if(this.x > 700)
        this.vx = -this.vx;
    if(this.x < 0)
        this.vx = -this.vy;

    if(this.y > 700)
        this.vy = -this.vy;
    if(this.y < 0)
        this.vy = -this.vy;
}
Sprite.prototype.mouseEventListener = function(evt, type)
{
    console.log("Hello");
}


//------------------------------------------
//GLOBAL VARIALBES

var setIntervalAmount = 30;
var scrollAmount = 0.5;
var game;
function Game()
{
    this.camera = new Object();
    this.camera.x = -350;
    this.camera.y = -350;
    this.camera.scale = 1;

    this.camera.rotate = 0;

    this.canvas = document.createElement("canvas");
    document.body.appendChild(this.canvas);
    this.canvas.id="mycanvas";
    this.canvas.width = 700;
    this.canvas.height = 700;
    this.context = this.canvas.getContext("2d");    

    var ctx = this.context;

    ctx.canvas.addEventListener('mousemove', function(event){
        var mouseX = event.clientX - ctx.canvas.offsetLeft;
        var mouseY = event.clientY - ctx.canvas.offsetTop;

        var canvasX = mouseX * ctx.canvas.width / ctx.canvas.clientWidth;
        var canvasY = mouseY * ctx.canvas.height / ctx.canvas.clientHeight;


        //console.log(canvasX+" | "+canvasY);

    });

    this.objects = new Array();  


}
Game.prototype.handleMouse = function(evt,type)
{
    for(var i=0;i<this.objects.length;i++)
    {
        this.objects[i].mouseEventListener(evt,type);
    }
};

Game.prototype.gameLoop = function()
{
    this.context.clearRect(0,0,this.canvas.width, this.canvas.height);
    this.context.save();


    this.context.translate(this.canvas.width/2, this.canvas.height/2);
    this.context.scale(this.camera.scale,this.camera.scale);
    this.context.rotate(this.camera.rotate);
    this.context.translate(this.camera.x,this.camera.y);


    for(var i=0;i<this.objects.length;i++)
    {
        this.objects[i].updateSprite();
        this.objects[i].drawSprite(this.context);         
    }
    this.context.restore();
}


/*Game.prototype.drawGame = function()
{
    var gameLoop = setInterval(function(){

        this.context.clearRect(0,0,this.canvas.width, this.canvas.height);
        this.context.save();


        this.context.translate(this.canvas.width/2, this.canvas.height/2);
        this.context.scale(this.camera.scale,this.camera.scale);
        this.context.rotate(this.camera.rotate);
        this.context.translate(this.camera.x,this.camera.y);


        for(var i=0;i<this.objects.length;i++)
        {
            this.objects[i].updateSprite();
            this.objects[i].drawSprite(this.context);         
        }
        this.context.restore();

    },setIntervalAmount); 

}*/



function mouseWheelListener() 
{
    var evt = window.event;
    console.log(evt.wheelDelta);
    if(evt.wheelDelta < 0)
        game.camera.scale /= (1+scrollAmount);
    else 
        game.camera.scale *= (1+scrollAmount);

}
function mouseDownListener()
{
    var evt = window.event;
    var type = "down"
    game.handleMouse(evt,type);
}
function mouseUpListener()
{
    var evt = window.event;
    var type = "up"
    game.handleMouse(evt,type);
}
function mouseMoveListener()
{
    var evt = window.event;
    var type = "move"
    game.handleMouse(evt,type);
}

//------------------

window.addEventListener('load',function(event){startgame();});

var dog = new Image();
dog.src = "grid.gif";

function startgame()
{
    game = new Game();

    for(var i=0;i<1;i++)
        game.objects.push(new Sprite(dog,250,250));


    setInterval(game.gameLoop,setIntervalAmount);

    document.getElementById("mycanvas").addEventListener("wheel", mouseWheelListener);
    document.getElementById("mycanvas").addEventListener("mousedown", mouseDownListener);
    document.getElementById("mycanvas").addEventListener("mouseup", mouseUpListener);
    document.getElementById("mycanvas").addEventListener("mousemove", mouseMoveListener);
}

setInterval 将在全局范围内调用 game.gameloop,这意味着函数内部 this 的值不是您期望的值。这可以通过将函数绑定到所需对象来解决。

例如将 setInterval(game.gameLoop,setIntervalAmount); 更改为 setInterval(game.gameLoop.bind(game),setIntervalAmount);.

example from MDN 可能会为您的代码的当前行为提供一些清晰度:

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();   
// returns 9 - The function gets invoked at the global scope

// Create a new function with 'this' bound to module
// New programmers might confuse the
// global var x with module's property x
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

您的代码唯一的问题是您执行 gameloop 方法的上下文。

通常setInterval、setTimeout函数在全局/window上下文下执行。因此,如果您在方法内指定 this,从技术上讲,您指的是全局上下文,即使您是在一个对象上执行它。

因此,为了确保您不会 运行 陷入问题,始终将方法作为 setInterval 的第一个参数,它将执行必要的功能,而不是方法引用,例如

setInterval(function(){/* 
    game.gameLoop()
*/}, 1000);

通过这种方式,您在全局上下文中执行 setInterval 函数,但您在游戏对象上显式调用方法。

function Sprite(imgg, w, h) {
  this.img = imgg;
  this.x = 350; //Math.random()*700;
  this.y = 350; //Math.random()*700;
  this.vx = 0; //Math.random()*8-4;
  this.vy = 0; //Math.random()*8-4;
  this.width = w;
  this.height = h;
  this.rotatespeed = 0.01;
  this.rotate = 40;

}
Sprite.prototype.drawSprite = function(ctx) {

  ctx.save();


  ctx.translate(this.x, this.y);
  ctx.rotate(this.rotate);


  ctx.drawImage(this.img, 0, 0, this.img.width, this.img.height, -this.width / 2, -this.height / 2, this.width, this.height);


  ctx.restore();
}
Sprite.prototype.updateSprite = function() {
  this.x += this.vx;
  this.y += this.vy;

  this.rotate += this.rotatespeed;


  if (this.x > 700)
    this.vx = -this.vx;
  if (this.x < 0)
    this.vx = -this.vy;

  if (this.y > 700)
    this.vy = -this.vy;
  if (this.y < 0)
    this.vy = -this.vy;
}
Sprite.prototype.mouseEventListener = function(evt, type) {
  //console.log("Hello");
}


//------------------------------------------
////GLOBAL VARIALBES

var setIntervalAmount = 200;
var scrollAmount = 0.5;
var game;

function Game() {
  this.camera = new Object();
  this.camera.x = -350;
  this.camera.y = -350;


  this.camera.scale = 1;

  this.camera.rotate = 0;

  this.canvas = document.createElement("canvas");
  document.body.appendChild(this.canvas);
  this.canvas.id = "mycanvas";
  this.canvas.width = 700;
  this.canvas.height = 700;
  this.context = this.canvas.getContext("2d");

  var ctx = this.context;

  ctx.canvas.addEventListener('mousemove', function(event) {
    var mouseX = event.clientX - ctx.canvas.offsetLeft;
    var mouseY = event.clientY - ctx.canvas.offsetTop;

    var canvasX = mouseX * ctx.canvas.width / ctx.canvas.clientWidth;
    var canvasY = mouseY * ctx.canvas.height / ctx.canvas.clientHeight;


    //console.log(canvasX+" | "+canvasY);

  });

  this.objects = new Array();


}
Game.prototype.handleMouse = function(evt, type) {
  for (var i = 0; i < this.objects.length; i++) {
    this.objects[i].mouseEventListener(evt, type);
  }
};

Game.prototype.gameLoop = function() {
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  this.context.save();


  this.context.translate(this.canvas.width / 2, this.canvas.height / 2);
  this.context.scale(this.camera.scale, this.camera.scale);
  this.context.rotate(this.camera.rotate);
  this.context.translate(this.camera.x, this.camera.y);


  for (var i = 0; i < this.objects.length; i++) {
    this.objects[i].updateSprite();
    this.objects[i].drawSprite(this.context);
  }
  this.context.restore();
}


/*Game.prototype.drawGame = function()
{
    var gameLoop = setInterval(function(){

        this.context.clearRect(0,0,this.canvas.width, this.canvas.height);
        this.context.save();


        this.context.translate(this.canvas.width/2, this.canvas.height/2);
        this.context.scale(this.camera.scale,this.camera.scale);
        this.context.rotate(this.camera.rotate);
        this.context.translate(this.camera.x,this.camera.y);


        for(var i=0;i<this.objects.length;i++)
        {
            this.objects[i].updateSprite();
            this.objects[i].drawSprite(this.context);         
        }
        this.context.restore();

    },setIntervalAmount); 

}*/



function mouseWheelListener() {
  var evt = window.event;
  console.log(evt.wheelDelta);
  if (evt.wheelDelta < 0)
    game.camera.scale /= (1 + scrollAmount);
  else
    game.camera.scale *= (1 + scrollAmount);

}

function mouseDownListener() {
  var evt = window.event;
  var type = "down"
  game.handleMouse(evt, type);
}

function mouseUpListener() {
  var evt = window.event;
  var type = "up"
  game.handleMouse(evt, type);
}

function mouseMoveListener() {
  var evt = window.event;
  var type = "move"
  game.handleMouse(evt, type);
}

//------------------

window.addEventListener('load', function(event) {
  startgame();
});

var dog = new Image();
dog.src = "https://i.stack.imgur.com/W0mIA.png";

function startgame() {
  game = new Game();

  for (var i = 0; i < 1; i++)
    game.objects.push(new Sprite(dog, 250, 250));


  setInterval(function() {
    game.gameLoop();
  }, setIntervalAmount);


  document.getElementById("mycanvas").addEventListener("wheel", mouseWheelListener);
  document.getElementById("mycanvas").addEventListener("mousedown", mouseDownListener);
  document.getElementById("mycanvas").addEventListener("mouseup", mouseUpListener);
  document.getElementById("mycanvas").addEventListener("mousemove", mouseMoveListener);
}