How can multiple Imported classes call each others methods? - javascript

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?

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

TypeError: class is not a constructor

I have an issue where my tests are not running due to the following type error:
TypeError: _ViewStore.default is not a constructor
The app itself runs fine and I am assuming that the test cannot get the JourneyStore variable as it is used in the setCurrentPageFromJourney method of the ViewStore class.
Does anyone know what might be causing this?
The view store class:
class ViewStore {
currentPage: Stage;
previousPageHistory: Stage[];
constructor() {
makeAutoObservable(this);
this.currentPage = PDP_VIEWS.DEVICE;
this.previousPageHistory = [];
}
set setCurrentPage(page: Stage) {
this.currentPage = page;
this.previousPageHistory.push(page);
this.previousPageHistory = [...new Set(this.previousPageHistory)]; // removes duplicates
}
hasVisitedPage(page: Stage): boolean {
return this.previousPageHistory.includes(page);
}
setCurrentPageFromJourney() {
const journeyState = JourneyStore.journeyParams.state; // test passes if JourneyStore is not used (but then the app breaks)
switch (journeyState) {
case `a`:
this.setCurrentPage = PDP_VIEWS.DEVICE;
break;
case `b`:
this.setCurrentPage = PDP_VIEWS.INSURANCE;
break;
}
}
}
export default ViewStore;
In another file, the view store (and others, including the journey store) is instantiated like this:
import ViewStoreClass from './ViewStore';
const ViewStore = new ViewStoreClass();
I think the app uses Mobx which I am unfamiliar with and do not know whether that would make any difference.
The test:
describe('ViewStore', () => {
let store = new ViewStore();
test('should pass', () => {
expect(true).toBe(true)
})
});
By the way, the JourneyStore is just another class, with lots of properties and methods - I would show it but it will be a lot of code, and I think that may be all you need to know about it.
Any help would be greatly appreciated, thank you.

What is the difference between these two singleton approaches in JS?

The way to solve the singleton pattern in JS (TS actually) is looks like this (the best approach if you ask me):
export default class Singleton {
private static _instance: Selection
constructor() {
if (Selection._instance) {
return Singleton._instance
} else {
Singleton._instance = this
}
}
}
And then:
import Singleton from './Singleton.ts'
const singleton_1 = new Singleton()
const singleton_2 = new Singleton()
singleton_1 === singleton_2 // true
But in this scenario I have to create new variables every time I need that class.
I can achieve exactly the same the easier way:
class Singleton {
constructor() {
// some logic
}
}
export default new Singleton()
import Singleton from './Singleton.ts'
const wut = Singleton.field
Singleton.method('do something')
Am I getting something wrong or the first approach is a little bit excessive and complicated and the second one just do the same thing in more obvious way?
I understand that if I have static fields in my class, I couldn't use it that way, but cases when you really need static fields are rare.
It is essential in a scenario where only one instance needs to be created, for example, a database connection. It is only possible to create an instance when the connection is closed or you make sure to close the open instance before opening a new one. This pattern is also referred to as strict pattern, one drawback associated with this pattern is its daunting experience in testing because of its hidden dependencies objects which are not easily singled out for testing.
let databaseInstance = null;
// tracks the number of instances created at a certain time
let count = 0;
function init() {
console.log(`Opening database #${count + 1}`);
//now perform operation
}
function createIntance() {
if(databaseInstance == null) {
databaseInstance = init();
}
return databaseInstance;
}
function closeIntance() {
console.log('closing database');
databaseInstance = null;
}
return {
open: createIntance,
close: closeIntance
}
}
const database = DatabseConnection();
database.open(); //Open database #1
database.open(); //Open database #1
database.open(); //Open database #1
database.close(); //close database
source: https://codesource.io/javascript-design-patterns/

Is `this` necessary to access member variables of typescript classes?

In the class below, I use this 9 times to deal with member variables and functions. It's clogging up all my beautiful code! Is there anything that I can do to make it prettier? For example, is there anyway to access the context member variable without referring to this?
//controls timing and game phases.
import { Context } from "./helpers/Context"
import { Model } from "./Model"
import { View } from "./View"
export class Controller {
context : Context;
constructor(context : Context) {
this.context = context;
}
//make other objects in context, add them in.
begin = () : void => {
this.context.model = new Model(this.context);
this.context.view = new View(this.context);
this.init();
}
init = () : void => {
this.context.model.init();
this.context.view.init();
//causes an error! help.
this.context.model.preloadModels([
"/models/bleachers.obj"
], () => { this.buildWorld(); })
}
buildWorld = () : void => {
this.context.view.makeGrass();
this.context.view.makeSkybox();
this.context.view.makeBleachersOnEdgeOfField();
}
}
If you're used to languages like C or Java where you don't have to use this for class fields, it can look weird at first. I thought the same thing too. But for languages like Javascript and Python, it's normal to write this and self a lot of times. I think it's just a language-specific style thing that can look ugly to someone who is not used to seeing it, but this is the normal way and most JS programmers would not think it's ugly because they are used to it.

