这篇文章本来是想模块导入导出和事件循环一起写的,但是感觉一起写的话会太长了,所以就分开两篇文章写吧。下一篇会重点介绍一下js中的事件循环,js代码到底是以何种顺序去执行的呢?我相信你看懂了事件循环再去看node对你的帮助是非常大的。
讲模块系统之前先认识一下node.js中的全局对象。
node.js的全局对象
众所周知的是在浏览器中的老大哥是谁,它就是window,this指向的也是window,那么在node中的全局对象就不是window了,而是global,可以在命令行中去看一下,想学习node的应该已经安装了node环境,如果还没有安装可以去node中文网去找到你对应的操作系统和版本去下载,如果node命令不是全局还需要配置一下环境变量,现在window操作系统安装上node之后应该就自动配置完成了。
打开命令行,输入 node 回车,然后输入 this 或者global就可以看到全局对象。你会看到好多东西,但是他比window对象是少太多太多了。
在JavaScript中,使用script标签去引入js文件的话,那么在js文件中的全局变量都会挂载到window对象下面,在各个文件中都可以共享它那个变量,比如jQuery,你引入了一个jQuery文件,那么在其它的文件当中,你是可以访问到$这个变量的。
而在node.js中是如何实现文件之间的的引入呢,就不得不提及到commonjs了。
common.js
CommonJs是一种规范,Node.js就是这种规范的实现。
引入模块require
全局变量在所有模块中均可使用。讲道理我们理解的require就是一个全局变量, 但是官方文档说的是此变量虽然看起来像全局变量,但实际上不是。 它们的作用域只在模块内。那我也不管它了,它的实现大概也就是像那种在文件中的一个函数把require传进来,像下面这样。
具体的讲解:CommonJs模块规范,我们知道每个模块文件中存在着require、exports、module这3个变量,但是它们在模块文件中并没有定义,那么从何而来呢?甚至在Node的API文档中我们知道每个模块中还有__filename、__dirname这两个变量的存在,它们又是从何而来的呢?
事实上,在编译的过程中,Node.js对获取的JavaScript文件内容进行了头尾包装。在头部添加了(function(exports,require,module,__filename,__dirname){\n,在尾部添加了\n});一个正常的JavaScript文件被包装成了如下的样子。
(function (exports,require,module,__filename,__dirname) { exports.a = 1; exports.fn = function () { console.log(1); }; });
如图为global的全局变量
具体的使用方法
let obj = require('./2.js'); console.log(obj);
值得注意的是: 1. ./代表的是当前目录下,然后是2.js,需要注意的是,如果引入的是本地的文件,那么一定要带上路径。
2. 如果后缀名是js文件的话是可以省略的。
3.有一些模块是不需要带路径的,它们称之为核心模块,何为核心模块
第一种是安装好node就有的一些模块,另外一种是用npm安装依赖的那些在node_modules文件夹下面的
4.模块的加载机制: 文件名 > 文件名.js >文件名.json>文件名.node
与前端浏览器会缓存静态脚本文件以提高性能一样,Node对引入过的模块都会进行缓存,以减少二次引入时的开销。不同的地方在于,浏览器仅仅缓存,而Node缓存的是编译和执行之后的对象。
安装好node就有的一些模块可以去node中文网的文档左侧都是,比如这些都是。
导出模块 module.exports
先看下面的例子,运行app.js,具体操作为命令行打开到当前的目录下, 运行 node app.js
app.js
let obj = require('./2.js'); console.log(obj); // 1
2.js
module.exports = 1;
app.js文件中引入的是2.js文件,然后 2.js文件通过module.exports来赋值为1,require的话还有一点就是他会去寻找引入进来文件的module.export,然后把那个1赋值给了obj,打印出来 1。
module也是一个全局变量,它和require一样,但实际上不是。 它们的作用域只在模块内。导出模块可能有的人见过下面的写法。
2.js
exports.a = 1;
app.js代码同样不改变,打印出来的东西是 {a: 1};
其实exports是module.exports的一个引用, exports 也是一个全局变量,和上面require,module一样,作用域只在模块内。
导出模块的话你可能会想到这样做
1 module.exports = {}; //在内部的话这个东西默认等于{} 2 3 //所以你想到了导出的话可以这样 4 module.exports = { 5 a: 1, 6 b: function(){}, 7 c: 'name' 8 }
如果你这样做的话就是不正确的
1 //这样做是不正确的 2 exports = { 3 a: 1, 4 b: function(){}, 5 c: 'name' 6 }; 7 8 // 正确的做法应该是这样 9 exports.a = 1; 10 exports.b = function () {}; 11 exports.c = 'name'; 12 13 // 因为exports是module.exports的一个引用,重新给exports赋值的话只会改变exports的值,而require引入的话寻找的是模块中的module.exports
可能有的人会这样想,我直接写到global对象下面去不就可以引入了吗,为什么还需要用exports这种东西呢?具体写法如下
app.js
let obj = require('./2.js'); console.log(global.obj); console.log(global.obj2);
2.js
global.obj = { a: 'name', b: function () { console.log('1111') } }; global.obj2 = '狗蛋';
怎么说呢,这样做是可以的,但是不推荐把所有的东西都写到global下面,会污染global对象,还是推荐使用exports去进行模块之间的导入导出。
模块的导入导出的操作非常简答,但是对一些概念性的东西理解之后我相信会更好,当然require还有好多原理没有讲到,如果有兴趣的可以查阅资料继续学习,我相信如果只是使用的话掌握到现在的程度已经是可以的了,想继续深入的可以看看 朴灵写的 深入浅出Node.js 这本书。
如果你看了我的文章有了一些收获我会非常高兴的,由于能力有限,文章有的部分解释的不到位,希望在以后的日子里能慢慢提高自己能力,如果不足之处,还望指正。