为什么nodejs里面可以直接使用module?

1,287 阅读4分钟

node 遵循的是CommonJS规范, 为了避免全局命名冲突,以及同名函数名或者变量名被覆盖的情况,提出了module的概念,并且内部实现了,每个js是个独立的modlue,内部的变量和方法都是私有的,其他模块要用的话,自己要先通过module关键字导出,其他模块要用的话要用require导入。

看完本文,你讲了解到:

    1. require的时候,nodejs做了什么
    1. module module.export是什么关系
    1. 为什么可以直接使用module module.exports 以及二者直接是什么关系? 为什么可以直接使用 __dirname __filename?
    1. 循环引用的问题

一个例子

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

总结

    1. 为什么nodejs可以使用module ? 因为nodejs包装了一层,包装的时候传入了module ,require,等参数
  • modlue 和module.exports 关系是? var module = { exports: {}} 这样的关系
  • 所以不要写exports.xx = {} 这种 会覆盖引用,应该写 modlue.exports.xxKey = xxValue modlue.exports.xxKey1 = xxValue1 或者 modlue.exports = { key1: val1, key2:val2, ...}
  • node如何解决循环引用的问题?写的时候尽量避免,或者先导出 再require (有待再次验证)
  • 感谢阅读,希望有帮助。

参考