Should I use ES6 Class' static methods when defining a component? - javascript

If I have a simple component such as the one below, would it better to use static methods within my class or not? I realise this could be down to opinion but what I'm interested in is if there are architectural reasons why one way might have advantages over the other.
Non static methods:
class mainNav {
toggleMainNav() {
const navBtn = document.getElementById('toggle-menu');
const navMenu = document.getElementById('main-nav');
navBtn.addEventListener('click', () => {
navMenu.classList.toggle('open');
});
}
init() {
this.toggleMainNav();
}
}
module.exports = mainNav;
To instantiate I would need to instantiate the class first like so:
const mainNav = require("../js/components/mainNav/mainNav");
//Init nav
const nav = new mainNav;
nav.init();
Alternatively with static methods I could do this:
class mainNav {
static toggleMainNav() {
const navBtn = document.getElementById('toggle-menu');
const navMenu = document.getElementById('main-nav');
navBtn.addEventListener('click', () => {
navMenu.classList.toggle('open');
});
}
static init() {
this.toggleMainNav();
}
}
module.exports = mainNav;
and instantiate:
const mainNav = require("../js/components/mainNav/mainNav");
mainNav.init();
On the face of it using static methods in this case seems easier as I don't have to instantiate an instance of the class first.
Not coming from an OOP programming background, Is there is a reason why doing this would be considered bad practise?

A class with no data is basically a collection of static methods. This is an antipattern. In this case, just exporting the function alone would suffice, like this:
function doSomething() { ... }
module.exports = doSomething
and use it like this:
const func = require('../path/module')
func()

Related

Nodejs: Don't run `new class` with export

