作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.COMMON JS规范
1>.ECMA Script 5标准的缺陷
早期ECMA Script 5标准的缺陷概述:
(1)在ECMA Script 5中是没有模块系统,但是有模块化的组件,比如Jquery,但并非模块化系统,在ECMA Script 6是有较为完整的模块化系统了;
(2)标准库较少;
(3)没有标准接口;
(4)缺乏管理系统,比如咱们想要安装Jquery,Vue,React等开源组件需要程序员自行去相应的官网下载安装,因此在早期的ECMA Script 5中,缺少类似python的pip包管理工具。
综上所述,我们需要模块话技术:
(1)如果程序的规模达到了一定的程度,则必须对其进行模块化;
(2)模块化可以有多种形式,但至少应该提供能够将代码分割为多个源文件的机制;
(3)CommonJS的模块功能可以帮我们解决该问题;
2>.Common JS规范
CommonJS规范的提出,主要是为了弥补当前JavaScript没有标准的缺陷。
CommonJS规范为JS指定了一个美好的愿景,希望JS能够在任何地方运行。
CommonJS对模块的定义十分简单,如下所示:
(1)模块引用;
(2)模块定义;
(3)模块标识;
3>.模块的引用
在规范中,定义了require()方法,这个方法接手模块标识,以此将一个模块引入到当前运行环境中。
模块引用的示例代码可参考下面的案例。
4>.模块定义
在运行环境中,提供了exports对象用于导出当前模块的方法或者变量,并且它是唯一的导出的出口。
在模块中还存在一个module对象,它代表模块自身,而exports是module的属性。即"exports=module.exports"。在NodeJS中一个文件就是一个模块。
模块定义的示例代码可参考下面"module.exports和exports的区别"的案例。
5>.模块标识
模块标识其实就是模块的名字,也就是传递给require()方法的参数,它必须是符合驼峰命名法的字符串,或者是以当前目录("."),上级目录("..")开头的相对路径、或者绝对路径。
模块的定义十分简单,接口也十分简洁。每个模块具有独立的空间,它们互不干扰,在引用时也显得干净利落。
模块标识的示例代码可以参考下面的案例。
6>.NodeJS模块的实现
NodeJS中虽然使用的是CommonJS规范,但是其自身也对规范做了一些取舍。
在NodeJS中引入模块,需要经历如下3个步骤:
(1)路径分析
(2)文件定位
(3)编译执行
在NodeJS中,模块分为三类:
(1)是底层由C++编写的内建模块;
(2)是Node提供的核心模块(例如"os","sys","fs","path"等模块);
(3)用户编写的模块,称为文件模块。
二.模块化
1>.模块化概述
在开发过程中,我们写程序可以将代码写入到一个文件中,这在你的应用较小时其实并没有什么影响(比如你就想实现一个简单的九九乘法表的功能)。但是如果你的应用程序比较大的话那将整个程序写在同一个文件中是不可取的。
在实际开发中,我们需要将一个程序拆开称为多个模块,将一个大的应用拆开成为多个模块包括但不限于以下几个好处:
(1)可以降低耦合度,如果一个模块对其它的模块依赖度很强,则耦合度也强,我们将耦合度底的模块从主程序中拆解出来,便于以后用于其它的项目,从而降低主程序本身的耦合度;
(2)可以复用现有的代码,比如一个系统包括权限管理,路由分发,视图函数,权限检查等功能,我们可以将其拆开,复用到其它项目中;
(3)从长远的角度看可以降低开发的成本,因为之前开发的项目模块化后,后期的项目可以直接复用之前的模块。
在NodeJS中,任意一个JavaScript文件都是一个独立的模块,各个Javascript模块之间是可以相互引用的。
2>.自定义编写用于被demo.js模块导入的模块
/**
* 我们在全局作用域定义的变量并不能跨越模块哟~比如下面我定义了一个list变量,该变量并不能被其它模块导入。
*
* 当然,如果您非要将list属性暴露给其它模块,就得将其定义为exports的属性哟~
*/
function for66(start=1,stop=6){
for(var i = start; i <= stop; i++){
var list = [];
for(var j = 1;j <= i;j++){
result = j + "x" + i + "=" + i * j;
list.push(result);
}
console.log(list);
}
}
/**
* 我们可以通过exports来向外部暴露变量和放啊,只需要将需要暴露给外部的变量或方法设置为exports的属性即可。
*/
exports.name = "尹正杰";
exports.age = 18;
exports.func = for66;
/**
* 注意哈,我们定义的add函数尽管是全局作用域的变量,但它并不能跨越模块,因此
*/
function add(x,y) {
return x + y;
}
console.log(add(10,20))
helloworld.js模块
/**
* 定义一个math模块,在该模块提供两个方法:
* add(x, y):
* 提供两个数相加的和。
*
* subtract(x, y):
* 提供两个数相减。
*
* multiply(x, y):
* 提供两个数相乘。
*
* divide(x, y):
* 提供两个数相除的方法。
*
*/
exports.add = function (x,y) {
return x + y;
}
exports.subtract = function (x,y) {
return x - y;
}
exports.multiply = function (x,y) {
return x * y;
}
exports.divide = function (x,y) {
return x / y;
}
math.js模块
3>.编写demo.js模块(调用helloworld.js模块和math.js模块)
/**
* 在nodejs中,我们可以通过require()函数来引入外部的模块,可以为该函数传递模块的路径。
*
* 使用require()引入外部模块以后,该函数返回的就是该模块引用。
*
*/
var result = require("./helloworld.js");
var math = require("./math") // 注意哈,模块的js后缀也可以不写哟~
console.log(result)
console.log(result.name)
console.log(result.age)
result.func()
console.log(math.add(100,20))
console.log(math.subtract(100,20))
console.log(math.multiply(100,20))
console.log(math.divide(100,20))
三."module.exports"和"exports"的区别
1>.MonkeyKing.js模块文件导出变量和函数
/**
* exports等效于实际上对应的是"module.exports"的简写形式。本质上指向的是同一个对象。
*/
exports.name = "齐天大圣"
exports.age = "501"
exports.sayHello = function () {
return "我是花果山水帘洞齐天大圣孙悟空是也!"
}
/**
* 综上所述,exports等效于"module.exports",因此他们下面这种写法等效于上面的写法。
*/
module.exports.name = "美猴王"
module.exports.age = "502"
module.exports.sayHello = function () {
return "我是花果山水帘洞齐天大圣美猴王是也!"
}
/**
* 但是"module.exports"和"exports"毕竟还是有区别的:
* (1)"exports"只能基于"exports.Attribute_name"的形式对模块外部暴露一个属性;
* (2)而"module.exports"既可以通过"exports.Attribute_name"的形式对模块外部暴露1个属性,也可以通过直接赋值的方式(如下面的案例所示)对模块外部暴露多个属性;
*
* 温馨提示:
* 如果您对"module.exports"和"exports"对模块外部暴露属性的区别容易搞混淆的话,我推荐大家以后就直接使用"module.exports"即可,因为它的容错性较强!
*/
module.exports = {
name:"孙行者",
age:503,
sayHello:function () {
return "我是花果山水帘洞齐天大圣孙行者是也!"
}
}
/**
* 注意观察下面的代码,总结一下两条:
* (1)exports本质上指向的是"module.exports",但我们下面竟然将exports的属性值进行了修改。换句话说,此时"exports"和"module.exports"并非指向堆内存中的同一个对象了,而是我们自定义的一个object实例。
* (2)如果按照下面的方式定义"exports",此时我们只能当"exports"是一个全局变量,而非起到对模块外部暴露多个属性的作用,因此最终对外暴露的多个属性是由上面的"module.exports"来进行暴露的哟~
*/
exports = {
name:"者行孙",
age:504,
sayHello:function () {
return "我是花果山水帘洞齐天大圣者行孙是也!"
}
}
2>.demo.js模块导入MonkeyKing.js模块并调用对应的属性和方法
// 导入MonkeyKing.js模块
var result = require("./MonkeyKing");
console.log(result.name);
console.log(result.age);
console.log(result.sayHello());