Javascript - Storing Class methods in mulitple files? - javascript

Is is possible to have one class and distribute it's methods over multiple files?
My class is now in one file and it's getting to be a long file, that is becoming hard to navigate developing.
I thought of extending a class, but that is building on top of a class. And multiple extended classes don't communicate amongst each other, right?
So the main file would have the constructor and the other files would have one or just a few methods of its class.

You can assign to properties of the prototype, just like in the old days when functions were used instead of classes - import the functions from the other files, and after the class is defined, assign the properties:
// main.js
import method1 from './method1';
import method2 from './method2';
// or use require() syntax, depending on how your environment is set up
class Foo {
constructor(name) {
this.name = name;
// ...
}
}
Foo.prototype.method1 = method1;
Foo.prototype.method2 = method2;
// method1.js
export default function() {
console.log('method1 running for ' + this.name);
};
Both method1 and method2 will still be able to reference this to refer to the instance, just like they were defined as a method directly inside Foo.
You can use import syntax with ES6 modules (eg <script type="module") or in a module bundler system like Webpack. In node, import using require(path) and export by assigning to module.exports or properties of module.exports.

If a single class is getting so big that you're worried the file containing it is getting too big, I'd say the best thing you can do is refactor the class into smaller classes that are more focussed on solving a single problem.
If you can't do that, then you can have the main class's file create the class:
class Example {
}
...and then have multiple files that add methods to its prototype:
Example.prototype.doThis = function doThis() {
// ...
};
Example.prototype.doThat = function doThat() {
// ...
};
You lose the ability to use super in those methods, but other than that it works.
I thought of extending a class, but that is building on top of a class. And multiple extended classes don't communicate amongst each other, right?
I'm not sure what you mean about not being able to communicate between superclasses and subclasses. The instance data is common to all, and they can use each other's methods.

Related

Javascript/Express - Exports all methods vs exporting class containing methods

I am confused while encountering this over-looked question like how they are different ?
Module.exports = {
search_companies(req, res) {
//some ops
},
get_details(req, res) {
//some ops
}
};
vs
class MainContrller {
search_companies(req, res) {
//some ops
},
get_details(req, res) {
//some ops
}
}
module.exports.MainController = MainController;
The first one exports an object with the function search_companies and get_details. So you can call these to function on the object that is exported.
The second one exports a class MainController with the functions search_companies and get_details. Here you have to create an instance of MainController to be able to call those two functions on the instance.
You use the first syntax if you only need one instance of that object through the whole project. It's like a singleton or like static, but without the need to define an actual class for it.
And you use the second one if you need multiple different instances of MainController.
A module is supposed to be used like:
const { search_companies } = require('...');
A class is supposed to be used like:
const { MainController } = require('...');
const { search_companies } = new MainController();
MainController in this case is a bad practice because it mimics the usage of classes in other languages without taking the specifics of JavaScript into account.
MainController doesn't benefit from being a class if this instance is ignored and doesn't lose in functionality when a class is refactored to separate functions.
Classes aren't glorified namespaces in JavaScript; there are modules that already serve this purpose. If there's a need for a namespace and no need for class instance, a module can be used as a rule of thumb.

Allowing users to modify imported ES6 module functions in context