this is my config:
const one = new one()
const two = new two()
export default {
one,
two
}
And this is my classes:
export class one {
constructor () {
console.log("one");
}
}
export class two {
constructor () {
console.log("two");
}
}
And this is my setup:
import runner from "./";
runner.one
Why after call runner.one, also runner.two running?!
I want to run only runner.one
It happens in here:
const one = new one()
const two = new two()
cause You defined in constructor() methods of both classes to do console.log
when You call new ClassNameHere() it executes constructor()
You could simply export classes instead of instantiating them:
runners.mjs
export class one {
constructor () {
console.log("one");
}
}
export class two {
constructor () {
console.log("two");
}
}
executor.mjs
import * as runners from "./runners";
const one = new runner.one();
OR name classes CapitalizedCamelCased:
runners.mjs
export class RunnerOne {
constructor () {
console.log("one");
}
}
export class RunnerTwo {
constructor () {
console.log("two");
}
}
executor.mjs
import {RunnerOne} from "./runners";
const one = new RunnerOne();
It's happing because you're instantiating both of them globally using const specifier.
When you do this, the code calls the constructor of each class as soon as it comes across instantiation (new keyword).
const one = new one()
const two = new two()
export default {
one,
two
}
If this is not what you want, and you want them to initialize separately, there could be multiple ways to achieve that.
One is to separately instantiate them, before you access them. (You could use it in a situation where you still need access to instantiated object inside the class you're instantiating them in). Something like this:
Class that contains one and two:
(assuming you define your classes in file onetwo.mjs)
import * as ot from "./onetwo.mjs";
let one;
let two;
function instantiateOne() {
one = new ot.one();
}
function instantiateTwo() {
two = new ot.two();
}
export default {
one,
two,
instantiateOne, // expose methods needed to create instances
instantiateTwo,
}
Class that uses above:
import runner from "./";
runner.instantiateOne(); // this will only create instance of 'one'
runner.one; // accessing one
Of course there are other ways, and it might need more changes to make it work in your project, but this is one of the patterns. Just be aware when using such a strategy, you either always check before using the object (one or two) that they have been instantiated, or you ensure you do it at start.
NOTE: This is just to give you an idea. This isn't a concrete implementation. If you do want to implement something like this, you'll have to handle all edge cases (example, prevent re-initialization etc.)

javascript good way to create or design Singleton class

I think in javascript it could create Singleton class like below
test.js
class Container {
constructor() {
this.map = new Map;
}
set(key, value) {
this.map.set(key, value);
}
get(key) {
return this.map.get(key);
}
}
module.exports.Container = new Container();
so that
I could use that other files like in index.js
import container from './test'
However the constructor of Container need some other parameters so that it could do better. And the Container also need to be Singleton class ,
because it is a common class . And i do not know how to create it?
constructor(servie) {
this.map = new Map;
this.servie = servie;
}
by the way the service variable is created when application starts
EDIT: I've realised my naming is slightly confusing, as I've used Container to represent the IoC container, and then realised you've used that name to represent your Singleton.
Providing you are able to instantiate your singleton at a central point of code (entry point file for example, while setting up server). You can achieve a Singleton in a number of ways. For example:
let instance = null;
class Singleton {
constructor(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
}
}
module.exports = (...args) => {
return instance || (instance = new Singleton(...args));
}
You could do this any multitude of ways e.g. with static functions instead of the anonymous function. However, with all of these I think it becomes slightly confused as to whether you're init'ing the instance or just trying to retrieve it.
IoC container patterns (often used in C# projects) are a little cleaner in my opinion. Where you might have a single container class that keeps track of your singletons and provides separate access and initiation functions.
class Container {
constructor() {
this.instances = {};
}
add(name, instance) {
this.instances[name] = instance; // depends if you want overwrite or not
}
get(name) {
if (!this.instances[name]) {
throw new Error('some appropriate error messaging for your app');
}
return this.instances[name];
}
remove(name) {
delete this.instances[name];
}
}
module.exports = new Container(); // this has to be a no-param constructor
Then in your app init code, you initialise the singletons and register them with your container:
const Container = require('<path_to_Container>');
const Singleton = require('<path_to_Singleton>');
...
Container.add('mySingleton', new Singleton(arg1, arg2));
and access it at any time with:
Container.get('mySingleton');
Couple notes:
You could use Singleton.name instead of a string name ('mySingleton' in the example)
This is just a personal preference. But I like it because it feels more declarative, you register (add) an instance to the container at app start-up and don't blur the lines between initialisation and usage. And it means that if you add more of these singletons going forward, you have a single pattern for it already in place.

unable to call static function on ES6 class

I have a file called helpers.js in the 'helpers' folder. The contents are like below:
class Helpers {
constructor(config) {
if (this._singleton) {
throw new Error('A singleton has already been created.');
}
this._singleton = this;
}
/**
* Gets the singleton object.
* #returns {Helpers}
*/
static getSingleton() {
return this._singleton;
}
}
module.exports = Helpers;
Then in /helpers/user.js I want to get the helper's singleton instance.
This is my code:
const helpers = require('../helpers').getSingleton();
or
const Helpers = require('../helpers');
const helpers = Helpers.getSingleton();
The error I keep getting is:
TypeError: require(...).getSingleton is not a function
or
TypeError: Helpers.getSingleton is not a function
If I hover over Helpers in VSCode, I get this tooltip
And, whenever I hover over getSingleton() I get this tooltip:
So the path is correct, but it still gives me the errors.
The easiest way to implement the singleton pattern in JavaScript is to just not export the class at all, e.g.
class Helpers {}
let helper;
module.exports = function() {
if (!helper) helpers = new Helpers();
return helper;
};
// loaded with
var helpers = require('../helpers')(); // note the extra () to call it
or even better, since we aren't restricted to Java-like behavior, just skip the function entirely and do
class Helpers {}
module.exports = new Helpers();
// loaded with
var helpers = require('../helpers');
but then if all your module is exporting is a single instance of a class, there's very little reason to use a class in the first place. You might as well do
exports.helperMethodOne = function(){};
exports.helperMethodTwo = function(){};
exports.helperMethodThree = function(){};
// loaded with
var helpers = require('../helpers');
or
module.exports = {
helperMethodOne() {},
helperMethodTwo() {},
helperMethodThree() {},
};
// loaded with
var helpers = require('../helpers');
Your require statement is wrong, but its hard to tell you precisely the right syntax without knowing your environment.
const config = require('/path/to/file');
Is typical. So try:
const Helpers = require('../helpers');
You wrote '../helpers.js' in your screenshot, not '../helpers'
You get the error:
TypeError: require(...).getSingleton is not a function
Because require(...) resolves to something else, like null, and null.getSingleton is not a function.
Also, you cannot reference this meaninfully inside a static context. this ought to only be used for class instances, not static members.
You can do something like this to use it as Singleton.getInstance();
class Singleton {
static instance = new Singleton();
static getInstance = () => Singleton.instance;
constructor() {
throw new Error('Use Singleton.getInstance()');
}
}
module.exports = Singleton;
or even something more sneaky and use it as new Singleton()
class Singleton {
static instance;
constructor() {
if (!Singleton.instance) {
Singleton.instance = this;
}
return Singleton.instance;
}
}
module.exports = Singleton;

Node.js: exporting class/prototype vs. instance

As I do most of my programming in Java, I find it compelling to export a class in a Node.js module instead of an object instance, e.g.:
class Connection {
constructor(db) {
this.db = db;
}
connect(connectionString) {
this.db.connect(connectionString);
}
}
exports.Connection = Connection;
Since doing this would require instantiating the class multiple times across dependent modules, I still need to export an already existing instance for use in the rest of the production code. I do it in the same module:
exports.connection = new Connection(require("mongoose"));
This allows for some testability, as the real dependency can be swapped in a test:
const Connection = require("./connection").Connection;
describe("Connection", () => {
it("connects to a db", () => {
const connection = new Connection({connect: () => {}});
// ...
});
});
This approach works, but it has a strange feel to it as I'm mixing two patterns here: exporting a prototype (for unit tests) and an instance (for the production code). Is this acceptable? Should I continue with this or change to something different? If so, what is the preferred pattern?
You're right, it's a bad coding style, but actually you can write a function which, depending on the received parameter, returns either the single instance (for the whole application), or the class itself (for testing). Something like this:
class MyClass() {}
const instance = new MyClass();
function getInstanceOrClass(isTesting) {
if(isTesting) {
return MyClass;
} else {
return instance;
}
}
exports.getInstanceOrClass = getInstanceOrClass;
// in other files
const getInstanceOrClass = require('./yourFileName');
const classSingletonInstance = getInstanceOrClass();
// in test files
const getInstanceOrClass = require('./yourFileName');
const MyClass = getInstanceOrClass(true);

javascript - Check if parent methods are used inside child methods

I'm writing some JS that extends a parent class and I wanted to know if there's a way to tell if a child class is using a parent method without having called it yet. Ideally I'd like to run a check in the constructor of the parent to see if any of the child methods are using the parent's methods in the method definition.
I've done a bit of research and have come across things like Object.getOwnPropertyNames() but I'm not sure if I'm headed in the right direction.
For instance:
class Path {
constructor (name) {
// how can I check if addRelationship have been used? If possible.
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (relationship) {
// do something
this.currentRelationship = relationship.path;
return this;
}
makePath () {
let path = [this.path];
if(this.currentRelationship) {
path.push(this.currentRelationship)
}
return path.join("/");
}
}
class OnePath extends Path {
// ...
someMethodFromThatRelationship () { }
}
class TwoPath extends Path {
// ...
}
var onePath = new OnePath('one');
var twoPath = new TwoPath('two-path');
class SomeOtherPath extends Path {
one () {
return this.addRelationship(onePath);
}
two () {
return this.addRelationship(twoPath);
}
}
The idea of the above example is I could check if addRelationship is referenced in any methods and if so, register a this.relationships.one and this.relationships.two before one() and two() are actually called. I hope I'm making sense. I'd love to know if this is even possible.
Updated
The end result of the above code would be the ability to do the following:
let someOtherPath = new SomeOtherPath('some-other-path');
// now I can call
someOtherPath.relationships.one.someMethodFromThatRelationship();
// and can also call the save method from the extended class
someOtherPath.one().makePath();
// some-other-path/one
// I can also just call
someOtherPath.makePath();
// some-other-path
Is there a way to tell if a child class is using a parent method without having called it yet?
No. Figuring out what programs do without calling them is equivalent to the unsolvable halting problem.
I think what you are actually looking for is a more declarative approach for creating the relationship and its accompanying method in one go. Don't use too much magic (which a parent constructor inspecting its child class code would certainly be) but be explicit.
class Path {
constructor (path) {
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (name, relationship) {
this.relationships[name] = relationship;
this[name] = function() {
// do something
this.currentRelationship = name;
return this.relationships[name];
}
return this;
}
makePath () {
let path = this.path;
if (this.currentRelationship) {
path += "/" + this.relationships[this.currentRelationship].makePath();
}
return path;
}
}
class SomeOtherPath extends Path {
constructor(name) {
super(name);
this.addRelationship("one", new OnePath('one'));
this.addRelationship("two", new TwoPath('two-path'));
}
}
or even
class Path {
constructor (path, relationships = {}) {
this.relationships = relationships;
this.currentRelationship = '';
this.path = path;
for (let const r in relationships)
this.addRelationship(r, relationships[r]);
}
…
}
class SomeOtherPath extends Path {
constructor(name) {
super(name, {
one: new OnePath('one'),
two: new TwoPath('two-path')
});
}
}
Maybe you don't even need these child classes any more if they don't have other methods or are only instantiated once (as singletons).
Notice that the above approach will create new methods and new subpaths on every instantiation of the constructor, if you don't want that you can of course also put the declaration on the class statically. Just make addRelationShip a static method that initialises the default relationships objects and puts the methods on the class' .prototype. The variations of the pattern are endless.
You even might want to experiment with the proposed decorators feature for classes.

Categories

Resources