在 Node.js 中,导出和导出是模块化的

I've found the following contract in a Node.js module:

module.exports = exports = nano = function database_module(cfg) {...}

I wonder whats the different between module.exports and exports and why both are used here.

转载于:https://stackoverflow.com/questions/7137397/module-exports-vs-exports-in-node-js

weixin_41568127
?yb? I've added my own words for clarity ([]): If you want your module to be of a specific object type [(such as a function/string/array)], use module.exports; if you want your module to be a typical module instance, [you should] use exports [, exclusively, to be concise]. See my answer for examples etc. stackoverflow.com/a/50162465/4722345
2 年多之前 回复
weixin_41568127
?yb? check out my new answer re: why use both stackoverflow.com/a/50162465/4722345
2 年多之前 回复
csdnceshi71
Memor.の module.exports is not a function... Why is that?
接近 3 年之前 回复
csdnceshi50
三生石@ Well, I have read every single reply here, and I still have no clue what the difference between the two is, when and how to use one vs. the other, or why there are two.
3 年多之前 回复
csdnceshi69
YaoRaoLov Here is my 2 minute article on this topic (written in Node v6 and ES2015): medium.com/@lazlojuly/…
大约 4 年之前 回复
csdnceshi77
狐狸.fox hacksparrow - "if you want your module to be of a specific object type, use module.exports; if you want your module to be a typical module instance, use exports." huh, what, why?.
大约 4 年之前 回复
csdnceshi75
衫裤跑路 Can't understand why provide both module.exports and exports. Why not just provide module.exports to avoid such confusion?
4 年多之前 回复
csdnceshi62
csdnceshi62 Quick Summary: both exports and module.exports point to the same object, unless you reassign one. And in the end module.exports is returned. So if you reassigned exports to a function then dont expect a function since it isn't going to be returned. However if you had assigned function like this exports.func = function... then resulting thing would have func property with function as a value. Because you added the property to the object that exports was pointing to ..
5 年多之前 回复
csdnceshi58
Didn"t forge It's all about references. Think of exports like a local variable object pointing to module.exports. If you overrite the value of exports, then you lose the reference to module.exports, and module.exports is what you expose as a public interface.
5 年多之前 回复
csdnceshi55
~Onlooker Updated 'for posterity' link: nodejs.org/docs/latest/api/modules.html#modules_module_exports
7 年多之前 回复
csdnceshi54
hurriedly% Great resource: hacksparrow.com/node-js-exports-vs-module-exports.html ^_^
接近 8 年之前 回复
csdnceshi61
derek5. For posterity: nodejs.org/docs/latest/api/modules.html#module.exports
8 年多之前 回复

20个回答

Setting module.exports allows the database_module function to be called like a function when required. Simply setting exports wouldn't allow the function to be exported because node exports the object module.exports references. The following code wouldn't allow the user to call the function.

module.js

The following won't work.

exports = nano = function database_module(cfg) {return;}

The following will work if module.exports is set.

module.exports = exports = nano = function database_module(cfg) {return;}

console

var func = require('./module.js');
// the following line will **work** with module.exports
func();

Basically node.js doesn't export the object that exports currently references, but exports the properties of what exports originally references. Although Node.js does export the object module.exports references, allowing you to call it like a function.


2nd least important reason

They set both module.exports and exports to ensure exports isn't referencing the prior exported object. By setting both you use exports as a shorthand and avoid potential bugs later on down the road.

Using exports.prop = true instead of module.exports.prop = true saves characters and avoids confusion.