Passing a variable to a (globally?) exported class

I apologize if this question doesn't really make sense - I'm probably lacking a bit of an understanding on how exactly module.export works and scoping between classes, but I will try my best to explain. I currently have a discord bot with a music player that has a MusicPlayer class - this class is exported in this manner:
const ytdl = require('ytdl-core-discord');
const MusicQueue = require('./musicqueue');
const discordUtils = require ('../../../utils/discord-utils');
class MusicPlayer {
constructor() {
this.repeat_playlist = false;
this.queue = new MusicQueue(); // this is the persistent queue for the server
this.dispatcher = null;
this.volume = 1;
this.stopped = true;
this.paused = false;
this.repeat_current_song = false;
this.auto_delete = false;
this.manual_index = false;
this.autodcEnabled = false;
}
....
}
module.exports = new MusicPlayer();
I have various music-related commands that import this music player like so:
const { Command } = require('discord.js-commando');
const discordUtils = require ('../../utils/discord-utils');
let musicplayer = require(`./modules/musicplayer`);
module.exports = class TestCommand extends Command {
constructor(client) {
....
}
...
}
My question is, how would I pass a local variable to the musicplayer constructor since I'm exporting it via module.exports = new MusicPlayer();?
It's my understanding that using module.exports = new MusicPlayer(); would allow me to use the same musicplayer object throughout the entire project (which is what I want); but - and this is where I'm not sure I understand how module.exports works - there's no guarantee when (if at all) the musicplayer will be instantiated.
If you never use a music-related command the musicplayer will never be created because let musicplayer = require('./modules/musicplayer'); will never be run, right?
Either way, now I need to pass something to the musicplayer constructor from within one of the commands, how can I do so in a way that I don't have to repeatedly do something like: let musicplayer = require('./modules/musicplayer')(variable); in every file that requires the music player? Is there a way for me to declare let musicplayer = require('./modules/musicplayer')(variable); one time and have that musicplayer be used throughout the entire codebase?
Let's list out a few requirements:
You want to share a single music player among a bunch of code.
You want the music player to be created upon demand, only when someone actually needs it
You want to initialize the music player with some variable you have in your code
To make this work, you have to centralize the creation of the music player and that has to be coordinated with whoever has the initialization variable that you want to pass to the constructor. There are many ways to do that. I will illustrate two of them.
Let's assume that it's your application initialization code that has this variable that you want to use when creating the music player. So, rather than having everyone load the music player module themselves, we will have them all ask the app for the music player.
// app.js
const MusicPlayer = require(`./modules/musicplayer`); // load module to get constructor
let musicPlayerInitVariable; // your code sets this during startup
const sharedPlayer;
function getMusicPlayer() {
if (!sharedPlayer) {
sharedPlayer = new MusicPlayer(musicPlayerInitVariable);
}
return sharedPlayer;
}
// one of many possible exports from your app
module.exports = { getMusicPlayer };
Then, in any of your application files that want access to the shared music player, then just do this:
const musicPlayer = require('./app.js').getMusicPlayer();
This requires that anyone using the shared musicPlayer knows how to reference app.js.
Another way to do it is to have your app, initialize the music player module itself with some initial state that it can then use when it creates the music player upon demand.
// musicplayer.js
const ytdl = require('ytdl-core-discord');
const MusicQueue = require('./musicqueue');
const discordUtils = require ('../../../utils/discord-utils');
class MusicPlayer {
constructor() {
this.repeat_playlist = false;
this.queue = new MusicQueue(); // this is the persistent queue for the server
this.dispatcher = null;
this.volume = 1;
this.stopped = true;
this.paused = false;
this.repeat_current_song = false;
this.auto_delete = false;
this.manual_index = false;
this.autodcEnabled = false;
}
....
}
// this is the constructor variable
// You can either give it a default value so things will work if the player
// is not configured or if you don't give it a default value, then the code
// will require that configurePlayer() is called first
let musicPlayerInitVariable;
// call this before anyone calls getMusicPlayer()
function configurePlayer(data) {
// save this for later
musicPlayerInitVariable = data;
}
let sharedPlayer;
function getMusicPlayer() {
if (!sharedPlayer) {
if (!musicPlayerInitVariable) {
throw new Error('Music player must be configured with configurePlayer() first')
}
sharedPlayer = new MusicPlayer(musicPlayerInitVariable);
}
return sharedPlayer;
}
module.exports = { getMusicPlayer, configurePlayer };
Then, in your app-startup code somewhere, you do this:
require('./modules/musicplayer').configurePlayer(passMagicVariableHere);
From then on, anyone can just do:
const musicPlayer = require('./modules/musicplayer').getMusicPlayer();

