How create static functions/objects in javascript/nodejs (ES6) - javascript

I want to create a static class using Javascript/Node JS. I used google but i can't find any usefull example.
I want to create in Javascript ES6 something like this (C#):
public static MyStaticClass {
public static void someMethod() {
//do stuff here
}
}
For now, I have this class, but I think that this code will creates a new instance every time that it be called from "require".
function MyStaticClass() {
let someMethod = () => {
//do some stuff
}
}
var myInstance = new MyStaticClass();
module.exports = factory;

Note that JS is prototype-based programming, instead of class-based.
Instead of creating the class multiple times to access its method, you can just create a method in an object, like
var MyStaticClass = {
someMethod: function () {
console.log('Doing someMethod');
}
}
MyStaticClass.someMethod(); // Doing someMethod
Since in JS, everything is an object (except primitive types + undefined + null). Like when you create someMethod function above, you actually created a new function object that can be accessed with someMethod inside MyStaticClass object. (That's why you can access the properties of someMethod object like MyStaticClass.someMethod.prototype or MyStaticClass.someMethod.name)
However, if you find it more convenient to use class. ES6 now works with static methods.
E.g.
MyStaticClass.js
class MyStaticClass {
static someMethod () {
console.log('Doing someMethod');
}
static anotherMethod () {
console.log('Doing anotherMethod');
}
}
module.exports = MyStaticClass;
Main.js
var MyStaticClass = require("./MyStaticClass");
MyStaticClass.someMethod(); // Doing someMethod
MyStaticClass.anotherMethod(); // Doing anotherMethod

I would use an object literal:
const myObject = {
someMethod() {
// do stuff here
}
}
module.exports = myObject;

You can use the static keyword to define a method for a class
class MyStatisticsClass {
static someMethod() {
return "MyStatisticsClass static method"
}
}
console.log(MyStatisticsClass.someMethod());

I am late to the party, but it seems one aspect is missing.
NodeJs doesn't execute your module code every time you use require. It is more like a kind of stateful container, that initializes your module once and passes this instance each time you use require.
I am nodejs noobie, so don't use following without discussion with someone more mature, but I adhere for software principles, that considers using static methods evil (e.g. it is better to construct interface contracts against interfaces, not against concrete interface implementation; you just don't simply make it with static methods).
In other languages, it is usual corner stone to have some IoC container, that has all of your modules registered and solves passing of dependencies for you. Then you write everything as "Service" classes. Service class is instantiated most often only once per application life-time and every another piece of code, that requires it gets the same instance from the IoC container.
So I use something similar, without the comfort of IoC :( :
Note in this example - A's constructor is called only once, althought required 3 times.
Test.ts:
import {a} from './A';
import {b} from './B';
import {c} from './C';
console.log(c, b);
A.ts:
export class A
{
constructor(){
console.log('"A" constructor called');
}
foo() {
console.log('foo');
}
}
export const a = new A();
B.ts:
import {a, A} from './A';
export class B
{
constructor(a: A)
{
console.log('"B" constructor called, got a:', a);
a.foo();
}
}
export const b = new B(a);
C.ts:
//The same as B.ts
Result:
node test.js
"A" constructor called
"B" constructor called, got a: A {}
foo
"C" constructor called, got a: A {}
foo
C {} B {}
So as You can see - no static methods. Works with instances (althought not with interfaces in this simplified example). A's constructor called only once.

An IICE (Immediately Invoked Class Expression) :
const A = new (class a{Print(){console.log('I Am static function')}});();
A.Print();
// console.log(a);
// **Uncaught ReferenceError: a is not defined at <anonymous>:`enter code here`1:1**
// 'A' Variable is the only reference to the class a, and the only instance of it.
Or even better, a nameless class:
const A = new class {Print(){console.log('I Am static function')}};

Related

How to evaluate classes from files applied globally using Eval - node.js

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();
}
}

How to Call Global Variables From Constructors

