Javascript: How to require using const - javascript

There's one thing I don't understand about modern Javascript. I see a lot of people discussing whether they should use var, const, or let when requiring new modules. Most people say it's const as their first priority and let second, but I don't see many people who are fan of var. However, this code down below would throw a error TS2451: Cannot redeclare block-scoped variable 'other' error. (Note: This error comes from the Typescript compiler, using the commonjs flag.)
main.js
'use strict';
const A = require('./A.js');
const B = require('./B.js');
// do more stuff
A.js
'use strict';
const other = require('./other.js');
class A {
//...
};
module.exports = A;
B.js
'use strict';
const other = require('./other.js');
class B {
//...
};
module.exports = B;
I'm not sure in which cases it's error-less to use const. It seems that it only works when a module is imported in the main module using const, and then everything else in other modules have var for importing the same module. I'd like to know if I'm missing something. Thanks.
EDIT:
This is the code of one of my modules. When I change the vars at the top to const, the error begins. I've also defined the same imports in other modules that are interconnected.
var Datastore = require('nedb');
var validate = require("validate.js");
var path = require('path');
module.exports = class Planet{
private db_filename : string = "db/planets.db";
private ds;
private static instance : Planet = null;
private constructor(){
}
init(db_filename : string) : Planet{
this.ds = new Datastore({ filename: db_filename, autoload: true, timestampData: true });
return this;
}
static get_instance() : Planet{
if(this.instance == null)
this.instance = new Planet();
return this.instance;
}
}

Generally speaking: You can redefine a variable defined with var, you cannot with const/let-defined variables. You should always use const because it throws you errors (as you see) if you accidentally redefine a variable. If you need to modify the variable later on you have to step down to let.
// works
var a = 1;
var a = 2;
// error (because var a is defined above)
let a = 1;
let b = 1;
// error (because let b is defined above)
let b = 2;
// error
const b = 1;
// error
const a = 1;
const c = 1;
// error
const c = 2;
// error
c = 2;
I do not know why your typescript-compiler throws an error. Testing this with plain node.js it works perfectly fine.

Related

How to use class in javascript

I declared a class in file A:
A = function(param1,para2){};
Then i used the class in file B:
let A = require('../filename');
let a = new A(param1,param2);
But the V8 said that,The A is not defined
How can i fix it ?
Thanks in regards.
If you're using CommonJS style modules, then you can add it to the module.exports to be able to require it in B.
A
let A = function(param1,para2){};
module.exports = A;
B
let A = require('../filename');
let a = new A(param1,param2);

Richembed not working [duplicate]

