一、CommonJS特点

​ 经过前面讨论,已经知道无模块化时项目中存在的问题。CommonJS的特点就是解决这些问题即:

​ 1.每个文件都是一个单独的模块,有自己的作用域,声明的变量不是全局变量(除非在模块内声明的变量挂载到global上)

​ 2.每个文件中的成员都是私有的,对外不可见

​ 3.A模块依赖B模块时,在A模块内部使用require函数引入B模块即可,模块之间依赖关系更加清晰

​ 4.模块的加载有缓存机制,当加载完一次后,后续再加载就会读取缓存中的内容

​ 5.模块的加载顺序是按照代码的书写顺序来加载

二、CommonJS的应用环境

​ 应用在Node.js中。CommonJS的加载机制是同步的,在Node环境中模块文件是存在本地硬盘中,所以加载起来比较快,不用考虑异步模式。

三、CommonJS的用法

​ 已经知到CommonJS规范下,每个文件就是一个模块,都有私有作用域,那如何才能让外部访问某个模块中的内容呢?

​ 方式1:把模块中的成员挂载到global全局对象中 【非常不推荐】

​ 方式2:使用模块的成员module.exports或者exports导出成员

​ 最后在外部使用require函数引入所需模块

方式1:把变量挂载到global模块下【看完忘掉即可】

//module-b.js
var a = 10;
global.a = a;
//module-a.js
require("./module-b.js");
console.log(a); //10

方式2:使用module.exports导出成员

1、使用module.exports单个导出

//module-b.js
const a = 10;
const add = function(a, b) {
return a + b;
}
module.exports.a = a;
module.exports.add = add;
//module-a.js
const moduleB = require("./module-b.js"); console.log(moduleB.a); //10
console.log(moduleB.add(1, 1)); //2

2、使用module.exports直接导出一个对象

//module-b.js
const a = 10;
const add = function(a, b) {
return a + b;
}
module.exports={a,add};
//module-a.js
const moduleB = require("./module-b.js"); console.log(moduleB.a); //10
console.log(moduleB.add(1, 1)); //2

3、使用exports代替module.exports来导出成员

//module-b.js
const a = 10;
const add = function(a, b) {
return a + b;
}
exports.a=a;
exports.add=add;
//使用exports时 不可使用下面这种方式导出成员
exports={a,add}
//module-a.js
const moduleB = require("./module-b.js"); console.log(moduleB.a); //10
console.log(moduleB.add(1, 1)); //2

由代码可见,在使用module.exports和exports导出成员时略有不同,具体是为什么呢?稍后作出解释

四、module.exports、exports和require为什么可以直接使用?

从我们平时写代码的经验来看,在一个文件中可以使用的成员由以下几种情况:

1、全局成员

2、在文件内部声明了该成员

但我们所了解的代码运行环境中的全局成员只有一个,像window和global这种,那大概率不是全局成员。而且我们在模块内部并未声明这三个变量,那为何能直接使用呢?

其实在node运行环境中,每个模块都是运行在一个函数中,正是因为这个函数的存在,才让每个模块有了私有作用域

(function (exports, require, module, __filename, __dirname) {
// HERE IS YOUR CODE
});

通过代码来证明一下这个函数的存在

既然我们写的代码都在函数内部,那我们应该通过arguments能获取到这个函数的参数

//module-a.js
console.log('模块中的第一句代码');
console.log(arguments.length) //运行结果
模块中的第一句代码
5

arguments.length的值是5,那八成就是这个样子了。但感觉说服力不强,继续看...

//module-a.js
console.log('模块中的第一句代码');
console.log(arguments.callee.toString()) //运行结果
模块中的第一句代码
function (exports, require, module, __filename, __dirname) {
console.log('模块中的第一句代码');
console.log(arguments.callee.toString())
}

终于露出了庐山真面目,为什么可以直接用,应该一目了然了!(可以尝试打印一下这个五个参数中都是什么内容)

五、module.exports和exports

通过打印module成员,可以看到exports是module下的一个对象。module的exports属性表示当前模块对外输出的桥梁,module.exports指向的成员都会被暴露出去

以上示例中的写法都是把模块内的成员挂载到module.exports中暴露出去的

const a = 10;
const add = function(a, b) {
return a + b;
}
module.exports.a = a;
module.exports.add = add;
或者使用
module.exports={a,add}

exports又如何导出的呢?

//module-a.js
console.log(module.exports)
console.log(exports)
//输出结果
{}
{}

两个成员都是对象,那会不会是同一个东西呢?

//module-a.js
console.log(module.exports)
console.log(exports)
console.log(module.exports===exports)
//输出结果
{}
{}
true

