前面聊过JavaScript中的模块的诞生要晚于Nodejs的模块概念的,当然nodejs的引入数据也是有格式的,ES6中使用的关键字 export和import,而nodejs中的关键是是exports和require两个关键字:
- exports :将模块(一般默认一个单独的js)中的js的数据暴露出去。但是又有些写
module.exports
。两者具体有什么相同和区别下面也会聊。 - require: 将模块中的数据引入到目前的模块中。
require引用模块的时候,会自动执行模块的代码。
nodejs中的模块来源不同一般分为三种:
- 内置模块: 这些是nodejs自己提供的内置模块,可以直接引用,比如fs,path,http等
- 自定义模块: 程序员自己创建的模块
- 第三方模块:由第三方写的模块,不是内置也不需要用户写,相当于java中的第三包引用,不过这个需要下载到本地然后使用(聊这个的时候会聊npm)
当然还有一些名词需要简单的了解一下:
-
模块作用域:
就是说明这个变量在所在的js中由效果,而不是一个window的变量,引入的文件也无法直接引用被引用的变量。
-
module对象:
每个引入的js自定义模块中都有一个module对象,它存储着当前模块的所有信息
老规矩演示一个:
端口命令运行nodejs
前提:安装好nodejs这个js运行环境。
首先来一个test.js
console.log('nodejs helloworld');
通过命令窗口调用这个命令
# 第一步骤 打开端口 然后 cd到 js 所在文件
cd .....js
或者直接在js所在文件,然后再地址栏输入:cmd
然后输入:
node ./test.js
但是一般为了方便不会再窗口使用,而是将nodejs集成在编程软件中,比如我用的pytcharm,然后安装了nodejs插件。所以可以直接运行,下面也是通过这个pycharm演示,而不是通过端口演示,但是所示的js也是可以通过端口运行的。
模块对象
首先我们看一下module这个对象是什么鬼?
console.log(module);
- id : 这个输出是 .
- path: nodejs运行js模块所在的文件路径。
- exports: 这个是模块暴露的对象,也就是引用这个js模块可以在引用文件 中使用的。
- filename: 当前模块文件名,同时带有路径。
- loaded :属性值为布尔值。当值为false表示模块未加载完毕,属性值为true时,表示模块加载完毕。
- children : 包含模块中引用模块的信息(比如b引用了a,所以b中module中children由a的数据包括 id,path等数据)的数组。
- paths: 是安装node后用来存放用包管理工具下载安装的包的文件夹,优先调用同目录下的,如果找不到就找父路径下,依次到根路径而组成一个数组。
模块引用
例子演示
现在来一个模块引用演示:
第一文件 : test.js
console.log('test被引用的时候自动执行了')
var name='张三';
exports.name= name;
exports.drive=function(){
console.log('开汽车');
}
第二个文件: test_main.js
// 引入的时候一般用调用的模块命名 方便使用
const test= require('./test.js');
// 无法直接使用调用模块的变量
// console.log('不可直接调用----',name) //name is not defined
console.log('通过返回module对象调用-----',test.name)
test.drive();
// 在后面演示一下children属性
console.log(module.children)
然后看一下调用:
既然exports是module的一个属性,自然也可以如下写:
console.log('test被引用的时候自动执行了')
var name='张三';
module.exports.name= name;
module.exports.drive=function(){
console.log('开汽车');
}
最后发现结果是一样的,所以不再黏贴结果图片。
两个似乎是一个东西,然后在做以下尝试:
test.js改成如下:
console.log('test被引用的时候自动执行了')
var name='张三';
exports.name= name;
module.exports.drive=function(){
console.log('开汽车');
}
test_main.js如下:
// 引入的时候一般用调用的模块命名 方便使用
const test= require('./test.js');
console.log('通过返回module对象调用-----',test.name)
test.drive();
module.export 和 export区别
上面演示似乎exports和module.exports都是指向一个对象。
可以如下演示:
console.log('typeof exports: ',typeof exports);
console.log('typeof module.exports: ',typeof module.exports);
console.log('exports==module.exports: ',exports==module.exports);
console.log('exports===module.exports: ',exports===module.exports);
l通过比较可以看出是一个Object数据,说明是一个引用数据,而且通过**和=**两者都是相等说明一件事情那就是数据存储的指针地址是一样的。
既然指定exports的对象是默认指向的,那么是否可以如下操作。
// test.js 文件如下写
console.log('test被引用的时候自动执行了')
var name='张三';
//如果着两个改成 module.exports 结果一样 为了减少文章篇幅只演示一个
exports.name= name;
exports.drive=function(){
console.log('开汽车');
}
exports={
age:36
}
//test_main.js
const test= require('./test.js');
console.log(test);
看结果:
似乎什么直接赋值exports一个对象,似乎没有覆盖引入的对象但是改成如下:
然后再如下尝试:
// test.js 文件如下写
console.log('test被引用的时候自动执行了')
var name='张三';
//如果着两个改成 module.exports 结果一样 为了减少文章篇幅只演示一个
exports.name= name;
exports.drive=function(){
console.log('开汽车');
}
module.exports={
age:36
}
//test_main.js
const test= require('./test.js');
console.log(test);
发现返回的结果被覆盖。 这里就可以看出一个了exports和module.exports似乎又不是指向一个东西,这个如何理解?
首先记住一句话:nodejs引用模块的时候,而被引用的对象是module.exports
.
但是为什么exports也可以使用呢?这个就要聊数据的两个种类:基本数据类型
和引用数据类型
,如果两者不太了解可以看一下前面的文章,虽然聊的是隐式转换但是里面涉及到两种数据类型的讲解:传送阵。
exports是为了方便使用简写,但是起本质是等于写了一个类似:exports=module.exports. 这个相当是一个浅拷贝,不理解深拷贝和浅拷贝可以看另一篇文章:传送阵.
而例子的数据可以这样理解:
这个就要记住两点:
require引用的对象是被引入的module.exports对象
module.exports既然对象所以expots默认是指向module.exports默认的地址
注意:如果觉得有点绕,还是不太明白,就看一下我上面放的两个地址,然后再看这个就明白了。
来一个例子具体理解一下(个人建议先不要看结果):
// test.js 文件如下写
console.log('test被引用的时候自动执行了')
var name='张三';
//如果着两个改成 module.exports 结果一样 为了减少文章篇幅只演示一个
exports.name= name;
exports.drive=function(){
console.log('开汽车');
}
module.exports={
age:36
}
exports=module.exports;
exports.address='某市某街道250号';
//test_main.js
const test= require('./test.js');
console.log(test);
本篇就是简单 聊了一些nodejs引用自定义的模块,以及注意的exports和module.exports的区别。后面先聊就是内置的几个模块的使用,然后再聊第三方模块的使用,以及npm是什么东西。