I have a bot.js class in which I made the function getUserInventory. I required bot.js in my main file app.js and created a new instance.
bot.js
class SteamBot {
constructor(logOnOptions){
[...]
this.logOn(logOnOptions);
}
logOn(logOnOptions){
[...]
}
getUserInventory(sid, gameid, contextid, onlyTradeable, callback){
[...]
}
}
module.exports = SteamBot;
app.js
const SteamBot = require('./bots/bot.js');
const bot = new SteamBot({
'logOnOptions'
});
Now I can call the function getUserInventory in my app.js file by just typing bot.getUserInventory(...).
But what if I want to call the function in another file? Do I have to just type the same code that I wrote in app.js? Or would that cause problems?
I am a beginner.
When you are calling new SteamBot(); you are creating a new instance of a SteamBot, it depends how you intend to use your SteamBot, do you need multiple steambots or just one ? If you only need one instance in your app,I would advise you to do something like that :
file /steambot.class.js
// this file contains your class
class SteamBot {
...
}
module.exports = SteamBot;
file /steambot.js
const SteamBotClass = require('./steambot.class');
let SteamBot;
module.exports = function(...args) {
SteamBot = SteamBot || new SteamBotClass(...args);
return SteamBot;
};
/app.js
const SteamBot = require('./steambot')({
'logOnOptions'
});
And if you want to use in a third file the same instance:
const SteamBot = require('./steambot')();
with this structure, in your app, you would be able to create a new class if you need it one day, with just the .class.js file, but if you need to keep one instance of SteamBot all along the app, just require steambot.js.
it gives you a singleton pattern based on file, a bit different from the answer from Grégory which I also like.
Hope this helps :)
What you are doing in app.js is instantiate a new object of the class SteamBot.
Then you are calling the method getUserInventory of that object.
Can you call the method of this object from another file?
Yes if you pass to that file the object you just created. If not you gotta instantiate a new object, which can be a mistake due to the fact that i'll be totally different from the first object you did created (an other implementation of the class that's going to have it's own attributes).
To pass through the object instantiation you have two things you can do :
Use static methods. A static method do not require an instantiation.
example:
SteamBot.getUserInventory(...)
class SteamBot {
static getUserInventory(...) {
...
}
}
Use singleton pattern, that will allow the creation of only one instance of a class.
example:
let instance = null;
class SteamBot {
constructor() {
if (instance) return instance;
instance = this;
return instance;
}
static getInstance() {
return instance || new SteamBot();
}
}
It doesn't mater how you name your bot variable. If you name it bot you will call bot.getUserInventory(...), if anotherBot then anotherBot.getUserInventory(...) if foo then foo.getUserInventory(...) and so on
Related
Parent script including childscript
const fs = require('fs');
function include(f) {
eval.apply(global,[fs.readFileSync(f).toString()])
}
include(__dirname+'/fileToRead.js');
hi();
let childVar = new child();
------------------------- childScript ------------------------------
function hi() {
console.log("hi");
}
class baseObj {
constructor(){
console.log("hello")
}
}
class child extends baseObj{
constructor(){
super();
}
}
----------------------------result-----------------------------------
// the hi function executes
hi
// the instance does not
let childVar = new child();
^
ReferenceError: child is not defined
---------------------Conclusive question---------------------
how do would I get the eval method to globalize the child class as well as the baseObj class using my include method provided.
---------------------------------disclaimer------------------
Disclaimer : I know modules exist and I think they are great , but when you are doing something so large server side its a huge pain to keep track of exports and imports.
Especially since cyclic references exist. Not to mention every single time you create a file you need to add whatever functionality to it with require statements and find the path to those files and then export whatever functionality you are creating in that file to go to other files .
I wish to avoid modules using this method for the most part and make it similar to dealing with client side import statements.
Thanks for the help and have a nice day guys ! :)
Your problem boils down to this:
eval('function foo() {}')
console.log(foo) // ok
eval('class bar {}')
console.log(bar) // nope
This is because
Bindings introduced by let, const, or class declarations are always instantiated in a new LexicalEnvironment. (https://tc39.es/ecma262/#sec-performeval, NOTE)
If you really want to replace the stock require, for whatever reason, try using the VM API (this is what require uses under the hood).
If you really-really need this, this is an option, as class (unlike var) does not create global bindings:
var baseObj = class {
constructor() {
console.log("hello")
}
}
var child = class extends baseObj {
constructor() {
super();
}
}
I have a class:
// MyClass.ts
export class MyClass {
constructor(){
// can I get the name of the module or function which called the constructor here ??
// ex: SomeModule or testFunction
}
}
and here I create a new instance:
// SomeModule.ts
const testFunction = () => {
return new MyClass();
}
I don't want to have to pass an extra parameter to the constructor to indicate who created it. I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
That's not possible. And you shouldn't need to know anyway. If it actually is important, you should be passing it as an explicit argument.
It's mostly for logging purposes.
For that use case, you might find that a stack trace is enough (see Print current stack trace in JavaScript and related topics). A better practice though is to pass a logger object that contains the relevant context information and uses it to enrich the log calls.
I have this class which we're going to call Connection, and there's a function in this class named Disconnect which returns a class named PartialConnection. There are some arguments for both classes, but that shouldn't be taken into effect because it's quite complicated for such an example.
This is the class structure I'm working with as a sketch:
const BaseConnection = require("./BaseConnection.js");
const PartialConnection = require("./PartialConnection.js");
class Connection extends BaseConnection { // BaseConnection is also a class with standard variables every type of connection should have. PartialConnection also extends from this.
constructor () {
...
}
Disconnect() {
return new PartialConnection();
}
}
Before you all ask, the path to ./PartialConnection.js is correct and it's set with module.exports = PartialConnection; with PartialConnection being the class name.
File PartialConnection.js
const Connection = require("./Connection");
const BaseCon = require("./BaseConnection");
class PartialConnection extends BaseCon {
constructor () {
...
}
}
module.exports = PartialConnection;
Although, I must say that the colouring of the module.exports = PartialConnection is off like shown here: https://imgur.com/a/pB9yLW5 - I also should say that, when executing the same function but for the PartialConnection to Connection, it just works fine. It has something to do with circular references.
When I create a new instance of Connection and run the Disconnect function, it returns the following error:
/Users/---/Desktop/Projects/QDB/lib/Connections/Connection.js:87
return new PartialConnection();
^
TypeError: PartialConnection is not a constructor
at Connection.Disconnect (/Users/---/Desktop/Projects/QDB/lib/Connections/Connection.js:87:16)
at process.<anonymous> (/Users/---/Desktop/Projects/QDB/lib/Connections/Connection.js:76:75)
at process.emit (events.js:219:5)
(I blanked out my name for privacy.)
As you can see, it seems like I can't initiate and return a new class of some sort. This used to work about a few weeks ago, but now it doesn't.
Version;
$ node -v
v13.3.0
$ npm -v
6.14.2
And for clarification - I'd like it to almost-literally terminate the current class and return a new class that doesn't allow you to make adjustments to the Connection class.
If you have any solutions or if you can help in any way, it's much appreciated!
Alright, so I have experimented a bit more and found out that, if you require the class within the function instead of at the beginning of the file, it caches instantly and you're able to call it instead of having it return an empty object.
const BaseConnection = require("./BaseConnection");
class Connection extends BaseConnection {
constructor () {
...
}
Disconnect() {
const PartialConnection = require("./PartialConnection");
return new PartialConnection();
}
}
If you have any other answers, later on, feel free to share!
I am writing an app that has features that can be turned on and off via a config.json that looks something like this:
"appFeatures": {
"header": {
"create": true,
"title": "Here Comes the Sun"
},
"imageStrip": {
"create": false,
"imageDirectory": "../data/images",
"imageDimensions": [288, 162]
},
"areaChart": {
"create": true
},
"axes": {
"create": true
}
}
For each feature there is already a corresponding class of the same name that implements the feature. I'd like to use the name of the feature to create a new instance of the class. After fetching the config.json, I have code (within a Main.js class) that looks like:
this.features = Object.entries(this.config.appFeatures)
.filter((entry) => {
return entry[1].create === true;
});
this.features.forEach((feature) => { this.createFeatureInstances(feature[0]); });
And then I try to create instances, a la this.header = new Header():
createFeatureInstances(featureName) {
const className = `${featureName.replace(featureName[0], featureName[0].toUpperCase())}`;
this[`${featureName}`] = new Function(`
return class ${className} {}
`)();
This creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for. How might I write the createFeatureInstances function so that I can create the instance of each class that corresponds to a feature?
EDIT Because new features may be added to this app in the future by others, I would like to minimize the times that I hard code which features are available to the app. With my current design, another developer can add another feature by writing a new feature class, importing that class into the Main.js class, and pop the config entries into the config .json without having to touch anything else in the code. For this reason, solutions like this one: Create an instance of a class in ES6 with a dynamic name? won't give me a solution because they rely on having a complete list of the classes that should already exist.
You need to have a name-class mapping somewhere. Factory function or somewhere else is your call. Your current solution lacks this, which is causing the problem:
...creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for
Some explanation with a simple example
// Imagine you've defined Test
class Test {
callMe() {}
}
// And your consumer wants a className Test to be created
const className = "Test";
// This is a simple map of the name to class
const nameToClass = {
"Test": Test
}
// This creates a new class called Test, not your defined class
const AttemptedDynamicTest = new Function(`
return class ${className} {}
`)();
// The next two logs just prove the previous comment
console.log(Test === AttemptedDynamicTest); // false
console.log(typeof AttemptedDynamicTest.prototype.callMe); // undefined
// What you really need is to maintain a map, and just use it to dynamically
// create a new instance every time
console.log(typeof nameToClass[className].prototype.callMe); // function
You can use a string to initialize a cooresponding (valid and existing) class using the following snippet:
var dynamicClassInstance = new this[classNameString]();
Is there a way to get the file name from a reference to a class? Please note this example is over simplified to illustrate what I'm trying to do (don't start suggesting logging libraries please!)
//Logger1.js
class Logger1 {
}
//MainProcess.js
class MainProcess {
startChildProcess(Logger) {
//This extension doesn't work, but looking to find something similar that does:
Logger.fileName = () => {
return __filename
}
let loggerFileName = Logger.fileName() //Returns "Main.js" not "Logger1.js", so no good...
childProcess.fork(processFileName, [loggerFileName] )
}
}
//In a child process spawned by main:
loggerPath = process.argv[2]
let Logger = require(loggerPath)[path.basename(loggerPath).replace(".js","")]
let logger = new Logger()
I can obviously add a property with a string value, or a method to return __filename the Logger1 class, but I'd rather avoid it. Is there a way to do this from inside the MainProcess class, keeping Logger1 and any other external code clean?
The reason I don't pass the instance of the logger, is the main process then creates child processes, and these child processes instantiate their own loggers. And there is no way to pass object references down to child processes as far as I'm aware.