1

I've been writing a game engine, and I wanted to re-organize my code to make it more modular. Currently, I have a main function called Isometric that accepts the canvas to draw.

var iso = new Isometric('canvas');

So far so good. Next, I had .newMap() to create a new map.

var map = iso.newMap(10,1,10); // Creates 10x1x10 map

However, I want to change that since there might be some confusion with the .map property (since this returns an array called map).

I wanted the syntax to look like this:

iso.Map.create(10,1,10);

So I tried something like this:

function Isometric(id) {
    this.Map = function() {
        this.create = function() {
        }
    }
}

But when I went to access it, I realized that the second level of this still refers to the same first level this. So, I can't create a sub-class of the Map object.

I've looked at a few different methods, but none of them had clear examples and I couldn't get them to work.

Among them, I'm aware that you can use prototype, and Object.create() but I've not gotten much success.

How can I do this?

The other solution I have is to do something like this:

function Isometric('id') {
    this.Map = {
        'create': function() { },
        'load': function() {}
    }
}

and then access it like

iso.Map['create'];

but I don't like that at all. Any clean methods of doing it?

My main interest is an example with the third-level method ..create() inside .map. If you could provide me with documentation related to my question that I have not yet found, that would be nice. But even mozilla docs didn't seem to help.

istos
  • 2,654
  • 1
  • 17
  • 19
Josh
  • 110
  • 1
  • 8
  • 1
    I can't give you a clear answer, but I can say that prototype is what you should be looking into. – Waxi Feb 05 '15 at 20:46
  • 2
    *"I realized that the second level of 'this' still refers to the same first level this"* most certainly not. Every function has it's "own" this. Calling `iso.Map.create(10,1,10);` doesn't actually work because functions don't have a `create` method. *"and then access it like ...."* There is nothing that prevents you from using dot notation in that case: `iso.Map.create;` – Felix Kling Feb 05 '15 at 20:47
  • Thank you, I'll continue to look into .prototype. It's just the set up I'm concerned with. Do I just create all the functions as a list of functions? function Isometric() { } function Map() {} function Create() {} and then use prototype and all that to connect them somehow? Doesn't seem right. – Josh Feb 05 '15 at 20:47
  • Felix, I'm the one creating the 'create' method though. Edit: I'll try that once more but that didn't do the trick the first time I did it. – Josh Feb 05 '15 at 20:48
  • If you just set `this.Map = {...}` you can still do `iso.map.create(...)` btw. – JCOC611 Feb 05 '15 at 20:49
  • 1
    @Josh: True, but in your first attempt, `this.Map = function() { this.create = function() { } }`, `Map` is a function and functions don't have a `create` method. Just like you cannot call `Isometric.Map`, because `Map` is a property of an *instance* of `Isometric`, `create` would be a property of an instance of `iso.Map` if you called it as `new iso.Map()`. – Felix Kling Feb 05 '15 at 20:49
  • https://www.dropbox.com/s/tbx2aariwvcvfwb/Screenshot%202015-02-05%2015.54.35.png?dl=0 Here's what happens when I try to access it as mentioned. Then how do I make it work, Felix? .-. I'm confused. – Josh Feb 05 '15 at 20:55
  • You'll want to have a look at http://stackoverflow.com/q/16502467/1048572 and especially [constructor inside constructor - bad practice?](http://stackoverflow.com/q/20784304/1048572) – Bergi Feb 05 '15 at 20:58
  • @Josh: WTH?! Please link to jsfiddle itself, instead of making a screenshot and storing that at dropbox! – Bergi Feb 05 '15 at 21:00
  • @Bergi I did that just to show the error message :P Plus I haven't saved the fiddle. – Josh Feb 05 '15 at 21:04
  • @Josh: Oh, it would have been clear enough to us if you just pasted the error message in your comment :-) Regardless, Felix already told you: `var map = new iso.Map(); map.create()` – Bergi Feb 05 '15 at 21:07
  • Is something like this what you're looking for? http://jsfiddle.net/h1e819La/ – istos Feb 05 '15 at 21:23
  • @istos I haven't gotten the chance to look through it completely, but it looks exactly like what I had hoped for. Post that as an answer and I'll mark that as the accepted answer. Thank you! – Josh Feb 05 '15 at 22:24
  • Thank you @bergi for clearing up what felix meant. I can't tag felix again, but I'll say thank you to him too in here. Thanks a lot! – Josh Feb 05 '15 at 22:25

