I am attempting to export a constructor function using the exports object in a CommonJS style module. For some reason, requiring the module results in an empty object being returned instead of the exported function.
For example, this module;
define(function(require, exports) {
var Example = function() {
this.example = true;
};
exports = Example;
});
Results in a Uncaught TypeError: object is not a function error when it is required in another module and instantiated.
define(function(require, exports) {
var Example = require('example');
var example = new Example();
});
However, if I modify the module to return the constructor instead of using the exports object everything works as expected.
define(function(require, exports) {
var Example = function() {
this.example = true;
};
return Example;
});
Is there anyway around this?
Just like you would do in Node.js, you have to assign to module.exports rather than exports itself. So:
define(function(require, exports, module) {
var Example = function() {
this.example = true;
};
module.exports = Example;
});
Assigning to exports cannot work because exports is a variable that is local to your function. There is no way for anything outside of the function to know that you've assigned to it. When you assign to module.exports. It is a different matter because you are modifying the object to which module refers.
The RequireJS documentation suggests doing it like you did in your last snippet: just return the value you'd assign to module.exports.
Related
what is the difference between this exporting function in typescript
export const handler = someWrapper(
eventHandler({
...someMiddlewares,
lambdaHandler
})
)
and this exporting in javascript:
module.export ={
someWrapper(
eventHandler({
...someMiddlewares,
lambdaHandler
}),
)
lambdaHandler
}
Please see Difference between “module.exports” and “exports” in the CommonJs Module System
module is a plain JavaScript object with an exports property. exports is a plain JavaScript variable that happens to be set to module.exports.
At the end of your file, node.js will basically 'return' module.exports to > the require function. A simplified way to view a JS file in Node could be this:
var module = { exports: {} };
var exports = module.exports;
// your code
return module.exports;
If you set a property on exports, like exports.a = 9;, that will set module.exports.a as well because objects are passed around as references in JavaScript, which means that if you set multiple variables to the same object, they are all the same object; so then exports and module.exports are the same object.
But if you set exports to something new, it will no longer be set to module.exports, so exports and module.exports are no longer the same object.
I made a TypeScript code which was compiled like this:
define("Global/Global", ["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Global = (function () {
function Global() {
}
Global.transition_time = 200;
return Global;
}());
exports.Global = Global;
});
Now, in a classic "script.js" I'm trying to console.log() the "transition_time" value. But it tells me "Global is not defined".
I put a breakpoint in the console in the Global's function, but it's never triggered.
EDIT :
That's the Global's TypeScript code:
export class Global {
static transition_time: number = 200;
}
It looks like you have an AMD module there, so you'll need to use an AMD module loader such as RequireJS if you aren't using one already. Then the proper syntax to access your module from script.js is like this:
require(["Global/Global"], function(Global_module) {
console.log(Global_module.Global.transition_time);
});
The require function doesn't add anything to the global namespace; instead, you have to pass a callback that receives the module you asked for and does whatever you wanted with it. Note the Global_module.Global.transition_time; Global_module is the name I gave to the variable that receives the entire module, and Global is the name of the exported class within the module.
In Eloquent Javascript (http://eloquentjavascript.net/10_modules.html) there's this explanation and implementation of require():
...problem is that it is not possible for a module to directly export
a value other than the exports object, such as a function. For
example, a module might want to export only the constructor of the
object type it defines. Right now, it cannot do that because require
always uses the exports object it creates as the exported value.
The traditional solution for this is to provide modules with another
variable, module, which is an object that has a property exports. This
property initially points at the empty object created by require but
can be overwritten with another value in order to export something
else.
function require(name) {
if (name in require.cache)
return require.cache[name];
var code = new Function("exports, module", readFile(name));
var exports = {}, module = {exports: exports};
code(exports, module);
require.cache[name] = module.exports;
return module.exports;
}
require.cache = Object.create(null);
So, can someone explain, how module.exports is a solution? Why can't i just overwrite exports?
If I have this hello.js file:
var greeting = function() {
console.log('Hello');
}
module.exports = greeting;
Then in main.js:
var temp = require('./hello.js');
temp();
When you say module.exports = greeting is that attaching the greeting function to the exports object on module. Since when I require hello.js in main.js I am able to call temp() directly. And don't have to do like temp.greeting();
Does this mean that since require returns module.exports it just returning the method on the exports object rather than returning the exports object entirely correct? I am confused on why it is returning what is on the exports object (the greeting function) and not the real exports object itself.
require(...) returns module.exports from that module. This is usually an object, but it can also be anything else (usually a function) like in your case where the module exports only a single function.
There's nothing wrong with doing this - module.exports is just a plain object (there's most likely something like module.exports = {}; somewhere in the code that runs "around" the contents of a module's js file)
Can I build a module that exports instantiated variables?
Module:
var module1 = require('module1')
var Module2 = require('module2')
module1.dosomething(variables)
exports.module1 = module1
//or
module2 = new Modle2(variables)
module2.dosomething(variables)
exports.module2 = module2
Can I require the module above in many other files and use the exports as instantiated variables or will they be re-instantiated every time I require them and not shared between the files requiring them.
Thanks!
Your example is confusing because you use module1 in multiple contexts, both as a module, a variable within another module, and an exported property of that module.
Think of modules as closures and exports as return values. Most likely you want to export a function/factory function and call that every time if you want to create new instances with the export, anything else will be shared since it just returns the object.
Module1
module.exports.var1 = function(opts) {
// do stuff
return variables;
};
module.exports.var2 = new Blah(); // single instance
Other module
var var1 = require('module1').var1({ opt1: 'foo' }); // New instance every time
var var2 = require('module1').var2; // Same var instance even if you include in another module
You can even do things like this to be really annoying. Most npm modules make you create instantiated versions to avoid this kind of silliness.
// file.js
var m1 = require('./m1');
m1.awesome = false;
console.log("I am awesome", m1.awesome);
// file2.js
var m1 = require('./m1');
console.log("I am awesome", m1.awesome);
// both.js
require('./file');
require('./file2');
// m1.js
exports.awesome = true;
now run this:
node file1.js
I am awesome false
node file2.js
I am awesome true
node both.js
I am awesome false
I am awesome false