csdnceshi75
衫裤跑路 I have no idea why they don't apply set Proxy for exports.
接近 3 年之前 回复
csdnceshi79
python小菜 I wouldn't be surprised if the output changes based on the system. But most likely they don't. I would just run console.log(exports) and see what the default values are for your system. This isn't legacy entirely, basically they started with exports but they realized do to efficiency reasons that if they wanted to export an entire object (for example a function) that it would be best to create a property on already existing object namely module.exports. So basically to export a fun run module.exports = function(){} otherwise you can freely attach properties to the exports
7 年多之前 回复
weixin_41568127
?yb? thanks for the valuable answer. few more queries - at the entry of server.js, what is expected to be the values of module.exports and exports? is module.exports expected to be null and exports set to an empty object? Is this legacy or there is some valid use case to point exports and module.exports to two different object ever?
7 年多之前 回复
csdnceshi79
python小菜 Yes the function will export properly provided you set module.exports
7 年多之前 回复
weixin_41568196
撒拉嘿哟木头 Hey Lime, this is a pretty old answer but I hope you can clarify something. If I were to set module.exports but not exports, would my code still work? Thanks for any help!
7 年多之前 回复
csdnceshi68
local-host - thanks - I'm glad it's largely irrelevant because if it wasn't it would mean I'd completely misunderstood everything. :-| :)
7 年多之前 回复
csdnceshi56
lrony* It just happens to be the name of the library the OP's example was taken from. In the module, it allows the author to write things like nano.version = '3.3' instead of module.exports.version = '3.3', which reads a little more clearly. (Note that nano is a local variable, declared a little before the module exports are set.)
7 年多之前 回复
csdnceshi68
local-host Thanks Lime. Just one question - what's the purpose of nano in your example?
7 年多之前 回复

Basically the answer lies in what really happens when a module is required via require statement. Assuming this is the first time the module is being required.

For example:

var x = require('file1.js');

contents of file1.js:

module.exports = '123';

When the above statement is executed, a Module object is created. Its constructor function is:

function Module(id, parent) {
    this.id = id;
    this.exports = {};
    this.parent = parent;
    if (parent && parent.children) {
        parent.children.push(this);
    }

    this.filename = null;
    this.loaded = false;
    this.children = [];
}

As you see each module object has a property with name exports. This is what is eventually returned as part of require.

Next step of require is to wrap the contents of file1.js into an anonymous function like below:

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
});

And this anonymous function is invoked the following way, module here refers to the Module Object created earlier.

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");

As we can see inside the function, exports formal argument refers to module.exports. In essence it's a convenience provided to the module programmer.

However this convenience need to be exercised with care. In any case if trying to assign a new object to exports ensure we do it this way.

exports = module.exports = {};

If we do it following way wrong way, module.exports will still be pointing to the object created as part of module instance.

exports = {};

As as result adding anything to the above exports object will have no effect to module.exports object and nothing will be exported or returned as part of require.

weixin_41568196
撒拉嘿哟木头 This should be the accepted answer!
3 年多之前 回复
csdnceshi56
lrony* I don't see any advantage to add exports = module.exports = app; at the last line of the code. It seems like the module.exports will get exported and we will never use exports, because again it's at the last line of the code. So, why don't we just simply add module.exports = app;
接近 5 年之前 回复
csdnceshi78
程序go I think this should be the best answer, it explains why func() fails in @William's answer!
大约 5 年之前 回复
csdnceshi60
℡Wang Yan Lost me here exports = module.exports = {};
5 年多之前 回复

Even though question has been answered and accepted long ago, i just want to share my 2 cents:

You can imagine that at the very beginning of your file there is something like (just for explanation):

var module = new Module(...);
var exports = module.exports;

enter image description here

So whatever you do just keep in mind that module.exports and NOT exports will be returned from your module when you're requiring that module from somewhere else.

So when you do something like:

exports.a = function() {
    console.log("a");
}
exports.b = function() {
    console.log("b");
}

You are adding 2 function 'a' and 'b' to the object on which module.exports points too, so the typeof the returning result will be an object : { a: [Function], b: [Function] }

Of course this is the same result you will get if you are using module.exports in this example instead of exports.

This is the case where you want your module.exports to behave like a container of exported values. Whereas, if you only want to export a constructor function then there is something you should know about using module.exports or exports;(Remember again that module.exports will be returned when you require something, not export).

module.exports = function Something() {
    console.log('bla bla');
}

Now typeof returning result is 'function' and you can require it and immediately invoke like:
var x = require('./file1.js')(); because you overwrite the returning result to be a function.

However, using exports you can't use something like:

