module.exports in nodejs - fn vs object - javascript

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;

Related

Javascript: How to require using const

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.

How to export and import two different function objects in JavaScript?

I use Jasmine-Node to test Javascript code.
How can one export two different function objects like Confusions1 and Confusions2 so that both are available in the Jasmine-Node test file?
My attempt looks like this:
// confusions.js
var Confusions1 = function() {};
Confusions1.prototype.foo = function(num) {
// keep track of how many times `foo` is called
this.count++;
}
module.exports = Confusions1;
// second function object
var Confusions2 = function() {};
Confusions2.prototype.foo = function() {
var a = 2;
this.bar();
};
Confusions2.prototype.bar = function() {
return this.a;
}
module.exports = Confusions2;
And my Jasmine Test file:
// confusions.spec.js
var Confusion = require('./confusions.js');
describe("chapter 1, common misconception ", function() {
describe("to assume `this` refers to the function itself: ", function() {
var confusion = new Confusion();
// some test code omitted
});
describe("to assume `this` refers to the function's scope", function() {
var confusion = new Confusion();
// test code omitted
});
});
I want it so that Confusions1 and Confusions2 from confusions.js are both usable in the two nested describe blocks within confusions.spec.js
I assume that I have to somehow initialize different objects after requiring the file var Confusion = require('./confusions.js');
Something like var confusion1 = new Confusions1(); var confusion2 = new Confusions2(); But how exactly (without splitting both Objects in two separate files)?
So you want to have a module that behaves like a container of exported values, in another words you want to export two functions:
// foo.js
var Confusion1 = function() {}
var Confusion2 = function() {}
...
exports.Confusion1 = Confusion1
exports.Confusion2 = Confusion2
Wherever you need this module, you could require this stuff like:
// bar.test.js
var confusions = require('path-to-the-foo-file')
console.log(confusions.Confusion1)
console.log(confusions.Confusion2)
Also as it seems you should check how module system that you are using works in general, check this answer: module.exports vs exports in Node.js

How to make a custom CasperJS module with custom parameter?

FileUtil.js:
exports.a = function(pre) {
var module = {}
module.writeStringToFile = function writeStringToFile() {
casper.log(pre + " succ");
};
return module
}
main.js:
var casper = require('casper').create();
var FileUtil = require('FileUtil').a('xxx')
FileUtil.writeStringToFile() //xxx succ
That works, but what I want is var FileUtil = require('FileUtil')('xxx') instead of require('FileUtil').a('xxx').
I tried exports = function(pre) ..., but it doesn't works.
So, how to make a custom CasperJS module with custom parameter?
If you want var FileUtil = require('FileUtil')('xxx') to be your object then you need to use module.exports. It can export a single object, which can even be a function:
module.exports = function(pre) {
var module = {}
module.writeStringToFile = function writeStringToFile() {
casper.log(pre + " succ");
};
return module
}
Of course, it would be a better form to rename the inner module variable to something else.

NodeJS - How to assign constructor to module.exports in self-executing function?

I'm trying to assign a constructor in a self-executing function in NodeJS. I'm pretty sure it's not working because my parameter is a variable pointing to module.exports, but I'm curious if there's a way to make it work while staying as close to the self-executing format as possible.
Here's how the code is being called...
var TemplateEngine = require('./templateEngine');
templateEngine = new TemplateEngine({engine: 'swig'}); // "object is not a function"
Here's an example of code that works fine...
var assert = require('assert');
var swig = require('swig');
// Constructor
var TemplateEngine = function(args) {
assert.ok(args.engine, 'engine is required');
var templateEngine = {};
templateEngine.engine = args.engine;
templateEngine.Render = function(templateString, model) {
var result = swig.render(templateString, model);
return result;
};
return templateEngine;
};
module.exports = TemplateEngine;
and here's an example of the code style I'd like to use, but which produces a "TypeError: Object is not a function" error because I'm not actually assigning to module.exports, just a variable that copied whatever it was pointing to.
(function(templateEngine) {
var assert = require('assert');
var swig = require('swig');
templateEngine = function(args) {
assert.ok(args.engine, 'engine is required');
var templateEngine = {};
templateEngine.engine = args.engine;
templateEngine.Render = function (templateString, model) {
var result = swig.render(templateString, model);
return result;
};
return templateEngine;
};
})(module.exports);
Is there a way for me to use the above self-executing format and have my module export a Constructor?
In your second example, you are simply overwriting the templateEngine parameter, and that's not going to have any effect.
To get the same result as your first example, simply:
Pass module into your IIFE:
(function(module) {
})(module);
Assign a property to that:
(function(module) {
var assert = require('assert');
var swig = require('swig');
module.exports = function (args) {
...
};
})(module);

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.

Categories

Resources