How to Call Global Variables From Constructors - javascript

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.

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 check which module called the constructor of a class in Javascript at runtime?

I have a class:
// MyClass.ts
export class MyClass {
constructor(){
// can I get the name of the module or function which called the constructor here ??
// ex: SomeModule or testFunction
}
}
and here I create a new instance:
// SomeModule.ts
const testFunction = () => {
return new MyClass();
}
I don't want to have to pass an extra parameter to the constructor to indicate who created it. I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
That's not possible. And you shouldn't need to know anyway. If it actually is important, you should be passing it as an explicit argument.
It's mostly for logging purposes.
For that use case, you might find that a stack trace is enough (see Print current stack trace in JavaScript and related topics). A better practice though is to pass a logger object that contains the relevant context information and uses it to enrich the log calls.

Angular 2+ variable hoisting?

How does Angular resolve all it's variables regardless of where there placed in a component?
For example in Vanilla JS
console.log(a) // undefined
let a = 'Hello;
Angular component
ngOnInit(){
this.example()
}
example(){
console.log(this.a) // Hello
}
a = 'Hello'
I'm aware that this is bad practice and the compiler will complain about that placement of the variable but none the less I am curious how Angular achieves this, or whether it's not an Angular specific behaviour?
This is not an Angular behavior. Actually the piece of code that you provided is inside a class, and the a is not a variable, actually it's a property.
JavaScript (and Typescript) doesn't requires properties to be declared before methods (neither constructor), since it's just a declaration that will be used futurely when this class will be instantiated.
Although tslint may warn you about the placement of it after methods, it's just a coding style concern.
You may translate a class to a traditional function constructor:
class Car {
make = 'default';
drive() {
/* ... */
}
model = 'foo'
}
can be wrote as (and is converted to when using some polyfill on browsers that doesn't support ES6 Class):
var Car = function() {
this.make = 'default';
this.model = 'foo';
}
Car.prototype.drive = function() {
/* ... */
}
Note that in the second case, the properties are defined inside the constructor, so it will always run before the method be called.

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

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

JSHint with ECMAScript6: method is not defined

I'm implementing a client-side application using ECMAScript6 and use JSHint for static code analysis. I often use the following pattern in my code:
class MyClass {
constructor() {
//This is how I would like to call myMethod
myMethod();
//This is how I should call myMethod to make JSHint analysis pass
this.myMethod();
}
myMethod(){
//Implementation
}
}
My primary language is Java so I expect that simply calling myMethod() should be ok. However without adding this to method call I'm getting "'myMethod' is not defined" warning from JSHint. My questions are:
Is it correct to make calls without this in such situation? (e.g. in PHP you always need to add $this-> to non-static method call)
If that's correct to make calls without this is there any way (any .jshintrc flag) to turn off this warning in JSHint?
No, this is and never was correct in JavaScript. Methods always need to be called on a receiver explicitly to make this work, and they need to be referred to using property access notation because methods are just functions on properties in javascript. They're not available as functions in the scope of your other methods. It's the same for properties, btw.
JsHint is right here, and there's no reason to turn that warning off. Even if that may possible, executing your program in spite of that would just make it not work.
Is it correct to make calls without this in such situation? (e.g. in
PHP you always need to add $this-> to non-static method call)
No, it is not. You always have to specify the receiver of the method.
If that's correct to make calls without this is there any way (any
.jshintrc flag) to turn off this warning in JSHint?
JSHint returns "'myMethod' is not defined" warning correctly as there is not function called myMethod in the scope of the constructor.
In the code you provided the identifier myMethod isn't defined, but the inherited property myMethod of instances of MyClass is defined.
If you define myMethod as a Function under a closure which isn't available elsewhere then you can access as it in the form you desire
var MyClass = (function () {
function myMethod() {
//Implementation
}
class MyClass {
constructor() {
myMethod();
}
}
return MyClass;
}());
I don't get to write much ES6 so I'm not sure if putting the function myMethod inside MyClass's definition is a SyntaxError
Please note however that this is required to reference your specific instance of MyClass, so you'll probably need to use it somewhere if you want MyMethod to act on the instance.
function myMethod(obj) {...}
// ...
myMethod(this);
If you read the MDN's description of class
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JS classes provide a much simpler and clearer syntax to create objects and dealing with inheritance.
This is saying using class is just shorthand for the old way of doing it, not a new model, so it may be easier to think of what your current code would look like if written in ES5,
var MyClass = (function () {
function MyClass() {
this.constructor.apply(this, arguments);
}
MyClass.prototype = Object.create(null);
MyClass.prototype.constructor = function () {
myMethod(); // referenceError
this.myMethod(); // works
};
MyClass.prototype.myMethod = function () {
//Implementation
};
return MyClass;
}());

Categories

Resources