exports = function Something() {
    console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function

Because with exports, the reference doesn't 'point' anymore to the object where module.exports points, so there is not a relationship between exports and module.exports anymore. In this case module.exports still points to the empty object {} which will be returned.

Accepted answer from another topic should also help: Does Javascript pass by reference?

csdnceshi78
程序go - You can omit module.exports because exports is a reserved variable that is implicitly declared by NodeJS runtime which automatically references the value of module.exports, as per docs: nodejs.org/api/modules.html#modules_exports
2 年多之前 回复
csdnceshi79
python小菜 This answer clear up things for people who didn't understand other answers. Started saying from the scratch.
接近 3 年之前 回复
csdnceshi63
elliott.david Thank you so much for this explanation. I don't even know why the answer above is accepted barely explains anything...
大约 3 年之前 回复
csdnceshi72
谁还没个明天 - Thank you for explaining what's actually going on, versus just describing the behavior (which just leads to more behavior questions). For people who know JS, informing us that the args are set up like var module = new Module(); and var exports = module.exports; completely explains what's going on.
接近 4 年之前 回复
csdnceshi66
必承其重 | 欲带皇冠 by adding properties to exports you are effectively ensuring that you are returning a "typical" module export object. In contrast, by using module.exports you can return any value you want (primitive, array, function) and not just an object (which is the format most people expect). So module.exports offers more power but can also be used to have your module export atypical values (like a primitive). In contrast exports is more limiting but safer (so long as you simply add properties to it and don't reassign it).
4 年多之前 回复
weixin_41568126
乱世@小熊 It is less cumbersome to write exports.something instead of module.exports.something
大约 5 年之前 回复
csdnceshi75
衫裤跑路 So then, what's the point of using exports? Why not just always use module.exports if it's just a variable reassignment? Seems confusing to me.
大约 5 年之前 回复
csdnceshi56
lrony* oooo finally this answer explains it. Basically export refers to an object to which you can add properties but if you reassign it to function then you are no long attach a property to that original object. Now export refer to function while module.exports is still pointing to that object and since it's what's returned. You can say export has been basically garbage collected.
5 年多之前 回复
csdnceshi59
ℙℕℤℝ the explanation is here: Does JavaScript pass by reference? exports.a = function(){}; works, exports = function(){} doesn't work
5 年多之前 回复
csdnceshi65
larry*wei Nice explanation but I still don't understand how you can completely omit module.exports from a module, for example in this npm package: github.com/tj/consolidate.js/blob/master/lib/consolidate.js
5 年多之前 回复

Here is the result of

console.log("module:");
console.log(module);

console.log("exports:");
console.log(exports);

console.log("module.exports:");
console.log(module.exports);

enter image description here

Also:

if(module.exports === exports){
    console.log("YES");
}else{
    console.log("NO");
}

//YES

Note: The CommonJS specification only allows the use of the exports variable to expose public members. Therefore, the named exports pattern is the only one that is really compatible with the CommonJS specification. The use of module.exports is an extension provided by Node.js to support a broader range of module definition patterns.

exports and module.exports are the same unless you reassign exports within your module.

The easiest way to think about it, is to think that this line is implicitly at the top of every module.

var exports = module.exports = {};

If, within your module, you reassign exports, then you reassign it within your module and it no longer equals module.exports. This is why, if you want to export a function, you must do:

module.exports = function() { ... }

If you simply assigned your function() { ... } to exports, you would be reassigning exports to no longer point to module.exports.

If you don't want to refer to your function by module.exports every time, you can do:

module.exports = exports = function() { ... }

Notice that module.exports is the left most argument.

Attaching properties to exports is not the same since you are not reassigning it. That is why this works

exports.foo = function() { ... }
csdnceshi67
bug^君 Simple and easier way to understand this feature.
2 年多之前 回复
weixin_41568196
撒拉嘿哟木头 Nice and straightforward
3 年多之前 回复
csdnceshi65
larry*wei This was the easiest to understand out of all the answers !
4 年多之前 回复

This shows how require() works in its simplest form, excerpted from Eloquent JavaScript

Problem 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.

Solution 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);
csdnceshi76
斗士狗 I had to recreate this in Node and test a few things until I got, I suck. Basically, the inner function created for the module never even returns the exports object. So the "exports" object isn't actually reassigned in the module e.g. if you try to write exports = "this is now a string" directly. The object only exists as a reference. This is behaviour I don't think I've really picked up on properly until now.
4 年多之前 回复

I just make some test, it turns out that, inside nodejs's module code, it should something like this:

var module.exports = {};
var exports = module.exports;

so:

1:

exports = function(){}; // this will not work! as it make the exports to some other pointer
module.exports = function(){}; // it works! cause finally nodejs make the module.exports to export.

2:

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!

3: but, while in this case

module.exports = function(){}; // from now on we have to using module.exports to attach more stuff to exports.
module.exports.a = 'value a'; // works
exports.b = 'value b'; // the b will nerver be seen cause of the first line of code we have do it before (or later)
csdnceshi69
YaoRaoLov Lyman, so module.exports is sort of the 'real-deal' that node goes off of but at some point you'll need to add all your exports to module.exports unless you're using a exports.namespace (case 2 above), which in that case seems to be like Node ran a extends(module.exports, exports); adding all 'namespaces' of exports to the module.exports object? In other words, if you're using exports then you probably want to be setting properties on it?
6 年多之前 回复

I went through some tests and I think this may shed some light on the subject...

app.js:

var ...
  , routes = require('./routes')
  ...;
...
console.log('@routes', routes);
...

versions of /routes/index.js:

exports = function fn(){}; // outputs "@routes {}"

exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

module.exports = function fn(){};  // outputs "@routes function fn(){}"

module.exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

I even added new files:

./routes/index.js:

module.exports = require('./not-index.js');
module.exports = require('./user.js');

./routes/not-index.js:

exports = function fn(){};

./routes/user.js:

exports = function user(){};

We get the output "@routes {}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js:

exports = function fn(){};

./routes/user.js:

exports = function user(){};

We get the output "@routes { fn: {}, user: {} }"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js:

exports.fn = function fn(){};

./routes/user.js:

exports.user = function user(){};

We get the output "@routes { user: [Function: user] }" If we change user.js to { ThisLoadedLast: [Function: ThisLoadedLast] }, we get the output "@routes { ThisLoadedLast: [Function: ThisLoadedLast] }".


But if we modify ./routes/index.js...

./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.ThisLoadedLast = require('./user.js');

./routes/not-index.js:

exports.fn = function fn(){};

./routes/user.js:

exports.ThisLoadedLast = function ThisLoadedLast(){};

... we get "@routes { fn: { fn: [Function: fn] }, ThisLoadedLast: { ThisLoadedLast: [Function: ThisLoadedLast] } }"

So I would suggest always use module.exports in your module definitions.

I don't completely understand what's going on internally with Node, but please comment if you can make more sense of this as I'm sure it helps.

-- Happy coding

weixin_41568184
叼花硬汉 I agree. It may be useful for namespacing is some circumstances, but is generally not going to make or break anything.
6 年多之前 回复
weixin_41568127
?yb? I think they are unnecessarily complicated and confusing. It should be transparent and intuitive.
6 年多之前 回复

Initially,module.exports=exports , and the require function returns the object module.exports refers to.

if we add property to the object, say exports.a=1, then module.exports and exports still refer to the same object. So if we call require and assign the module to a variable, then the variable has a property a and its value is 1;

But if we override one of them, for example, exports=function(){}, then they are different now: exports refers to a new object and module.exports refer to the original object. And if we require the file, it will not return the new object, since module.exports is not refer to the new object.

For me, i will keep adding new property, or override both of them to a new object. Just override one is not right. And keep in mind that module.exports is the real boss.

csdnceshi80
胖鸭 Yeah, this is actually the real answer. It's concise and clear. Others may be right but full of fancy terms and not focus exactly on the answer for this question.
2 年多之前 回复
weixin_41568183
零零乙 Acutally I think that you are the real boss :) many thanks
3 年多之前 回复
var a = {},md={};

//Firstly,the exports and module.exports point the same empty Object

exp = a;//exports =a;
md.exp = a;//module.exports = a;

exp.attr = "change";

console.log(md.exp);//{attr:"change"}

//If you point exp to other object instead of point it's property to other object. The md.exp will be empty Object {}

var a ={},md={};
exp =a;
md.exp =a;

exp = function(){ console.log('Do nothing...'); };

console.log(md.exp); //{}
共20条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