-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
doc: add more detailed illustration for module.exports
and exports
.
#9552
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -481,81 +481,126 @@ added: v0.1.16 | |
|
||
* {Object} | ||
|
||
The `module.exports` object is created by the Module system. Sometimes this is | ||
not acceptable; many want their module to be an instance of some class. To do | ||
this, assign the desired export object to `module.exports`. Note that assigning | ||
the desired object to `exports` will simply rebind the local `exports` variable, | ||
which is probably not what you want to do. | ||
The `module.exports` object is used to export values in a js file. It is | ||
initialized as `{}` by Module system, and passed to | ||
[module wrapper](#modules_the_module_wrapper) as argument. (This makes | ||
`module.exports` and `exports` available in a js file, the details will be | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ", the details..." unnecessary, remove, or replace with a "see LINK" that links to the docs. |
||
described below.) `require` returns the processed `module.exports` as the | ||
exports of a module. | ||
|
||
For example suppose we were making a module called `a.js` | ||
|
||
```js | ||
const EventEmitter = require('events'); | ||
|
||
module.exports = new EventEmitter(); | ||
#### exports alias | ||
<!-- YAML | ||
added: v0.1.16 | ||
--> | ||
|
||
// Do some work, and after some time emit | ||
// the 'ready' event from the module itself. | ||
setTimeout(() => { | ||
module.exports.emit('ready'); | ||
}, 1000); | ||
``` | ||
There is no magic behind `exports`. It is the `module.exports` passed to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove "there is no magic" phrase, it adds no value. "It is the" change to " There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The key point of this sentence is i want to remove the alias, because of this word make some one like me very confused , till check the source code. alias tend to refer to something like reference, imply the things happened in export will reflect into module.export. But it is indeed a value copy from module.export by argument. |
||
[module wrapper](#modules_the_module_wrapper) as the first argument. | ||
|
||
Then in another file we could do | ||
Here is a minimized code logic of `require` to illustrate the export | ||
principle, and how `module.exports` and `exports` works:<br/> | ||
|
||
As described in [module wrapper](#modules_the_module_wrapper), your js file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. js -> Javascript There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid the use of informal pronouns such as 'you', 'your', 'us', etc. |
||
will be wrapped like this: | ||
```js | ||
const a = require('./a'); | ||
a.on('ready', () => { | ||
console.log('module a is ready'); | ||
(function (exports, require, module, __filename, __dirname) { // fileWrapper | ||
// Your module code actually lives in here | ||
// So both of the exports and module.exports are valid in your file | ||
}); | ||
``` | ||
And this is a minimized logic of `require` for `module.exports`: | ||
```js | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the purpose of this code snippet? |
||
function _require(fileWrapper) { | ||
// module.exports is initialized as {} | ||
const module = { exports:{} }; | ||
// The wrapped file will be called with this = module.exports | ||
// module.exports is passed as exports. ( exports = module.exports ) | ||
// module is passed as module. | ||
fileWrapper.apply(module.exports, | ||
[module.exports, require, module, __filename, __dirname]); | ||
// the return value of require, its the export results of a required module. | ||
return module.exports; | ||
} | ||
``` | ||
When you `require` a file, Node.js will wrap it with a function wrapper as | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is a useful way to describe require. If you want to read the code to understand the behaviour, the code is there, its open source. Docs should describe behaviour and interface, not just reformat the code and expect the reader to read the code to figure out how it behaves. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, this is me main purpose of the PR, cause of the old docs of module.export did not describe what the module.export should be and should not be, so this led to many people repeatedly ask what is a module.export and export in stackoverflow. previously if some one want to make sure what is a module.export, the only way is read the source code. Want to make this easier. I thought _require is a pseudocode more than a code of reformating. Cause an exactly description maybe need hundreds of words(maybe will make the docs like a specification), so i thought a pseudocode is suitable here, and i checked it again, it is the things happened for module.export in currently source code. The pseudocode make thes behaviors and interface of module.exports and exports obviously. |
||
above, and call it like the code logic described in `_require`. By the code | ||
logic, behaviors of `module.exports` and `exports` are dependent on these | ||
points: | ||
- `require` is a synchronize process. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. synchronize -> synchronous |
||
- `require` return the `module.exports` as the exports of a module. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "returns" |
||
- Javascript always passes by value. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. javascript always passes what by value? |
||
|
||
As a result, the export results of a module is a returned value of | ||
`module.exports` at the tickcount which `require` is returned. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not true There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the behaviors of this. I thought whenever the user do anything in a file 'x.js', the result of require('./x.js') in 'y.js', will always be the returned value of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't know exactly what you are trying to say, but I believe you are saying There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nope, i want to say it is the value of the returned There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is why i write a pseudo code for require, a pseudo code will always exactly and cross langauges and terms. (But if i am good at English, may be i do not need that pseudo code to explain what it should be.) |
||
|
||
Note that assignment to `module.exports` must be done immediately. It cannot be | ||
done in any callbacks. This does not work: | ||
|
||
x.js: | ||
Let's illustrate the behaviors and principles with codes. (You can use either | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Trott is this colloquial "let's" and "you" used in node documentation? "codes" -> "code" |
||
`_require` or directly a js file to check these behaviors.) | ||
|
||
Synchronize behaviors (Demonstrate with the minimized `_require` above) : | ||
```js | ||
setTimeout(() => { | ||
module.exports = { a: 'hello' }; | ||
}, 0); | ||
``` | ||
function synchronize(exports, _, module) { // the module wrapper | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not a useful way to describe how to write modules, since users do not actually write modules like this |
||
// Your module codes actually lives below: | ||
exports.a = 'Add a props a to module.exports'; | ||
module.exports.b = 'Add a props b'; | ||
module.exports = { | ||
c: 'Assign module.exports to a new Object, so a and b are invalid.' | ||
}; | ||
exports.d = 'exports is invalid here, its referred to old module.exports'; | ||
exports = module.exports; // reassigning exports to new module.exports | ||
exports.e = 'After reassigned to new module.exports, exports works again.'; | ||
// | ||
exports = { | ||
f: 'Assign exports to a local Object, so its not referred to module.exports' | ||
}; | ||
exports.g = 'This just add a prop to the local Object above.'; | ||
} | ||
|
||
y.js: | ||
const exported = _require(synchronize); // Only c and e will be exported. | ||
console.log(exported); | ||
/* The exported result will be: | ||
{ | ||
c: 'Assign a new Object to module.exports, so a and b are invalid.' | ||
e: 'After reassigned to new module.exports, exports works again.' | ||
} | ||
*/ | ||
``` | ||
|
||
Asynchronous behaviors (directly checking in file) :<br/> | ||
Assume you have a `x.js`: | ||
```js | ||
const x = require('./x'); | ||
console.log(x.a); | ||
// Let's name the exported value, to make things clearly. | ||
const the_returned_exports = { | ||
a: 'sync export prop a' | ||
}; | ||
module.exports = the_returned_exports; | ||
setTimeout( () => { | ||
module.exports.b = 'okay, module.exports is referred to the_returned_exports'+ | ||
' which returned in previously tickcount.'; | ||
module.exports = { | ||
c: 'does nothing, it assign a new Object, but the module.exports was' + | ||
' returned at previously tickcount.( the return is the_returned_exports ).' | ||
} | ||
},1000); | ||
``` | ||
|
||
#### exports alias | ||
<!-- YAML | ||
added: v0.1.16 | ||
--> | ||
|
||
The `exports` variable that is available within a module starts as a reference | ||
to `module.exports`. As with any variable, if you assign a new value to it, it | ||
is no longer bound to the previous value. | ||
|
||
To illustrate the behavior, imagine this hypothetical implementation of | ||
`require()`: | ||
|
||
Then in `y.js`: | ||
```js | ||
function require(...) { | ||
// ... | ||
((module, exports) => { | ||
// Your module code here | ||
exports = some_func; // re-assigns exports, exports is no longer | ||
// a shortcut, and nothing is exported. | ||
module.exports = some_func; // makes your module export 0 | ||
})(module, module.exports); | ||
return module; | ||
} | ||
const x = require('./x'); | ||
console.log(x); // { a } here | ||
setTimeout( () => { | ||
console.log(x); // { a, b } here | ||
},1000); | ||
// c is not exported | ||
``` | ||
|
||
As a guideline, if the relationship between `exports` and `module.exports` | ||
seems like magic to you, ignore `exports` and only use `module.exports`. | ||
There is one more thing to notice: `this`. Because `module.exports` was passed | ||
as `this` argument for `The module wrapper`, you may used `this` as an alias of | ||
`exports` to export values previously, But since `this` is an undocumented | ||
value, the behavior of `this` is not guaranteed. Ex: when you use `babel`, | ||
`transform-es2015-modules-commonjs` will break `this`, so you should not use | ||
`this` as a shortcut for export. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't agree with the above. this is documented, its documented right here! And if the module does not work when some random thirdparty tool is used to convert it to some other form... that should be part of that tools documentation, not proposed as a general rule for node modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did do find this in old module docs. searched in doc/api/module.md again previously. |
||
|
||
**As a guideline**, never use 'this' to export, and if the `exports` and | ||
`module.exports` makes you confused, ignore `exports` and only use | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you are still confused at this point, the docs have failed. I don't see any description here about the real difference between the two: exports is more brief to use, and is good when just exporting functions module.exports is necessary to use when a module should return something other than the default object provided as an export value, such as a function If you want to add guidelines, the guideline should be to redefine exports and module.exports at the same time:
|
||
`module.exports`. | ||
|
||
### module.filename | ||
<!-- YAML | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"as an argument". Remove the parentheses around the complete sentence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: replace:
with: