js实现编写后端程序的不足之处前端
1. 没有模块系统(js一个先天不足就是模块功能)node
2. ECMAScript仅仅定义了js的核心库,可是对于文件系统、IO系统等却没有标准的API。HTML5虽然在一直致力于推动标准化,可是这些标准耶都是前端的。c++
3. 没有标准接口,没有定义过服务器或者数据库的接口。git
4. 缺少包管理系统,没有自动安装和管理依赖的能力。github
而CommonJS的出现正好弥补了没有标准的这一个缺点。
CommonJS
规范了 模块
、二进制
、Buffer
、二进制
、I/0
,进程环境
、文件系统
、web服务器网管接口
、包管理等
。web
Node的出现离不开CommonJS规范的影响,而CommonJS能以一种独寻常的姿态出如今各大公司的代码中,离不开Node优异的表现。数据库
1. 模块引入npm
var http = require("http");
2. 模块导出json
// math.js exports.add = function(){ ... }
// program.js Var math = require('math.js'); exports.increament = function(val){ return math.add(val); }
3. 模块标识后端
模块标识就是require()
的括号中的参数,必须是小驼峰结构,能够是相对路径也能够是绝对路径,还能够没有后缀名。
node 的模块实现其实是借鉴了CommonJS的部分,并非所有照搬,并且也增长了一些本身须要的东西进去。
1. 模块引入
node中模块引入须要通过:
1. 路径分析 2. 文件定位 3. 编译执行
2. 模块分类
1. 核心模块 Node提供的 2. 文件模块 用户编写的
3. 核心模块
核心模块部分在Node源码加载中就编译完毕了,编译成了二进制执行文件,在Node进程启动后,部分核心模块就直接加载进内存中,因此能够不用`文件定位`和`编译`,所以部分核心模块的加载时最快的。
4. 文件模块
文件模块则是在运行时动态加载,要通过完整的路径分析、文件定位、编译执行,一次通常比较慢。
5. 缓冲加载
node对二次加载的核心模块,一概采用的时缓冲优先的原则。
6. 路径分析和文件定位
模块标识符分析
核心模块的加载仅次于缓冲加载
首先将其转换为真实的路径,而后以真实路径做为索引,将其编译后放进缓冲中,等待调用。 因为是经过确切的文件地址找到的,因此须要花费必定的时间,
这是非核心模块,也不是路径形式的标识,它是一种特殊的文件模块,多是一个文件或者是一个包。这种查找是最费时间的。首先须要知道一下/模块路径/的概念/ > 例子 好比你要加载一个包,这个包放在了node_modules文件夹下,你要引入的话能够不以路径的形式写,能够是只写名称。(也就是引入一个本身npm的包) console.log(module.paths); // 会获得下面的数组 [ 'D:\\myweb\\node\\node\\module\\node_modules', 'D:\\myweb\\node\\node\\node_modules', 'D:\\myweb\\node\\node_modules', 'D:\\myweb\\node_modules', 'D:\\node_modules' ] 这就是模块路径了,查找机制是: 首先在当前路径下找是否有node_modules文件夹下的该包,若是没有就查找上一层目录,依次类推,直到根目录下的node_modules,若是依旧没有找到,那么就报错了。 // 显然这种状况就是致使它速度较慢的主要缘由了,(查找的路径越深就越慢),可是咱们能够经过一些小技巧来经可能的减小这种状况哦~~
文件定位
文件定位中还须要注意的包括有文件扩展名
、目录
、包的处理
。
node 的模块引入的时候是能够不写扩展名的,node会按照js
json
node
的顺序来分析。依次尝试。因为尝试使用的是node中fs模块的同步文件查找,所以可能会致使阻塞状况发生,所以这里咱们须要注意两个小技巧了:
小技巧:
1. json
node
文件最好加扩展名
2. 同步配合缓冲能够环节Node单线程阻塞调用的缺陷
另外,若是没有找到对应的文件,确实找到了一个目录,那么将会将其看成是一个包来处理了。
如何在这个包下找到咱们须要引入的入口文件对呢?
1. 首先找是否含有package.json,若是有,则分析它的main属性,找到main属性对应的那个文件。
2. 若是没有package.json或者是main解析失败了,那么就找文件名为index的文件,依次从index.js
index.json
index.node
查找。
3. 若是在该目录下依旧没有找到,那么就查找写一个匹配的目录,若是仍然没有找到,那么就报错了。
6. 模块编译
node中每个模块都是一个对象,当定位到一个文件的时候,node就会将其包装成一个module
对象,而后根据不一样的文件名,其载入方法也不一样的。
7. module.exports和exports的区别和联系
exports = module.exports = {};
的区别就和 var a = {}; b = a;
的区别同样.
首先要明确的一点,module
是一个对象 {Object}。
当你新建一个文件,好比mo.js
,文件内容以下:
console.log(module);
而后在CMD里执行这个文件node mo.js,就能看到module实际上是一个Module实例,你能够这么理解,NodeJS中定义了一个Module类,这个类中有不少属性和方法,exports是其中的一个属性:
function Module { id : 'blabla', exports : {}, blabla... }
当每个文件被执行或者时require的时候,node就会建立一个module实例。var module = new Module();
console.log(module); //你会看到Module中的exports为空对象{} module.exports = { print : function(){console.log(12345)} } console.log(module); //你会看到Module中的exports对象已经有了print()方法
module.exports 其实就是module实例中的module添加方法或者属性。
console.log(module); //你会看到Module中的exports为空对象{} console.log(exports); //你会看到Module中的exports为空对象{} module.exports = { print : function(){console.log(12345)} } console.log(module); //你会看到Module中的exports对象有了print()方法 exports.name = '小白妹妹'; console.log(module); //你会看到Module中的exports对象不只有了print()方法,还有了name属性
不难看出exports其实就是module.exports的一个引用。
// 你能够这么理解 var module = new Module(); var exports = module.exports;
当你require的时候,返回的就是module.exports的内容。
// 经常使用场景分析 module.exports.name = '小白妹妹'; exports.age = 10; module.exports.print = function(){console.log(12345)}; //若是只是添加属性和方法,二者能够混用。 // 也能够 module.exports = { name = '小白妹妹'; }; exports.age = 10; module.exports.print = function(){console.log(12345)}; // 【X】可是不能够 module.exports = { name = '小白妹妹'; }; exports = {age:10}; // exports如今是{age:10}这个对象的引用,再也不是module.exports的引用了 console.log(module); //你会看到Module的exports中只有name属性!!! // 【X】 exports.age = 10; console.log(module); //你会看到Module的exports中多了age属性 module.exports = { name = '小白妹妹'; }; // 直接改变了module.exports的引用,以前的属性值也被覆盖掉了。 console.log(module); //你会看到Module的exports中仍是只有name属性!!!
总结
- 改变exports的指向后所添加的exports.xxx都是无效的。由于require返回的只会是module.exports
- 不能在使用了exports.xxx以后,改变module.exports的指向。由于exports.xxx添加的属性和方法并不存在于module.exports所指向的新对象中。
- 对于要导出的属性,能够简单直接挂到exports对象上
- 对于类,为了直接使导出的内容做为类的构造器可让调用者使用new操做符建立实例对象,应该把构造函数挂到module.exports对象上,不要和导出属性值混在一块儿