I am defining an object like this:
function Project(Attributes, ProjectWidth, ProjectHeight) {
this.ProjectHeight = ProjectHeight;
this.ProjectWidth = ProjectWidth;
this.ProjectScale = this.GetProjectScale();
this.Attributes = Attributes;
this.currentLayout = '';
this.CreateLayoutArray = function()
{....}
}
I then try to create an instance like this:
var newProj = new Project(a,b,c);
but this exception is thrown:
Project is not a constructor
What could be wrong? I googled around a lot, but I still can't figure out what I am doing wrong.
The code as posted in the question cannot generate that error, because Project is not a user-defined function / valid constructor.
function x(a,b,c){}
new x(1,2,3); // produces no errors
You've probably done something like this:
function Project(a,b,c) {}
Project = {}; // or possibly Project = new Project
new Project(1,2,3); // -> TypeError: Project is not a constructor
Variable declarations using var are hoisted and thus always evaluated before the rest of the code. So, this can also be causing issues:
function Project(){}
function localTest() {
new Project(1,2,3); // `Project` points to the local variable,
// not the global constructor!
//...some noise, causing you to forget that the `Project` constructor was used
var Project = 1; // Evaluated first
}
An additional cause of this can be ES2015 arrow functions. They cannot be used as constructors.
const f = () => {};
new f(); // This throws "f is not a constructor"
For me it was the differences between import and require on ES6.
E.g.
processor.js
class Processor {
}
export default Processor
index.js
const Processor = require('./processor');
const processor = new Processor() //fails with the error
import Processor from './processor'
const processor = new Processor() // succeed
I've googled around also and found this solution:
You have a variable Project somewhere that is not a function. Then the new operator will complain about it. Try console.log(Project) at the place where you would have used it as a construcotr, and you will find it.
For my project, the problem turned out to be a circular reference created by the require() calls:
y.js:
var x = require("./x.js");
var y = function() { console.log("result is " + x(); }
module.exports = y;
x.js:
var y = require("./y.js");
var my_y = new y(); // <- TypeError: y is not a constructor
var x = function() { console.log("result is " + my_y; }
module.exports = x;
The reason is that when it is attempting to initialize y, it creates a temporary "y" object (not class, object!) in the dependency system that is somehow not yet a constructor. Then, when x.js is finished being defined, it can continue making y a constructor. Only, x.js has an error in it where it tries to use the non-constructor y.
I have a class in one file that I'm importing into a test file:
//Vec.js
class Vec {
}
module.exports.Vec = Vec;
Changing
//Vec.test.js
const Vec = require('./Vec');
const myVec = new Vec(); //TypeError: Vec is not a constructor
to
//Vec.test.js
const {Vec} = require('./Vec');
const myVec = new Vec(); //Succeeds!
resolved this error for me.
In my case I was using the prototype name as the object name. For e.g.
function proto1()
{}
var proto1 = new proto1();
It was a silly mistake but might be of help to someone like me ;)
Sometimes it is just how you export and import it. For this error message it could be, that the default keyword is missing.
export default SampleClass {}
Where you instantiate it:
import SampleClass from 'path/to/class';
let sampleClass = new SampleClass();
Option 2, with curly braces:
export SampleClass {}
import { SampleClass } from 'path/to/class';
let sampleClass = new SampleClass();
I just want to add that if the constructor is called from a different file, then something as simple as forgetting to export the constructor with
module.exports = NAME_OF_CONSTRUCTOR
will also cause the "Not a constructor" exception.
To add to #wprl's answer, the ES6 object method shorthand, like the arrow functions, cannot be used as a constructor either. 😅
const o = {
a: () => {},
b() {},
c: function () {}
};
const { a, b, c } = o;
new a(); // throws "a is not a constructor"
new b(); // throws "b is not a constructor"
new c(); // works
Car.js
class Car {
getName() {return 'car'};
}
export default Car;
TestFile.js
const object = require('./Car.js');
const instance = new object();
error: TypeError: instance is not a constructor
printing content of object
object = {default: Car}
append default to the require function and it will work as contructor
const object = require('object-fit-images').default;
const instance = new object();
instance.getName();
In my case this happened due to a circular reference between two classes. I imported class B in the class A file and imported class A in the class B file, so the program never reached to the point of actually define A as a class.
I just had a similar error when trying to use the BS5ModalJS in conjunction with webpack, meaning I was trying to import the js file.
Because the single .js file provided was designed to be used via the script tags, it took a while for me to realise that to avoid the "BSModal is not a constructor" error, I had to go into their code and add:
export {BSModal}
I was then able to use
import { BSModal } from './../thirdparty/BS5ModalJS/BS5Modal.js';
and do
let myModal enter code here= new BSModal(...)
without getting that error.
So if you're using webpack, perhaps make sure the classes and functions are exported (and therefore available) to the callers.
In my case I'd forgotten the open and close parantheses at the end of the definition of the function wrapping all of my code in the exported module. I.e. I had:
(function () {
'use strict';
module.exports.MyClass = class{
...
);
Instead of:
(function () {
'use strict';
module.exports.MyClass = class{
...
)();
The compiler doesn't complain, but the require statement in the importing module doesn't set the variable it's being assigned to, so it's undefined at the point you try to construct it and it will give the TypeError: MyClass is not a constructor error.
I had a similar error and my problem was that the name and case of the variable name and constructor name were identical, which doesn't work since javascript interprets the intended constructor as the newly created variable.
In other words:
function project(name){
this.name = name;
}
//elsewhere...
//this is no good! name/case are identical so javascript barfs.
let project = new project('My Project');
Simply changing case or variable name fixes the problem, though:
//with a capital 'P'
function Project(name){
this.name = name;
}
//elsewhere...
//works! class name/case is dissimilar to variable name
let project = new Project('My Project');
It is happening because you must have used another variable named "project" in your code. Something like
var project = {}
For you to make the code work, change as follows:
var project = {} into var project1 = {}
To add the solution I found to this problem when I had it, I was including a class from another file and the file I tried to instantiate it in gave the "not a constructor" error. Ultimately the issue was a couple unused requires in the other file before the class was defined. I'm not sure why they broke it, but removing them fixed it. Always be sure to check if something might be hiding in between the steps you're thinking about.
In browse (assuming your app is running), inspect, source, and make sure the javascript file for the constructor is loaded.
For me this happened because of a small typo.
I had a class export like ->
module.export = class TestClass {
constructor() {}
}
And I was trying to use this class as ->
const testClass = new TestClass();
The problem and typo was at module.export, which should be module.exports so
module.exports = class TestClass { // module.exports here instead of export
constructor() {}
}

Declaring libraries as constants

In NodeJS world we require modules using require function:
var foo = require ("foo");
In JavaScript (also in NodeJS) we have const keyword that creates a constant:
const
Creates a constant that can be global or local to the function in which it is declared. Constants follow the same scope rules as variables.
Example:
$ node
> const a = 10
undefined
> a
10
> a = 7
7
> a
10
My question is: would it be good to require libraries as constans?
Example:
const foo = require ("foo")
, http = require ("http")
;
/* do something with foo and http */
Are there any bad/good effects using const instead of var when requiring libraries?
It turns out that it became a common practice to use const over var for dependencies. At least in the Node.js source code this is happening:
http.js
So, I guess it's a good practice. I started to use it as well in my modules.
NodeJS hasn't any optimisations for library that requires as const - require is a simple non native function, which nothing is known about the type of the variable to which is assigned. There is source code of require:
Module.prototype.require = function(path) {
assert(util.isString(path), 'path must be a string');
assert(path, 'missing path');
return Module._load(path, this);
};
Module._load = function(request, parent, isMain) {
if (parent) {
debug('Module._load REQUEST ' + (request) + ' parent: ' + parent.id);
}
var filename = Module._resolveFilename(request, parent);
var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
}
if (NativeModule.exists(filename)) {
// REPL is a special case, because it needs the real require.
if (filename == 'repl') {
var replModule = new Module('repl');
replModule._compile(NativeModule.getSource('repl'), 'repl.js');
NativeModule._cache.repl = replModule;
return replModule.exports;
}
debug('load native module ' + request);
return NativeModule.require(filename);
}
var module = new Module(filename, parent);
if (isMain) {
process.mainModule = module;
module.id = '.';
}
Module._cache[filename] = module;
var hadException = true;
try {
module.load(filename);
hadException = false;
} finally {
if (hadException) {
delete Module._cache[filename];
}
}
return module.exports;
};
For more time library is an object (I think you don't have library like this module.exports = 10).
You can change all fields of object also if it is declared as const (If you want to get realy const object use Object.freeze(someObject)).
For conclusion: the effect is the same as for a common variable.
Link to V8 Variable Declaration Function used in NodeJS

Node.js double call to require()

//lib.js
var opt = 0
exports.set = function(arg) {
opt = arg
}
exports.prn = function() {
console.log(opt)
}
///prog.js
var lib = require('./lib')
var lib2 = require('./lib')
lib.set(222)
lib2.set(333)
lib.prn()
lib2.prn()
prog.js will output:
333
333
but I need it to output:
222
333
In ohter words, opt must be unique to variable lib and to variable lib2. How to achieve that?
That's because normally nodejs caches its modules which are got via require. You may use the following helper:
// RequireUncached.js
module.exports = function(module) {
delete require.cache[require.resolve(module)]
return require(module);
}
and the usage of the helper:
var requireUncached = require('RequireUncached.js');
requireUncached("./lib");
Have in mind that this approach is considered as bad practice and should not be used. I'll suggest to wrap your logic into a function, require the module and call the function. So, every time you get a new instance.
require will not load scripts multiple times, but always yield the same instance.
If you need different environments, make your module a constructor function that allows to be instantiated multiple times. Store opt on each object for that instead of in the (global) module scope.
// lib.js
module.exports = function constr() {
var opt = 0
this.set = function(arg) {
opt = arg
};
this.print = function() {
console.log(opt)
};
};
// prog.js
var lib = require('./lib'),
inst1 = new lib(),
inst2 = new lib();
/* or short:
var inst1 = new require('./lib')(),
inst2 = new require('./lib')(); */
inst1.set(222)
inst2.set(333)
inst1.print()
inst2.print()
The way the NodeJS module system works, the output is correct and your expectations contradict the design principle here.
Each module is loaded once and only once, and subsequent calls to require simply return the reference to the pre-existing module.
Maybe what you need to do is create a class you can create one or more instances of instead of using module-level globals.
Adding to Bergi's answer, You may also try it like
// prog.js
var lib = require('./lib')(),
lib2 = require('./lib')();
lib.set(222)
lib2.set(333)
lib.print()
lib2.print()
// lib.js
module.exports = function constr() {
var opt = 0
return { set : function(arg) {
opt = arg
},
print : function() {
console.log(opt)
}
}
};
Add this line as first line of your lib.js
delete require.cache[__filename]
now your module becomes in a separate namespace each time you require it.

module.exports in nodejs - fn vs object

I'm trying to understand the difference between the following two alternate representations of what I thought was the same functionality.
apiRegistry1.js
module.exports = function () {
var apiRegistry = {};
var files = fs.readdirSync('./apis');
for (var index in files) {
var fileName = files[index];
var module = fileName.split('.')[0];
apiRegistry[module] = require('../apis/' + module);
}
// console.log(apiRegistry) --> Prints {key: moduledef ..}
return apiRegistry;
};
vs
apiregistry2.js
var apiRegistry = {};
var files = fs.readdirSync('./apis');
for (var index in files) {
var fileName = files[index];
var module = fileName.split('.')[0];
apiRegistry[module] = require('../apis/' + module);
}
// console.log(apiRegistry) --> Prints {key: moduledef ..}
module.exports = apiRegistry;
server.js
var apiRegistry1 = require('./includes/apiregistry1')(); // returns {key: moduledef ..}
var apiRegistry2 = require('./includes/apiregistry2'); //returns {}
apiRegistry1 behaves as I would expect, but 2 doesn't. On some level it makes sense that 1 is a function and is evaluated when it's called, and that's why it works but I don't understand why printing the value within the module always works, but referencing outside of it doesn't. Is there something fundamental about how require works that I'm missing?
var module = fileName.split('.')[0];
is the culprit.
Because of how scoping works in JavaScript, that module variable isn't local to the for loop but instead the whole file. Thus, when you use it at the end:
module.exports = apiRegistry;
you are setting the exports property on your own module variable instead of the one node expects you to use. Changing to using another variable name solves your problem:
var apiRegistry = {};
var files = fs.readdirSync('./apis');
for (var index in files) {
var fileName = files[index];
var myModule = fileName.split('.')[0];
apiRegistry[myModule] = require('../apis/' + myModule);
}
// console.log(apiRegistry) --> Prints {key: moduledef ..}
module.exports = apiRegistry;

Categories

Resources