Define static class properties in JavaScript - 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.

Related

Javascript - Storing Class methods in mulitple files?

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.

Requiring In Classes in Node

I am trying to require in some classes in Node, but I require them in through a single file which holds an object of all the individual requires. Like so -
File: /classes/controller.js
module.exports = class Controller {}
File: /classes/model.js
module.exports = class Model {}
File: /classes/classes.js
module.exports = {
Controller: require('./controller.js'),
Model: require('./model.js')
}
Then in app.js
const classes = require('./classes/classes.js');
....code.....
....more code.....
const someController = new classes['Controller'];
The thing is, this works just fine - but only for the first file that requires in the 'classes' variable. Any subsequent files that need to also require 'classes' in, ends up holding only an empty object inside of the 'classes' variable (and no, the actual variable name does not matter, as I tried using a different variable name in the second file but it still wound up storing an empty object). For instance, I often need my Controller to implement a Model class.
So once app.js calls controller.js, inside of that -
File: /classes/controller.js
const classes = require('./classes.js');
module.exports = class Controller {
constructor(var) {}
someMethod() {
....code....
const someModel = classes['Model'];
}
}
I can call a 'console.log(classes)', and it will print out '{}', but again this is only if it is done inside of 'controller.js' and only after being called from inside of 'app.js'. It will print out all of the classes as expected if the 'console.log()' is done either inside of 'app.js', or if 'controller.js' is called directly instead of through 'app.js'. Which is how I know where/how the error is occurring. The specific error being thrown is -
classes.Model is not a constructor
So that is where my question/problem comes in, because as I said I have called other objects in multiple files without ever running into this problem. So it must have something to do with how classes are implemented, correct? And although I do not understand the specifics of how it works, I roughly know that when a file is required in, in Node, after that it uses that same reference for any subsequent files - correct? So I was thinking it must somehow be the interplay of those two things, but I have no idea after that fact nor if I am even correct in assuming that is the problem.
You have a circular dependency of require() statements. This causes one of the require() to just return an empty object. You can't do that.
Inside of controller.js, you have require('./classes.js');.
Inside of classes.js, you have require('./controller.js').
So, each refers to the other which is what causes the circular dependency.
You will need to restructure the way code is laid out into files so you don't have this. Sometimes, the simplest fix is to just combine a couple functions into one file rather than having them all in separate files that are vulnerable to this issue. I don't quite understand your overall design to know what best to suggest in this case.
If nobody else besides controller.js is really going to use classes.js, then you can just move that code into controller.js (where you don't need to require in controller.js any more).

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.

Node.JS - Using prototype in a module

So I'm writing a whole bunch of vendor-specific files in node which all have a similar controller pattern, so it makes sense for me to cut them out and put into a common file.
You can see my common controller file here: https://gist.github.com/081a04073656bf28f46b
Now when I use them in my multiple modules, each consecutively loaded module is overwriting the first. This is because the file is only required once and passed dynamically through to each module on load (this allows me to add extra modules and these modules are able to add their own routes, for example). You can see an example module here: https://gist.github.com/2382bf93298e0fc58599
You can see here on line 53 I've realised that we need to create a seperate instance every time, so I've tried to create a new instance by copying the standardControllers object into a new object, then initialising the new object. This has zero impact on the code, and the code behaves in exactly the same way.
Any ideas guys? I'm in a bit of a jam with this one!
First thing I'd do is try to make things simpler and reduce coupling by invoking the single responsibility principle, et al.
http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html
Put those Schemas into their own files, eg
models/client.js
models/assistant.js
models/contact.js
I've also found that embedded docs + mongoose is generally a PITA. I'd probably promote all those to top level docs.
You don't need to enclose your object's keys in quotes.
routes = {
list: function() {} // no quotes is aok
}
Also 'list' in typical REST apps is called 'index'. Anyway.
Ok, I'd break this up differently. Since you're requiring stuff from the index.js file in the middleware, they become tightly coupled, which is bad. in fact, I think I'd rewrite this whole thing so it was tidier. Sorry.
I'd probably replace your 'middleware' file with an express-resource controller
https://github.com/visionmedia/express-resource (built by author of express). This is a good framework for restful controllers, such as what you're building. The auto-loader is really sweet.
You may also want to look at: http://mcavage.github.com/node-restify/ It's new, I haven't tried it out, but I've heard good things.
Since what you're building is basically an automated mongoose-crud system, with optional overriding, I'd create an express-resource controller as your base
/controllers/base_controller.js
and it might look like
var BaseController = function() {} // BaseController constructor
BaseController.prototype.index = function() {
// copy from your middleware
}
BaseController.prototype.show = function() {
// copy from your middleware
}
BaseController.prototype.create = function() {
// copy from your middleware
}
// etc
module.exports = BaseController
Then I'd do something like:
/controllers/some_resource_controller.js
which might look something like:
var BaseController = require('./base_controller')
var NewResourceController = function() {
// Apply BaseController constructor (i.e. call super())
BaseController.apply(this, arguments)
}
NewResourceController.prototype = new Base()
NewResourceController.prototype.create = function() {
// custom create method goes here
}
module.exports = NewResourceController
Then to use it, you can do:
var user = app.resource(myResourceName, new ResourceController());
…inside some loop which sets myResourceName to be whatever crud you're trying to set up.
Here's some links for you to read:
http://tobyho.com/2011/11/11/js-object-inheritance/
http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
Also, it sounds like you're not writing tests. Write tests.
http://www.codinghorror.com/blog/2006/07/i-pity-the-fool-who-doesnt-write-unit-tests.html

Categories