I'm getting rather confused as to if something is possible or not.
I create a module that contains the following:
export function logText(){
console.log('some text');
}
export class Example {
constructor(){
logText();
}
}
The intent is for the user to call new Example to start off the module logic.
import { logText, Example } from 'example';
// Do some magic here to modify the functionality of logText
new Example();
Is it possible for the end user to modify logText?
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
I frequently see repos with lots of functions exported that are useless without the users having to remake almost all the functionality manually, making it rather pointless to do. One good example is this repo whre theuy even call the exported functions their 'API'. In that example these are rather pointless exports and at worse would just cause issues if someone tried to use them in conjunction with the main function. But if you could modify them and have them still run then it would make sense to me.
Given this:
import { logText, Example } from 'example';
Is it possible for the end user to modify logText?
Since you aren't being very specific about what you mean by "modify logText", I'll go through several options:
Can you reassign some other function to the variable logText?
No. You cannot do that. When you use import, it creates a variable that is const and cannot be assigned to. Even if it wasn't const, it's just a local symbol that wouldn't affect the other module's use of its logText() anyway. The import mechanism is designed this way on purpose. A loader of your module is not supposed to be able to replace internal implementation pieces of the module that weren't specifically designed to be replaced.
Can you modify the code inside of the logText function from outside of the module that contains it?
No, you cannot. The code within modules lives inside it's own function scope which gives it privacy. You cannot modify code within that module from outside the module.
Can you replace the logText() function inside the module such that the implementation of Example inside that class will use your logText() function?
No, you cannot do that from outside the module. You would have to actually modify the module's code itself or someone would have to design the Example interface to have a replaceable or modifiable logText() function that the Example object used.
For example, logText() could be made a method on Example and then you could override it with your own implementation which would cause Example's implementation to use your override.
Code in the module that you do not modify:
export class Example {
constructor(){
this.logText();
}
logText() {
console.log('some text');
}
}
Code doing the import:
import { Example } from 'example';
class MyExample extends Example {
constructor() {
super();
}
logText() {
console.log("my own text");
}
}
let o = new MyExample();
Can you create your own version of logText and use it locally?
Sure, you can do that.
function myLogText() {
do your own thing
}
And, you could even NOT import logText so that you could use the symbol name logText() locally if you wanted. But, that won't affect what Example does at all.
Are there ways to design the example module so that that logText() can be easily replaced.
Yes, there are lots of ways to do that. I showed one above that makes logText a method that can be overriden. It could also be passed as an optional argument to the Example constructor.
There could even be an exported object that allowed the caller to replace properties on that object. For example:
export const api = {
logText: function logText(){
console.log('some text');
}
};
export class Example {
constructor(){
api.logText();
}
}
Then, use it like this:
import { api, Example } from 'example';
api.logText = function() {
console.log('my Text');
};
I would generally not recommend this because it sets you up for usage conflicts between multiple users of the same module where each one tries to modify it globally in ways that conflict with each other. The subclassing model (mentioned above) lets each use of the module customize in its own way without conflicting with other usages of the module.
Is it possible for the end user to modify logText?
No, that's not possible, import bindings are immutable, and function objects are basically immutable wrt the code they contain.
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
Why not make the log function an optional argument in the constructor? Usually when something is variable it becomes a parameter.
export class Example {
constructor(log=logText){
log();
}
}

Any striking difference between class/instance methods vs static methods for use in applications?

I am building APIs for an application. I want to know if there is any difference between writing the functionality methods as this:
class Foo {
static method1(req, res) {}
static method2(req, res) {}
}
and
class Foo {
method1(req, res) {}
method2(req, res) {}
}
I know static methods are made directly on the class and are not callable on instances of the class and they are often used to create utility functions but I just want to know if there is a disadvantage or any effect if static is not added while creating the functionalities for the application.
If static is not added, then the method can only be called on an instance of the object.
If static is added, then the method can only be called with the class name prefix, not an instance of the object.
If you have a method that could be static (does not reference any instance data or use this to refer to an object instance), then you can make it either static or not static. If you make it non-static, it will still work just fine, but it will only be callable on an instance of the object itself or with a direct reference to Foo.prototype.method().
So, the disadvantage of not making a static method actually be declared static is that it's not as clean to use it when you don't have an instance of the object around. That's what static methods were invented for - to make it clean to declare and use functions namespaced to your class that don't require an instance.

Define static class properties in JavaScript

