在开发一个复杂的应用程序的时候,咱们须要把各个功能拆分、封装到不一样的文件,在须要的时候引用该文件。没人会写一个几万行代码的文件,这样在可读性、复用性和维护性上都不好,几乎全部的编程语言都有本身的模块组织方式,好比Java中的包、C#中的程序集等,node.js使用模块和包来组织,其机制实现参照了CommonJS标准,虽未彻底遵照,但差距不大,使用起来很是简单。html
在node.js中模块与文件是一一对应的,也就是说一个node.js文件就是一个模块,文件内容多是咱们封装好的一些JavaScript方法、JSON数据、编译过的C/C++拓展等,在关于node.js的误会提到过node.js的架构前端
其中http、fs、net等都是node.js提供的核心模块,使用C/C++实现,外部用JavaScript封装。node
模块在node.js中的概念很简单,看看如何建立一个咱们本身的模块供开发复用。npm
在node.js中建立模块很是简单,一个文件就是一个模块,因此咱们建立一个test.js文件就建立了一个模块编程
test.js闭包
问题是怎么使外部访问这个module,咱们知道客户端的JavaScript使用script标签引入JavaScript文件就能够访问其内容了,但这样带了的弊端不少,最大的就是做用域相同,产生冲突问题,以致于前端大师们想出了当即执行函数等方式,利用闭包解决。node.js使用exports和require对象来解决对外提供接口和引用模块的问题。架构
咱们能够把模块中但愿被外界访问的内容定义到exports对象中,对test.js稍做修改就能够了编程语言
test.js函数
var name=''; function setName(n){ name=n; } function printName(){ console.log(name); } exports.setName=setName; exports.printName=printName;
这样咱们在相同路径下建立index.js,使用require引用一下test.js moduleui
index.js
var test=require('./test'); test.setName('Byron'); test.printName();
有时候咱们但愿模块对外提供的使一个对象,修改一下test.js
test.js
var Student=function(){ var name=''; this.setName=function(n){ name=n; }; this.printName=function(){ console.log(name) ; }; }; exports.Student=Student;
这样咱们对外提供了一个Student类,在使用的时候须要这样
var Student=require('./test').Student; var student=new Student(); student.setName('Byron'); student.printName();
require('./test').Student 很丑陋的样子,咱们能够简单修改一下exports方式,使require优雅一些
test.js
var Student=function(){ var name=''; this.setName=function(n){ name=n; }; this.printName=function(){ console.log(name) ; }; }; module.exports=Student;
这样咱们的require语句就能够优雅一些了
var Student=require('./test');
很神奇的样子,不是说好的exports是模块公开的接口嘛,那么module.exports是什么东西?
事实的状况是酱紫的,其实module.exports才是模块公开的接口,每一个模块都会自动建立一个module对象,对象有一个modules的属性,初始值是个空对象{},module的公开接口就是这个属性——module.exports。既然如此那和exports对象有毛线关系啊!为何咱们也能够经过exports对象来公开接口呢?
为了方便,模块中会有一个exports对象,和module.exports指向同一个变量,因此咱们修改exports对象的时候也会修改module.exports对象,这样咱们就明白网上盛传的module.exports对象不为空的时候exports对象就自动忽略是怎么回事儿了,由于module.exports经过赋值方式已经和exports对象指向的变量不一样了,exports对象怎么改和module.exports对象不要紧了。
大概就是这么过程
module.exports=exports={}; ...... module.exports=new Object(); exports=xxx;//和new Object没有关系了,最后返回module.exports,因此改动都无效了
不管调用多少次require,对于同一模块node.js只会加载一次,引用屡次获取的还是相同的实例,看个例子
test.js
var name=''; function setName(n){ name=n; } function printName(){ console.log(name); } exports.setName=setName; exports.printName=printName;
index.js
var test1=require('./test'), test2=require('./test'); test1.setName('Byron'); test2.printName();
执行结果并非'',而是输出了test1设置的名字,虽然引用两次,可是获取的是一个实例
node.js中模块有两种类型:核心模块和文件模块,核心模块直接使用名称获取,好比最长用的http模块
var http=require('http');
在上面例子中咱们使用了相对路径 './test'来获取自定义文件模块,那么node.js有几种搜索加载模块方式呢?