Best method to setup properties of function constructor where logic is involved

时间:2015-12-10 01:07:51

标签: javascript inheritance

I have been learning a lot about prototypical and classical inheritance, and I am trying to rewrite some spaghetti code I have written to use one of these approaches.

The issue I am having is I am not sure the best way to initialize the properties of the constructor function because some of them require some logic to determine the value.

My example below is an enemy constructor function which is used in the spawnEnemy function beneath it. When I rewrite this I think I can make spawnEnemy a function of the prototype and have it inherited by the objects I create.

The first value I need to identify is the spawnDir, because this is used to set the x and y coordinates of the enemy. Speed is also randomized between a set of values, and the color will be a sprite in a later rewrite which will be an object that is passed in (that is not in the scope of my question).

I have provided my existing code below, maybe there isn't a "clean" solution like I am hoping. I want to learn best practices, and I can't help feel like there is a solution to this using inheritance somewhere and running some additional methods upon instantiating a sub class of some sort. I am just having trouble conceptualizing this model.

Ideal Solution

I am not sure if I am explaining things the best way possible above, so I will try to be concise here with what I am looking for. I am hoping to use the classical model, preferably, to provide a solution using constructors and inheritance for setting up the properties of the objects instantiated from the resulting class.

Spaghetti Code (yeah I know, it is very spaghetti)

  var Enemy = function(color, spawnDir, speed, w, h) {

  // spawnDir is randomized and passed in as argument
  // objects starting position is set based on what spawn direction was set
  if (spawnDir == 0) {
    this.x = -w;
  } else if (spawnDir) {
    this.x = GAME_WIDTH + w;
  }

  this.y = (GAME_HEIGHT / 2) - (h / 2); // objects y coordinate always set to very middle of screen
  this.color = color;                   // color or sprite image of enemy
  this.spawnDir = spawnDir;             // spawn direction passed as argument
  this.speed = speed;                   // speed passed as argument, randomized
  this.width = w;                       // width passed as argument
  this.height = h;                      // height passed as argument
  this.collision = 0;                   // sets whether object has collided, 0 = default, incremenets with each collision detected
  //this.id = generateRandomId(8);        // randomized id assigned to object as identifier (NOT USED CURRENTLY)

  // called in the draw loop, renders object to canvas
  this.draw = function(ctx) {
    ctx.shadowColor = "black";
    ctx.shadowBlur = 5;
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;

    ctx.fillStyle = this.color;
    ctx.fillRect(this.x, this.y, this.width, this.height);
  };
}; 

// enemy spawn function
var spawnEnemy = function() {
  // if enemies array length is less than enemy reserve limit and spawn counter is set to 0, then success
  if (enemies.length < enemyReserve && spawnCounter == 0) {
    var t;
    var x = Math.floor(Math.random() * 99 + 1); // randomizes spawn direction by generating random number between 0-100
    if (x <= 50) {                              // if the number is less than 50 then spawn is from the left side of the screen
      t = 0;                                    // if the number is greater than 50 then spawn is from the right side of the screen
    } else if (x > 50) {
      t = 1;
    }
    var s = Math.floor(Math.random() * 3 + 1);  // randomizes speed of the enemy
    var enemy = new Enemy("purple", t, s, 40, 20); // instantiates new enemy object with some statis + dynamic arguments

    spawnCounter = spawnRate;                   // spawn counter reset back to default value set in global variables
    enemies.push(enemy);                        // adds the new object to enemies global array
  } else if (spawnCounter != 0) {
    spawnCounter--;                             // if counter is not set to 0 then lowers counter value by 1
  }
};

1 个答案:

答案 0 :(得分:1)

I prefer to use a pattern where I define the properties within the constructor, any static methods after the constructor, then member methods on the prototype after the static methods. It ends up looking something like this:

function Enemy(color, spawnDir, speed, w, h) {
  //Define instance properties
  this.spawnDir = spawnDir;
  this.speed = speed;
  this.width = w;
  this.height = h;
  //...
}; 

//Static functions
Enemy.spawn = function(){
  var color, spawnDir, speed, w, h;
  //Calculate variables as needed
  return new Enemy(color, spawnDir, speed, w, h);
};


//Member functions
Enemy.prototype.draw = function(){
    //Do instance stuff.
    //Access instance properties with 'this'
};

As far as trying to create subclass (of different enemy types for example) look at the answer to Subclassing a class with required parameters in JavaScript