What is the best practice way to define static class private properties in JavaScript ES6 in Node.js?
I have the following Log class:
'use strict';
const moment = require('moment');
const LOG_DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
module.exports = class {
static debug(txt) {
console.log(`${moment().utc().format(LOG_DATE_FORMAT)} [debug] ${txt}`);
}
static info(txt) {
console.info(`${moment().utc().format(LOG_DATE_FORMAT)} [info] ${txt}`);
}
static warn(txt) {
console.warn(`${moment().utc().format(LOG_DATE_FORMAT)} [warn] ${txt}`);
}
static error(txt) {
console.error(`${moment().utc().format(LOG_DATE_FORMAT)} [error] ${txt}`);
}
};
Is defining moment and DATE_FORMAT outside of the module.exports and thus class definition the correct way of doing this?
Yes, it's an acceptable way of doing things. Since you want to define some variables that you share among all your static methods, you have basically four choices:
Define them as statics on the class also so you can reference them using the class name as a prefix. This has the benefit or drawback (depending upon what you want) that it makes them publicly accessible on your exported class.
Define them as module level variables as you've done. This makes them accessible by all the static methods, but keeps their use private to your module.
Redefine them separately inside of each method.
Define them as globals.
Option #3 seems like a lot of repetition and certainly wouldn't be very DRY.
Option #4 does not seem like a good way to go since there's no reason to make these globals (you pretty much never want to use globals with node.js anyway).
So, it comes down to option #1 or #2 and which to choose totally depends upon whether you want their values to be accessible outside the module or not. If you want them exported also, then make them static properties on your class and refer to them with the class prefix. If you only want to use them inside the module, then what you have done is perfect.

Typescript - implementing an interface without implementing the interfaces parents

I currently have the following interfaces defined in a project
interface foo {
fooProperty: number;
fooFunction(): void;
}
interface bar extends foo {
barProperty: string;
barFunction(): void;
}
Now I want to define a class like
class myBar implements bar {
public barProperty: string ="bar";
public barFunction() {
// do dosmething
}
}
However I don't want to have to implement foo's functions and properties too, as these are already defined in an existing JS class which the interfaces define.
Essentially what I'm trying to do is create a typescript class that is extended from a 3rd party JS library for which I have "d.ts" files that describe the implementation, but which is NOT in typescript in it's source form.
In order to use a particular function in the 3rd party library, I have to create a custom class that derives from a class they provide in JS, but which I then override using "prototype", however I need to do this using typescript.
Update 1
It seems that folks reading this are not quite getting what I'm trying to achieve here, so let me elaborate a bit more.
In the project I'm working on we use a 3rd party JS lib, lets call this "thirdLib.js"
Within "thirdLib.js" there is functionality, that in order to use it, requires me to extend a JS style class provided as part of "thirdLib.js" like so:
function myClass(){
thirdlib.thirdclass.call(this);
}
thirdLib.utilities.extendclass(myClass, thirdLib.thirdClass);
myClass.prototype.overriddenFunc = function(){
// Do custom function here
}
The "extendClass" method in "thirdlib" copys the constructor of the class I'm deriving from into my constructor or so I'm lead to believe.
Which is then used later on, elsewhere in "thirdlib.js" EG:
var myStuff = new thirdLib();
var theClass = new myClass();
myStuff.registerSomething(theClass);
myStuff.doAThing();
At the point where I call "doAThing", thirdLib.js has the new JS class registered with it, and that class has ALL the original functionality as present in "thirdClass" but with MY customisations added to it.
All I have for "thirdLib.js" is the JavaScript code (Minified) and a set of Typescript definition files (Which I've written myself, as none where provided with the lib), and I need to be able to create "myClass()" using normal Typescript functionality, while still extending and consuming everything that's in the original JS class, and while being able to add my functionality to the TS class and have that override the functionality in the base JS class when I do.
Update April 2022
For those who are wondering, about 6 months after my last comment I moved on from the company I was doing this project for, and so I never got to see it through to a resolution, since I don't have the code or access to it anymore, I doubt very much it will ever be resolved. For those who are interested, the "Custom Drawing Handler" I was trying to implement, was a custom drawing class for a (Then Commercial, now it's OSS) 3rd party JS library called "MXGraph"
If I got your problem right you could just have your class myBar extend the class myFoo, hence inheriting the given implementation and fulfilling the criteria defined in the interface.

Categories