2 Answers2

1

I think the appropriate thing to do here is to namespace your Map constructor under the Isometric constructor. Here is how you could go about it.

function Isometric(id) {
  this.id = id;
}

Isometric.prototype = {
  constructor: Isometric,

  test: function() {
    return "I'm an instance of Isometric.";
  }
};

Here we do the namespacing and add a create() helper method to the Map constructor to create its instances.

Isometric.Map = function Map(x, y, z) {
  this.x = x;
  this.y = y;
  this.z = z;
}

Isometric.Map.prototype = {
  constructor: Isometric.Map,

  test: function() {
    return "I'm an instance of Isometric.Map.";
  }
};

// Helper method to create `Map` instances
Isometric.Map.create = function create(x, y, z) {
  return new Isometric.Map(x, y, z);
};

Usage:

var iso = new Isometric('id123');
var map = new Isometric.Map(0, 7, 99);
var map2 = Isometric.Map.create(1, 2, 3);

iso.test(); //=> "I'm an instance of Isometric."
map.test(); //=> "I'm an instance of Isometric.Map."
map2.test(); //=> "I'm an instance of Isometric.Map."

Namespaces

It's important to note that the namespacing we just did prevents collisions with the new Map class in ES6 (new JS version) - more about ES6 Maps here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.

With that said, it's always important to namespace your code under one main object (you could call it app) and only make that namespace available globally.

In your case you could do something like the following example:

;(function(win) {
  // Isometric
  function Isometric(id) {
    this.id = id;
  }

  Isometric.prototype = {
    constructor: Isometric,

    test: function() {
      return "I'm an instance of Isometric.";
    }
  };


  // Map
  function Map(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  Map.prototype = {
    constructor: Map,

    test: function() {
      return "I'm an instance of Isometric.Map.";
    }
  };

  // Helper method to create `Map` instances
  Map.create = function create(x, y, z) {
    return new Map(x, y, z);
  };


  // Namespace Map under Isometric
  Isometric.Map = Map;


  // Expose globally
  // --------------------
  win.app = {
    Isometric: Isometric
  };
}(this));

Example:

var nativeMap = new Map();
nativeMap.set('name', 'joe'); //=> {"name" => "joe"}

var myMap = new app.Isometric.Map(33, 7, 99);
myMap.test(); //=> "I'm an instance of Isometric.Map."

// Native map wasn't affected (good)
nativeMap.test(); //=> TypeError: undefined is not a function
istos
  • 2,654
  • 1
  • 17
  • 19
0

I will leave the other answer as accepted because it was more detailed and even told me about my code possibly becoming deprecated as is.

That being said, I want to re-iterate Felix King's solution. I will figure a way of combining both ideas (using namespaces and using Felixs) but here's what the partial solution was.

function Isometric(id) {
    this.Map = function() {
        this.create = function() {
            return 5;
        };
    };
}

That was the set up I had. 5 is just an arbitrary value.

 var iso = new Isometric(); // New instance of Isometric()
 var mapping = new iso.Map(); // Access to Iso's mapping functions
 var map = mapping.create(); // Should return a map array 

 console.log(map); // Prints 5

For anyone following along on this, if you want to see how I end up implementing it, then see my Github project: https://github.com/joshlalonde/IsometricJS

I'll play around with the code in sometime this weekend.

Again, thanks for your help everyone! Good luck to anyone trying to do something similar.

Josh
  • 110
  • 1
  • 8