How can multiple Imported classes call each others methods?

Viewed 151

I have this index.js file:

import GameController from './Controllers/GameController'
import LandController from './Controllers/LandController'

const GAME = new GameController();
const LAND = new LandController();

And in this class, I want to call a function every second using setInterval:

export default class GameController {
  tickCount = 0;

  tick = setInterval( () => {
    this.Tick()
  }, 1000);

  Tick () {
    LAND.updateLand();
    this.tickCount++;
  }

}

But I get this error:

Uncaught ReferenceError: GAME is not defined

I'm new to webpack, I had this working in vanilla javascript but I'm still learning, so any help would be greatly appreciated! Thank you

EDIT: Updated GameController class based on bravo's answer, however now I get the error:

Uncaught ReferenceError: LAND is not defined

How can I make it so multiple classes can call each others methods?

3 Answers

You started using modules and classes, so you are moving to OOP (Object Oriented Programming). This means that you should forget about scripting, where you declare globals, everything manipulates everything, and start reasoning in a structured, hierarchical, and isolated fashion (where you have objects responsible for specific tasks).

You started properly by understanding what isolation is. You created an autonomous class to control the land. Then you need a game controller, which controls the land (and anything else, but we will come back to this lather).

Follow the reasoning; Game controls Land, in OOP this would translate in either Game receives Land or Game creates Land. Which of two to choose depends on the specific use case.

Game creates Land
// ./Controllers/GameController.js
import LandController from './Controllers/LandController'

export default class GameController {
  tickCount = 0;
  land = new LandController();

  intercal = setInterval(this.onTick.bind(this), 1000);

  onTick () {
    this.land.updateLand();
    this.tickCount++;
  }

}
// index.js
import GameController from './Controllers/GameController'

const GAME = new GameController();

Here you can see how Game creates Land, this is useful but reduces your access to the Land constructor (assuming you do not want to change Game to configure land)

Game receives Land
// ./Controllers/GameController.js

export default class GameController {
  tickCount = 0;
  land;

  constructor(land) {
    this.land = land;
  }

  intercal = setInterval(this.onTick.bind(this), 1000);

  onTick () {
    this.land.updateLand();
    this.tickCount++;
  }

}
// index.js
import GameController from './Controllers/GameController'
import LandController from './Controllers/LandController'

const LAND = new LandController();
const GAME = new GameController(LAND);

Here you have access to both constructors and your code is more maintainable. Game is not concerned about Land (what is, what it has in or where it came from) as long as it has a method updateLand() that has to be called at an interval.

More than land

The second solution allows you to implement a more versatile Game. Assume your Land has a method called update() rather than updateLand().

// ./Controllers/GameController.js

export default class GameController {
  tickCount = 0;
  children;

  constructor(children) {
    this.children = children;
  }

  intercal = setInterval(this.onTick.bind(this), 1000);

  onTick () {
    this.children.forEach(c => c.update());
    this.tickCount++;
  }

}

And then you can go beyond Land, maybe have a Character or more Land, or anything else.

// index.js
import GameController from './Controllers/GameController'
import CharController from './Controllers/CharController'
import LandController from './Controllers/LandController'

const LAND1 = new LandController();
const LAND2 = new LandController();
const CHAR = new CharController();
const GAME = new GameController([ LAND1, LAND2, CHAR ]);

This is how you can start reasoning to structure your OOP software properly; objects that are concerned about tasks and other objects.

  • When an object is not concerned about another object it should not have access to it (so no globals)
  • When an object is responsible for another object it should have direct access (receive them from the constructor or a setter) or directly instantiate them (create them).

If you really want to make the instances global, which I would not recommend because there are normally are better methods then using global variables, you should setup the variables in the window object

window.yourNamespace = {};
window.yourNamespace.game = new GameController();
window.yourNamespace.land = new LandController();

Then you can access both objects everywhere you like with

window.yourNamespace.game
window.yourNamespace.land

Answering your question about a non global solution:

I can only guess what your application will do but it sounds like a game. So GameController should be the main class. From this class you should instantiate all other classes like the LandController(). In that case you can access the LandController of course from the GameController. If you need an object in a class you should give the object as an parameter. E.g.

Class GameController()
{
    landController = new LandController();

    … here all needed methods for the class …

    // Example if you need LandController in an other Class
    exampleMethod()
    {
        Let otherClass = new OtherClass(this.landController);
    }
}

You can do it this way:

We may use a constructor for that.

export default class GameController {

  tickCount = 0;
  tick = null;

  constructor() {
        this.tick = setInterval(() => {
        this.Tick()
  }, 1000);

  }

  Tick () {
    LAND.updateLand();
    this.tickCount++;
  }

}
Related