node 遵循的是CommonJS规范, 为了避免全局命名冲突,以及同名函数名或者变量名被覆盖的情况,提出了module的概念,并且内部实现了,每个js是个独立的modlue,内部的变量和方法都是私有的,其他模块要用的话,自己要先通过module关键字导出,其他模块要用的话要用require导入。
看完本文,你讲了解到:
-
- require的时候,nodejs做了什么
-
- module module.export是什么关系
-
- 为什么可以直接使用
modulemodule.exports以及二者直接是什么关系? 为什么可以直接使用__dirname__filename?
- 为什么可以直接使用
-
- 循环引用的问题
一个例子
Q: 新建一个 a.js 里面写了require('') 运行后 会发生什么?
- A 会报错
The argument 'id' must be a non-empty string. Received, 报错的信息在nodejs源码里面写着所以直接require('') 会走到nodejs内部Modlue原型上的require方法里面,会先判断传入的路径是否为空, 为空就报错,停止往下执行。
我们新建一个b.js直接触发一个报错
var a = ;,运行会发现,我们写的js文件,node会包装一层, 包装的方式就是类似这种:
(function (exports, require, module, __filename, __dirname) { var a = ;- 因为,nodejs会把exports ,require, modlue当参数导入进来, 所以我们可以在写js的时候,直接使用。
- 那我们可以直接打印这几个变量,看看他们是什么关系
// console.log(module);
// Module {
// id: '.',
// exports: {},
// parent: null,
// filename:
// '/Users//development//nodeFromStratch/005.require.js',
// loaded: false,
// children: [],
// paths:
// [ '/Users//development//nodeFromStratch/node_modules',
// '/Users//development//node_modules',
// '/Users//development//node_modules',
// '/Users//development/node_modules',
// '/Users//node_modules',
// '/Users/node_modules',
// '/node_modules' ] }
- 发现是个
Modlue对象里面有个属性,exports 值是{} 这就解释了为什么,我们导入变量或者函数的时候要这么写了
var url = "test.com";
function logUrl(message) {
//do something
console.log("log method invoked");
console.log(message);
}
module.exports.log = logUrl;
module.exports.SomeUrl = url;
- 调用的堆栈信息从下往上为:
startup->Module.runMain->Module._load等一系列过程。
//b.js
var a =;
//报错信息
(function (exports, require, module, __filename, __dirname) { var a = ;
^
SyntaxError: Unexpected token ;
at new Script (vm.js:80:7)
at createScript (vm.js:274:10)
at Object.runInThisContext (vm.js:326:10)
at Module._compile (internal/modules/cjs/loader.js:664:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
at Module.load (internal/modules/cjs/loader.js:600:32)
at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
at Function.Module._load (internal/modules/cjs/loader.js:531:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)
at startup (internal/bootstrap/node.js:283:19)
nodejs requirejs大概原理
- 当我们写个nodejs文件的时候。站在nodejs角度,他大概做了这些事:
//自己实现的例子
function require() {
var modlue = {
exports: {}
// ... //省略其他属性和方法
};
(function(modlue, exports) {
//用户写的js被包装在这里
var a = 1;
modlue.exports.a = 1;
})(modlue, modlue.exports);
return modlue.exports;
}
console.log(require());//{ a: 1 }
//node官网的例子
To illustrate the behavior, imagine this hypothetical implementation of require(), which is quite similar to what is actually done by require():
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Module code here. In this example, define a function.
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
// At this point, the module will now export someFunc, instead of the
// default object.
})(module, module.exports);
return module.exports;
}
require怎么用,用在文件的哪里?
- require的使用非常简单,它相当于module.exports的传送门,module.exports后面的内容是什么,require的结果就是什么,对象、数字、字符串、函数……再把require的结果赋值给某个变量,相当于把require和module.exports进行平行空间的位置重叠。
而且require理论上可以运用在代码的任何地方,甚至不需要赋值给某个变量之后再使用,比如:
require('./a')(); // a模块是一个函数,立即执行a模块函数
var data = require('./a').data; // a模块导出的是一个对象
var a = require('./a')[0]; // a模块导出的是一个数组
你在使用时,完全可以忽略模块化这个概念来使用require,仅仅把它当做一个node内置的全局函数,它的参数甚至可以是表达式
require(process.cwd() + '/a');
或者
nodejs如何解决循环引用的问题?官网给出的是 unfinished copy
- 看一个例子 a.js require b.js 同时b.js 引用a.js
//a.js
var b = require("./b");
console.log("in a.js get b is :" + b.b);
module.exports.a = "this is a";
//in b.js get a is :undefined
// in a.js get abis :this is b
// b.js
var a = require("./a");
console.log("in b.js get a is :" + a.a);
module.exports.b = "this is b";
//运行a .js 结果是有个没有输出
//in b.js get a is :undefined
// in a.js get abis :this is b
//如何解决? 修改a b 中 先导出 再 reqiure
//a.js
module.exports.a = "this is a";
var b = require("./b");
console.log("in a.js get b is :" + b.b);
//b.js
module.exports.b = "this is b";
var a = require("./a");
console.log("in b.js get a is :" + a.a);
//再次运行a .js 得到结果为
in b.js get a is :this is a
in a.js get b is :this is b
//再次运行b.js 得到结果为
in a.js get b is :this is b
in b.js get a is :this is a
总结
-
- 为什么nodejs可以使用module ? 因为nodejs包装了一层,包装的时候传入了module ,require,等参数
- modlue 和module.exports 关系是? var module = { exports: {}} 这样的关系
- 所以不要写exports.xx = {} 这种 会覆盖引用,应该写
modlue.exports.xxKey = xxValuemodlue.exports.xxKey1 = xxValue1或者modlue.exports = { key1: val1, key2:val2, ...} - node如何解决循环引用的问题?写的时候尽量避免,或者先导出 再require (有待再次验证)
- 感谢阅读,希望有帮助。