Is it better to have one setter method or multiple for objects with a fixed number of fields?

I have a class with a member variable of type object. This object has a fixed number of fields. I'm trying to decide if I should use one setter function or multiple to mutate these fields.
To make the question more concrete, I've written the following class to model a simple organizational management structure in two different ways:
Multiple Setter Functions
class Management {
constructor() {
this.numberOfManagers = 100;
this.salaryDetails = {
regionalManagerSalary: 80000,
stateManagerSalary: 110000,
executiveManagerSalary: 200000
};
}
setRegionalManagerSalary(salary) {
this.salaryDetails.regionalManagerSalary = salary;
}
setStateManagerSalary(salary) {
this.salaryDetails.stateManagerSalary = salary;
}
setExecutiveManagerSalary(salary) {
this.salaryDetails.executiveManagerSalary = salary;
}
}
const management = new Management();
management.setRegionalManagerSalary(100000);
management.setStateManagerSalary(120000);
management.setExecutiveManagerSalary(210000);
One Setter Function
class Management {
constructor() {
this.numberOfManagers = 100;
this.salaryDetails = {
regionalManagerSalary: 80000,
stateManagerSalary: 110000,
executiveManagerSalary: 200000
};
}
setManagerSalary(typeOfManagerSalary, salary) {
this.salaryDetails[typeOfManagerSalary] = salary;
}
}
const management = new Management();
management.setManagerSalary('regionalManagerSalary', 100000);
management.setManagerSalary('stateManagerSalary', 120000);
management.setManagerSalary('executiveManagerSalary', 210000);
Would implementation 1. be better or would implementation 2. be better?
I will always consider the higher readability approach if it doesn't trade off a lot of writability. The second one is more generic but the first one is more clear.
So I think the first one is better as it is more clear that there are only 3 fields in salaryDetails. Just consider you are not the author and when you see the second one, you have no idea how many fields there will be in salaryDetails as someone could just call management.setManagerSalary('someNewSalary', 100000); somewhere to add some new fields.
Unless you have more than one Management objects and each of them might have some specific fields under salaryDetails, I don't think the second approach is better.
My recommendation would be to have one method say setSalaryDetails which will take salary object as input which you can set directly. Something like -
setSalaryDetails(salaryDetails){this.salaryDetails = salaryDetails; }
When you invoke this method you can appropriately create this object with those fields set that you will pass to it.
It is better to not have any setter at all. Initialize all the required properties in the constructor (let the constructor require arguments for this).
The code you posted it not OOP but procedural code under disguise. If you want to make it OOP then the code must stay where the data is, i.e. in the class.
As a first improvement step, your code should look like this:
class Management {
constructor(nb, regionalSalary, stateSalary, executiveSalary) {
this.numberOfManagers = nb;
this.salaryDetails = {
regionalManagerSalary: regionalSalary,
stateManagerSalary: stateSalary,
executiveManagerSalary: executiveSalary
};
}
}
const management = new Management(100, 100000, 120000, 210000);
Shorter, much cleaner, less error prone.
The fragment you posted is not enough to tell what you want to achieve but the new signature of the constructor tells me that you are mixing too many responsibilities into a single class.
A cleaner design is to use separate classes for each manager type:
/* abstract */ class Manager {
constructor(salary) {
this.salary = salary;
}
}
class RegionalManager extends Manager { }
class StateManager extends Manager { }
class ExecutiveManager extends Manager { }
class Management {
constructor(nb, regionalManager, stateManger, executiveManager) {
this.numberOfManagers = nb;
this.managers = {
regionalManager: regionaManager,
stateManager: stateManager,
executiveManager: executiveManager
};
}
}
const management = new Management(
100,
new RegionalManager(100000),
new StateManager(120000),
new ExecutiveManager(210000)
);
You can now implement the logic common to all manager types in class Manager, the logic that is specific to each manager type in its class and the logic that handles the managers by number, without caring about their individual characteristics, in class Management. This way the code uses less if and switch conditions, is easier to read, understand and develop further.

Categories