可见两个成员完全相等,则指向的堆内存的地址是同一个,所以使用exports导出模块内的成员也是理所应当的了。

所以模块最外部函数应该是有这么一句代码的

(function (exports, require, module, __filename, __dirname) {
exports=module.exports={}; //指向同一个内存地址
// HERE IS YOUR CODE
});

既然exports和module.exports是指向的是同一个内存,按说用法是一样的,为什么上边使用exports导出成员时,特意说明不可以使用exports直接导出一个对象呢?不妨试一下:

//module-b.js
const a = 10;
const add = function(a, b) {
return a + b;
} // module.exports = { a, add };
exports = { a, add};
const moduleB = require("./module-b.js");

console.log(moduleB)//{}
console.log(moduleB.a)//undefined

实验得出,通过exports直接导出一个对象时在外部并拿不到导出的数据,为什么呢?

看一下module.exports和exports在内存中的情况

当加载该模块时,执行完exports=module.exports={}后的内存情况

当执行完exports={a,add}时的内存情况

当exports={a, add}时,exports在内存中和module.exports指向的就不是同一个内存地址了,说白了抱不了module.exports的大腿了,咱们上面说过,模块导出成员是通过module.exports导出的。exports和module.exports不是同一个内存时,exports自然无法导出成员了。

既然如此那就把导出的成员老老实实挂载到exports下吧。整洋气一些,module.exports和exports同时使用

//module-b.js
const a = 10;
const add = function(a, b) {
return a + b;
} module.exports = add;
exports.a = a;
//module-a.js
const moduleB = require("./module-b.js"); console.log(moduleB)//Function
console.log(moduleB.a)//undefined

纳尼???把导出的成员挂载到exports下了为何引用的时候还是undefined???

注意:在使用exports.a=a前 使用了module.exports=add了,这时候使用exports为什么导不出成员,大家应该都明白了【原因同上】

为了避免在导出成员时,有这样或那样的问题,建议在模块中全部使用module.exports吧

六、require()

通过上面一系列的代码案例可以看出,require的作用是加载所依赖的文件。说白了就是执行了所加载模块的最外层的函数。

//module-b.js
const a = 10;
console.log('module-b中打印', a)
module.exports = { a };
//module-a.js
const moduleB = require("./module-b.js");
console.log(moduleB);
执行module-a.js的结果:

module-b中打印 10
{ a: 10 }

module-b.js中的console.log执行了。可见require函数确实令模块最外部的函数执行了。

由require执行完后有个参数来接受返回值看出,模块最外部的函数执行完后是有返回值的,那么模块最外部的函数应该是这个样子:

(function (exports, require, module, __filename, __dirname) {
exports = module.exports = {}; //指向同一个内存地址
// HERE IS YOUR CODE
return module.exports;//把module.exports返回出去,同时module.exports下挂载的成员也返回出去了
});

1、require加载文件的方式(后缀名默认是js)

​ (1)通过相对路径加载模块,以"./"或"../"开头。比如:require("./module-b")是加载同级目录下的module-b.js

​ (2)通过绝对路径加载模块,以"/"开头,这时候会去磁盘盘符根目录或者网站根目录去找此模块。比如:require("/module-b"),此时 会去根目录去找module-b.js

​ (3)直接通过模块名加载,比如:require("math"),这是加载提供node内置的核心模块或者node_modules目录中安装的模块。当加 载的是node_modules中的模块时,查找规则如下

//文件所在目录  E:/Work/Module/CommonJs/module-a.js
const math = require("math");
查找规则:【假设每次在对应的目录下都没找到math模块】
1、先去CommonJS目录下的node_modules中去查找math模块
2、再去Module文件夹下的node_modules中去查找math模块
3、再去Work文件夹下node_modules中去查找math模块
4、再去E盘下的node_modules中去查找math模块
最顶级目录还找不到的话则报错...

2、模块的加载机制

CommonJS模块的加载机制是,引入的值是输出的值的拷贝,一旦模块内的值导出后,在外部如何改变都不会影响模块内部,同样模块内部对这个值如何改变也不会影响模块外部,犹如“嫁出去的女儿,泼出去的水”