I have a class that looks like this:
class MyClass {
constructor() {
this.field = 42;
}
method() {
return "Hello world!";
}
}
I want to define an instance of it as a global variable, so I registered it:
global.MyObject= new MyClass();
Which means this will now work:
console.log(MyObject.field); // 42
console.log(MyObject.method()); // Hello world!
However, whenever I call that same code from another class it suddenly becomes a lot more random:
class OtherClass {
constructor() {
console.log(MyObject.field); // undefined
console.log(MyObject.method()); // TypeError: MyObject.method is not a function
}
}
Sometimes this works, sometimes it doesn't. I am not able to find out when it works and when it doesn't, only that it isn't totally random. Called from the same place, the constructor either works or not. Which does not help to isolate the problem.
It seems that when the class's constructor is called from a module that does not have a direct require('my-class') it will not work - even when OtherClass has that requironment? Even when every other module has that require? Which does not make any sense.
I tried it with two modules A and B. If A defines global variables, B can access them just fine even without a require - as long as someone else has it.
So... I'm stumped at how this works. Might be a silly JavaScript gimmick, I'm not a native JavaScript developer.
My setup looks like this:
src/
ActualCode (uses functionality that will create OtherClass)
test/
mock/
MyClass
OtherClass (require('MyClass'))
ActualCodeTest (require('ActualCode'), require('OtherClass')
The constructor in OtherClass will be able to access MyClass when called from MyClass and OtherClass, but not from ActualCode or ActualCodeTest. Even if called in sequence (so the global variable definitively exists).
Which means changing parameters and adding a require is out of the question, since the ActualCode should never know test code.
The problem seems to be with the Mocha framework. The global variable cannot be accessed inside the "it":
console.log(MyObject.field); // 42
it('test', () => {
console.log(MyObject.field); // undefined
}
If I remove the surrounding code it will (obviously) work.
How do I call global variables from a class's constructor?
This sample code works as expected when you have it all in a single file:
$ cat sample.js
class MyClass {
constructor() {
this.field = 42;
}
method() {
return "Hello world!";
}
}
global.MyObject = new MyClass();
class OtherClass {
constructor(props) {
console.log(MyObject.field);
console.log(MyObject.method());
}
}
new OtherClass();
$ node sample
42
Hello world!
I suspect the confusion comes in the sequence those lines are run when you're loading the modules. Node's module-loading code will run every line in the sequence they are encountered.
The problem is that Mocha can't access global variables in its tests. I could not find the
relevant bug for this behavior, but at least a person with the same problem.
Since the global variables are only used in the tests, I get to have an easy solution:
global.MyObject= new MyClass();
module.exports.MyObject = MyObject;
And this in the test:
before(() => {
global.MyObject = require('./mock/my-class').MyObject;
});
It's probably a good idea to add a TODO so you can revisit this issue every now and then in case the Mocha crew fixes it.

ES6: Instantiate class without calling constructor

Is there any way how to instantiate new class instance without calling its constructor?
Something like this:
class Test {
constructor(foo) {
this.foo = 'test';
}
}
const a = new Test('bar'); // call constructor
const b = Test.create(); // do not call constructor
console.log(a.foo, a instanceof Test); // bar, true
console.log(b.foo, b instanceof Test); // undefined, true
I am trying to develop TS mongo ORM, and would like to use constructors of entities for creating new objects, but do not want to call them when instancing entities of already persisted objects (those that are already stored in DB).
I know that doctrine (PHP ORM) uses this approach, but afaik they are using proxy classes to achieve it. Is there any easy way to achieve this in typescript (or generally in ES6/ES7)?
I already found this question ES6: call class constructor without new keyword, that asks for the opposite, and saw one answer mentioning Proxy object. That sounds like a possible way to go, but from the docs I am not really sure if it is achievable.
You can add a static method create, that create an Object from the class prototype. Something like that should work:
class Test {
constructor(foo) {
this.foo = foo
}
static create() {
return Object.create(this.prototype)
}
}
const a = new Test('bar') // call constructor
const b = Test.create() // do not call constructor
console.log(a.foo, a instanceof Test) // bar, true
console.log(b.foo, b instanceof Test) // undefined, true

Define 'real' private methods in ES6 Module/Class in a nodejs only environment without any information leak

I know that there is no REAL private method INSIDE ES6 classes. However I was playing around a little bit and discovered something good - maybe...
As I mentioned it is not possible to not expose properties of an object. But I've tried to achieve somewhat of OOP programming as I divided my classes into seperate files and then exported those classes like:
class MyClass {
constructor() {
/**
* Initialize stuff...
*/
}
myMethod() {
/**
* Do public stuff...
*/
}
}
// expose class to environment.
export default MyClass;
So I can import the class:
import MyClass from './MyClass.js';
Of course myMethod is accessible from any other file which imported the module. Sinced I was in need of variables and functions beeing accessible only by the class I've tried this:
// private variable outside of class scope but still accessible.
let possiblePrivateVariable = 'am I a private variable?';
class MyClass {
constructor() {
/**
* Initialize stuff...
*/
}
myMethod() {
// run private method.
console.log(_possiblePrivateMethod());
// show private variable.
console.log(possiblePrivateVariable);
}
}
// private function outside of class scope but still accessible.
function _possiblePrivateMethod() {
return 'am I a private method?';
}
// expose class to environment.
export default MyClass;
Now you can't access the private variable and private method:
// Import class to newFile.js and use it.
import MyClass from './MyClass.js';
const exposedClass = new MyClass();
exposedClass.possiblePrivateVariable; // -> undefined.
exposedClass._possiblePrivateMethod(); // -> undefined.
exposedClass. myMethod(); // -> logs: am I a private method?
// am I a private variable?
It is obvious that those are feeling like beeing 'private' because I am not exposing them with the word export. My question is if this method can be considered creating private variables and methods? And if the Method I've shown has any other possible leakage while running the code?
Regards,
Megajin
Sidenote: I do not need browser support since it is a NodeJS only environment. I am using NodeJS v8.1.4 and use babel in my npm start script so I can use import without any TypeError's.
I should mention as well that I'm aware that this question could be seen as a duplicate but it is not because this question is not about private properties, variables and methods inside a class but outside it.
My question is if this method can be considered creating private
variables and methods?
Yes it's an actual working solution to address the fact that ES6/7/8 do not handle privacy in classes.
And if the Method I've shown has any other possible leakage while
running the code?
Fast answer : No leak
Detailed answer:
In your private function system, what makes the function private is that in javascript the scope of a function is defined by where it's defined. In your case the file.
If you do not export the function outside the file, there is no way to access it. Like you cannot access the function in the following mainstream example :
function toto() {
const tmpToto = () => console.log('Hello');
// I can access tmpToto from here
tmpToto();
return false;
}
// I cannot access tmpToto from here
Here you get a nice explanation about scopes
More infos according to comment
How would you solve the problem that multiple class instances will
share the 'out of scope' variables?
You can use IIFE(Immediately-invoked function expression) as described by #Son JoungHo in this post.
let Name = (function() {
const _privateHello = function() {}
class Name {
constructor() {}
publicMethod() {
_privateHello();
}
}
return Name;
})();

In Typescript, can a method exist only on a subclass?

I have an inheritance hierarchy in a typescript application that resembles the following:
class A {
someProp: any;
constructor(someObj: any){
}
}
class B extends class A {
constructor(someObj: any){
super(someObj);
}
public doStuff(){
console.log("doing stuff!");
}
}
In a second file, I attempt to call methods on the subclass after instantiating it like so:
var instanceB: A;
...
instanceB = new B(someObj);
instanceB.doStuff(); // produces error symbol cannot be resolved, it is probably located in an inaccessible module
So what am I doing wrong? As far as I understand prototypal inheritance in JavaScript, the method will be searched for in the hierarchy of regardless of where it is defined.
As a workaround, I've added an abstract method in the base class, and then I provide the implementation in the subclass. The problem with this is that I need to be able to swap one subclass for another depending on the application state. And to me, it seems unnecessary to define a method on the parent class that all subclasses need not implement.
This doesn't work because instanceB is declared to be of type A and not the subtype B.
You can execute methods that belong to B, if instanceB is indeed an instance of B, by using a type guard:
var instanceB: A;
...
instanceB = new B(someObj);
if (instanceB instanceof B) {
instanceB.doStuff(); // no more error
}
Or by asserting instanceB to be of type B:
// no more error, but will throw an error when instanceB is not B
(instanceB as B).doStuff();

Categories

Resources