I would like to have a class with some constants, so I can make calls such as:
const MyClass = require('./myclass.js');
console.log(MyClass.CONST1);
console.log(MyClass.CONST2);
let a = new MyClass();
...
My problem: I have to define a large amount of constants, so I would like to have a distinct block of code where the constants are defined, apart from my class definition:
a stand-alone object which defines the constants
the class definition
... And afterwards, merge them together into a single class definition so the caller use the constants as if they were part of the class definition.
Here is the way I found to do so, but I don't know if this is correct or if it could lead to some problems:
myclass.js
const MYCONSTANTS = {
CONST1: 1,
CONST2: 2
};
class MyClass {
constructor() {
}
}
// "merge" constants into MyClass
let exportedClass = Object.assign(MyClass, MYCONSTANTS);
// freeze exportedClass in order to forbid any change in constants values
Object.freeze(exportedClass);
// export
module.exports = exportedClass;
It seems to work, but I am not sure the way I "extend" the class is correct: I manipulate MyClass as if it was a variable (with Object.assign). Maybe there is a better/safer way to achieve this ?
Related
I'm a bit new to React and I'm currently developing a Proyect which has a service layer. To do so, I created a function component which would have the methods as variables:
const CustomComponent = () => {
method1 = () => {...},
method2 = () => {...},
method3 = () => {...}
}
export default CustomComponent;
This component would then be imported to the component that will use it.
To make my architecture as clean as possible, I wanted to make some of the methods private. However, as you may already know, that is not possible to do in the solution I proposed. Do hoy have an idea on how to achieve this, or maybe there is a convention to make a service layer I'm not aware of?
Thank you so much in advance!
The architecture which I find particularly clean and maintainable is one where you split off logic from presentation into two files like this:
Service layer (ts):
export class Service implements ServiceInterface {
constructor(private instanceVariable: string = "foo") { }
private methodOne(): string {
return this.instanceVariable
}
public methodTwo(argumentVariable: string): string {
const importantString = this.methodOne();
return importantString + argumentVariable;
}
}
interface ServiceInterface {
methodTwo(argumentVariable: string): string;
}
export default new Service();
Service layer (js):
export class Service {
instanceVariable;
constructor(contructorArgument) {
this.instanceVariable = contructorArgument;
}
methodOne() {
return this.instanceVariable
}
methodTwo(argumentVariable) {
const importantString = this.methodOne();
return importantString + argumentVariable;
}
}
export default new Service();
Presentation layer:
import Service from "./service.ts";
const FunctionalComponent = () => {
const [localState, setLocalState] = useState(localStateInit);
return (
<>
<div>{Service.methodTwo("bar")}</div>
</>
)
}
Few things happen here (mostly regarding ts implementation).
Keep component's service and presentation layers in separate files.
Use an interface to describe the service class and its methods. This will help to work with your service layer in your component as you'll get Typescript's IntelliSense.
For this example I'm exporting an instance of the service as default export from its file. This gives you a cleaner API in your component's file, where you can call methods without having to "pollute" component file with instance creation. This has at least the following two drawbacks:
you mostly lose ability to work nicely with static class members
preconfigured instance variable (initiated as a private member in constructor) means its value cannot be replaced in testing.
If any of above are a no go, then clean up the constructor, export just the class itself and instantiate it as required in component file.
I'm also exporting the class itself. This is for testing purposes. In testing you want to be able to swap out arguments passed into class' constructor and you need to have class definition to do that.
You'll notice the shorthand notation for declaring and instantiating a private class variable in the constructor: private instanceVariable: string = "foo". This is equivalent to something like this:
class Service {
private instanceVariable: string;
constructor(constructorArgument: string) {
this.instanceVariable = constructorArgument;
}
Such notation is particularly nice when used with dependency injection.
Overall, this setup will help you with unit testing logic in your service layer, as you can test it like any other class. This comes particularly handy when there's a lot of conditional rendering logic.
Let me know if this is what you've been looking for. Maybe we could tailor it better for your use case.
I have an abstract class that does new this(), however, it isn't creating an instance of itself, but it is creating an instance of the class that extended it.
This works in JavaScript when compiled and returns the proper class. However, TypeScript is complaining.
Cannot create an instance of an abstract class.
abstract class Model {
static find<T extends Model>(someVar) {
let inst = new this() as T
// Do some extra stuff with the instance
return inst
}
}
class A extends Model { }
A.find()
The first problem of your solution - it isn't safe, T type may not contain empty constructor. The second problem: even if you call find() on A class, return type will be inferred as Model if it isn't set explicitly.
Because anyway you should specify A type when you call find, it would be better to pass the type constructor to the find method, in this case both problems will be solved.
abstract class Model {
static find<T extends Model>(ModelClass: new () => T, param: string): T {
let inst = new ModelClass()
// Do some extra stuff with the instance
return inst;
}
}
class A extends Model {}
const a = Model.find(A, "param");
I'm currently using an globals object to store variables used by other objects.
globals.js
export default {
colors: {
bg: 0x000000,
front: 0xffffff
},
scene: new THREE.scene();
};
which allow me to do this in a given class:
Foo.js
import globals from './globals.js';
class Foo {
constructor() {
this.color = globals.colors.front;
// ...
globals.scene.add(this.mesh);
}
}
I'm wondering if this is a good way to work. Should I create a separate module for each object (say a colors.js file and a Scene.js) and import them wherever needed? Or is a globals object fine? I can't think of any drawbacks but it doesn't seem clean to me.
I'm learning patterns myself. This stuff does depend somewhat on your app and your particular needs.
Singleton's are often seen as an anti-pattern, but don't always have to be.
import globals from './globals.js';
class Foo {
constructor() {
this.color = globals.colors.front;
// ...
globals.scene.add(this.mesh);
}
}
const myFoo = new Foo()
globals.colors.front.set('red')
myFoo.color.set('blue') //its the same global color
How about this?
import globals from './globals.js';
class Foo {
constructor(color) {
this._color = color;
// ...
globals.scene.add(this.mesh);
}
}
const myFoo = new Foo(globals.colors.front)
globals.colors.front.set('red')
//myFoo._color.set('blue') //_ in name hints it's private and should not be used
This way your FOO only knows about a color that is passed. If it needs to manage and hold onto it's own color you would create an instance of THREE.Color on it, in the constructor. If it just needs to reference some color, than it could be hidden from the outside world, only caring about the reference passed to it (not caring what's in it).
Regarding the scene, you could create a manager of some kind and then whenever you ask for a new instance of Foo the manager would add it to the scene.
Whats the difference between functions declared with const and functions declared without let or const and a function declared otherwise in an ES6 class?
class App extends Component {
submitFood = () =>{
// some code
}
Why does the above work OK but the declaration below give an error:
class App extends Component {
const submitFood = () =>{
// some code
}
First of all: None of the examples you provided is valid ES6. The grammar rules for ES6 classes only allow methods definitions inside the class body. I.e.
class MyClass {
method1() {}
method2() {}
}
The first example however is making use of the class fields proposal. This proposal extends the existing grammar to allow the definition of properties of the form
class MyClass {
someProperty = value;
}
These assignments are not evaluated at class definition time but at instantiation time. It's syntactic sugar for assigning properties in the constructor:
class MyClass {
constructor() {
this.someProperty = value;
}
}
Your second example is simply invalid syntax because there are no grammar rules that allow putting let or const before the class field.
Keep in mind that class fields are not variable declarations or variable assignments.
const submitFood = () =>{
// some code
}
Is the creation of function (submitFood=() {}) and the creation of a variable so usual rules in variable hosting blocked let and const.
so fail since submitFood() is not defined. (It will throw a ReferenceError).
I created my own es6 mixin (and it works, yea!). However, it seems that I do not fully understand what I have created here (following example here):
export var EventEmitterMixin = (daSuperClass) => class extends daSuperClass {
}
How do I read this line of code? It seems that daSuperClass is just cruft (as I can evidently change it to anything I like)? Why is it in there two times?
When defining a function you need to give a name to your parameters so that you can reference them. It may be easier to see what's going on if it is rewritten without the fat-arrow syntax:
export var EventEmitterMixin = function(daSuperClass) {
return class extends daSuperClass {
[...]
}
}
So the argument your mixin takes is going to form the prototype for the new class you are creating. You mix in your extra functionality by 'extending' from the base class you provide.