模块化之CommonJS的更多相关文章

  1. JS模块化规范CommonJS,AMD,CMD

    模块化是软件系统的属性,这个系统被分解为一组高内聚,低耦合的模块.理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可.一个模块化系统所必须的 ...

  2. javascript模块化之CommonJS、AMD、CMD、UMD、ES6

    javascript模块化之CommonJS.AMD.CMD.UMD.ES6 一.总结 一句话总结: CommonJS是同步加载模块,用在服务端:AMD是异步加载模块,用于浏览器端 1.为什么服务器端 ...

  3. 【JavaScript】JavaScript模块化编程 - CommonJS, AMD 和 RequireJS之间的关系

    通行的Javascript模块规范共有两种:CommonJS和AMD 先说说CommonJS   CommonJS - 大家是不是觉得JavaScript仅仅是一个客户端的编译语言,其实JavaScr ...

  4. JavaScript模块化编程 - CommonJS, AMD 和 RequireJS之间的关系

    这几天在学习CommonJS的时候突然在StackOverflow上搜索到一个非常好的一个帖子,是关于CommonJS, AMD和RequireJS之间的关系的问答贴.我感觉写的非常好,鉴于没有找到相 ...

  5. [JavaScript] 后端js的模块化规范CommonJs

    CommonJs概述 主要是单个文件定义的变量,函数,类都是私有的,其他文件不可见,单位的作用域 通过 exports(modules.exports)对外暴露接口,通过 require 加载模块 n ...

  6. JS模块化:CommonJS和AMD(Require.js)

    早期的JS中,是没有模块化的概念的,这一情况直到09年的Node.js横空出世时有了好转,Node.js将JS作为服务端的编程语言,使得JS不得不寻求模块化的解决方案. 模块化概念 在JS中的模块是针 ...

  7. 前端模块化之CommonJS,ES6,AMD,CMD

    最近在搞跨平台解决方案,讨论关于模块划分的问题以及如何尽量多的复用逻辑代码.于是就有了此文章,之前的博客也写过,不过由于主机商跑路,宝贵的资源也就没了,说多了都是泪~ 这里按模块化发展的历史回溯的时间 ...

  8. ES6 模块化与 CommonJS 模块化

    ES6 模块化 import命令用于输入其他模块提供的功能;export命令用于规定模块的对外接口. export 可以有多个,export default 仅有一个 a.js 模块a文件 导出多个方 ...

  9. 模块化(CommonJs、AMD、CMD、UMD)发展历史与优缺点

    全文主要整理自摘自<Webpack中文指南>(好文,建议直接去看,以下仅对该系列文章中的<历史发展>篇幅进行备份——也整理了点其他内容) 模块化 模块化是老生常谈了,这里不做阐 ...

  10. (转) 前端模块化:CommonJS,AMD,CMD,ES6

    模块化的开发方式可以提高代码复用率,方便进行代码的管理.通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数.目前流行的js模块化规范有CommonJS.AMD.CMD以及ES6的模块 ...

随机推荐

  1. spring cache 学习 —— @Cacheable 使用详解

    1. 功能说明 @Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的.也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方 ...

  2. CSS_rules

    CSS 特性 1)控制灵活,功能强大 元素-->标签 针对html的元素 2)可以设置html元素的属性,与html框架的进行分离 3)执行效率更高 CSS语法 text-indent 文字缩进 ...

  3. HP PROLIANT DL388 GEN10 (故障3019)SPP损坏

    HP PROLIANT DL388 GEN10 (故障3019)SPP损坏 1. 开机硬件自检,提示错误ERROR 3019: 2. 根据服务器版本GEN10下载最新固件SPP,可找服务商或者HP售后 ...

  4. 【Java基础】集合

    集合 集合概述 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储.另一方面,使用 Array 存储对象方面具有一些弊端,而 Java 集合就像一种容器 ...

  5. version can neither be null, empty nor blank

    在用mybatis-generator逆向生成mapper和DAO的时候,出现了这个错误. mybatis-generator:generate 原因是在pom.xml中我的mysql依赖没有写版本号 ...

  6. MySQL的索引优化分析(二)

    一.索引优化 1,单表索引优化 建表 CREATE TABLE IF NOT EXISTS article( id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO ...

  7. 【Spring】 Spring的核心容器

    Spring的核心容器 文章目录 Spring的核心容器 BeanFactory ApplicationContext 1.通过ClassPathXmlApplicationContext创建 2.通 ...

  8. mysql中的kill

    show processlist;查看id, 然后 kill id ; 就行了.

  9. 基于Dockfile构建JAVA环境网站镜像

    查看本地目录 [root@docker tomcat]# ls apache-tomcat-8.5.16.tar.gz  Dockerfile  jdk-8u91-linux-x64.tar.gz   ...

  10. 什么是xss攻击

    概述: XSS攻击是Web攻击中最常见的攻击方法之一,它是通过对网页注入可执行代码且成功地被浏览器 执行,达到攻击的目的,形成了一次有效XSS攻击,一旦攻击成功,它可以获取用户的联系人列 表,然后向联 ...