一、CommonJS
在nodejs中,由于有且仅有一个入口文件(启动文件),而开发一个应用肯定会涉及到多个文件配合,因此,nodejs对模块化的需求比浏览器端要大的多
由于nodejs刚刚发布的时候,前端没有统一的、官方的模块化规范,因此,它选择使用社区提供的CommonJS作为模块化规范
在学习CommonJS之前,首先认识两个重要的概念:模块的导出和模块的导入
1.模块的导出
要理解模块的导出,首先要理解模块的含义
什么是模块?
模块就是一个JS文件,它实现了一部分功能,并隐藏自己的内部实现,同时提供了一些接口供其他模块使用
模块有两个核心要素:隐藏和暴露
隐藏的,是自己内部的实现
暴露的,是希望外部使用的接口
任何一个正常的模块化标准,都应该默认隐藏模块中的所有实现,而通过一些语法或api调用来暴露接口
暴露接口的过程即模块的导出
2.模块的导入
当需要使用一个模块时,使用的是该模块暴露的部分(导出的部分),隐藏的部分是永远无法使用的。
当通过某种语法或api去使用一个模块时,这个过程叫做模块的导入
3.CommonJS规范
CommonJS使用exports
导出模块,require
导入模块
具体规范如下:
- 如果一个JS文件中存在
exports
或require
,该JS文件是一个模块- 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染
- 如果一个模块需要暴露一些API提供给外部使用,需要通过
exports
导出,exports
是一个空的对象,你可以为该对象添加任何需要导出的内容- 如果一个模块需要导入其他模块,通过
require
实现,require
是一个函数,传入模块的路径即可返回该模块导出的整个内容
4.nodejs对CommonJS的实现
为了实现CommonJS规范,nodejs对模块做出了以下处理
- 为了保证高效的执行,仅加载必要的模块。nodejs只有执行到
require
函数时才会加载并执行模块 - 为了隐藏模块中的代码,nodejs执行模块时,会将模块中的所有代码放置到一个函数中执行,以保证不污染全局变量。
(function(){
//模块中的代码
})()
- 为了保证顺利的导出模块内容,nodejs做了以下处理
- 在模块开始执行前,初始化一个值
module.exports = {}
module.exports
即模块的导出值- 为了方便开发者便捷的导出,nodejs在初始化完
module.exports
后,又声明了一个变量exports = module.exports
- 在模块开始执行前,初始化一个值
(function(module){
module.exports = {};
var exports = module.exports;
//模块中的代码
return module.exports;
})()
- 为了避免反复加载同一个模块,nodejs默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果
5.实例
//index.js中
//nodejs中导入模块,使用相对路径,并且必须以./或../开头
const util = require("./util.js");
//util.js中
var count = 0; //需要隐藏的内部实现
console.log("util模块执行了!");
exports.getCount = function (){
count ++;
console.log('count被输出:'+count);
};
exports.count = count;
//util.js中
var count = 0; //需要隐藏的内部实现
console.log("util模块执行了!");
module.exports.getCount = function (){
count ++;
console.log('count被输出:'+count);
};
module.exports.count = count;
var count = 0; //需要隐藏的内部实现
console.log("util模块执行了!");
module.exports = {//该方式更常用
getCount: function (){
count ++;
console.log('count被输出:'+count);
},
str: 'jimo'
}
6.练习
制作一个斗地主洗牌发牌的程序
划分模块:
1. 工具模块,导出一个函数,用于将一个数组中的所有内容乱序排列
2. 扑克牌构造函数(类)
1. 属性
1. 花色(1~4,♣、♥、♦、♠)
2. 牌面(1~15,14小王,15大王)
2. 方法
1. toString:得到该扑克牌的字符串
3. 入口模块(入口文件)
1. 创建54张扑克牌
2. 洗牌
3. 发牌
//index.js启动板块
var pokers = []; //扑克牌的数组
var Poker = require("./poker") //导入扑克牌的构造函数
for(var j = 1;j <= 13;j ++){
for(var i = 1;i <= 4;i ++){
pokers.push(new Poker(i,j));
}
};
pokers.push(new Poker(null, 14), new Poker(null, 15));
var util = require('./util');//导入洗牌发牌功能
util.sortRandom(pokers);//洗牌
var player1 = pokers.slice(0, 18);//人数牌数
var player2 = pokers.slice(18, 36);
var player3 = pokers.slice(36);
util.print(player1);//发牌
util.print(player2);
util.print(player3);
//poker.js扑克牌的构造函数
function Poker(color,number){
this.number = number;
this.color = color;
};
Poker.prototype.createPoke = function(){
var str = '';
//花色:♣、♥、♦、♠
if (this.color === 1) {
str = "♣"
}
else if (this.color === 2) {
str = "♥";
}
else if (this.color === 3) {
str = "♦";
}
else {
str = "♠"
};
//牌面
if(this.number >= 2 && this.number <= 10){
str += this.number;
}else if(this.number == 1){
str += 'A';
}else if(this.number == 11){
str += 'J';
}
else if(this.number == 12){
str += 'Q';
}
else if(this.number == 13){
str += 'K';
}else if (this.number === 14) {
str = "joker";
}
else if (this.number === 15) {
str = "JOKER";
};
return str;
}
module.exports = Poker;
//util洗牌发牌
module.exports = {
sortRandom: function (arr) {//洗牌
arr.sort(function (a, b) {
return Math.random() - 0.5;
})
},
print: function (pokers) {//发牌
var str = "";
for (var i = 0; i < pokers.length; i++) {
var p = pokers[i];
str += p.createPoke() + " ";
}
console.log(str);
}
};
这里可以继续丰富完善,比如洗牌后,发牌到牌手时会自动排序等