Node.js学习笔记----day03
认真学习,认真记录,每天都要有进步呀!!!
加油叭!!!
一、Node中的模块系统
使用Node编写应用程序主要就是在使用
- EcmaScript
- 和浏览器不一样的是,在Node中没有BOM、DOM
- 核心模块
- 文件操作的fs
- http服务的http
- url路径操作模块
- os操作系统信息
- 第三方模块
- art-template
- 必须通过npm来下载才可以使用
- 自己创建的模块
- 自己创建的文件
二、什么是模块化
- 具有文件作用域(没有污染)
- 具有通信规则(即可以加载又可以导出)
三、CommonJS模块化
在Node中的JavaScript还有一个很重要的概念:模块系统
- 模块作用域
与浏览器相比不是全局作用域,模块作用域,默认全局当中的所有成员不能被外部访问 - 使用
require()方法来加载模块
没有script标签,不能通过script标签来加载文件 - 使用
exports接口对象来导出模块中的成员
四、加载与导出
- 加载
require()
语法:
var 自定义变量名称 = require('模块')
require 方法有两个作用:
(1) 加载文件模块并执行里面的代码
(2)得到被加载文件模块导出的接口对象
- 导出
exports()
Node中是模块作用域,默认文件中所有的成员只在当前文件模块有效
对于希望可以被其他模块访问的成员,我们就需要把这些公开的成员都挂载到exports接口中就可以了
在每个文件模块中都提供了一个对象:exports
exports 默认是一个空对象
你要做的就是把所有需要被外部访问的成员挂载到这个 exports 对象中
2.1 导出多个成员
exports.age = 123
exports.hi= 'hello'
exports.func = function(){
console.log('aaa')
}
exports.message={
name:'zs'
}
也可以这样来导出多个成员
module.exports = {
add = function(){
return x + y
},
str:'hello'
}
2.2 导出单个成员,拿到的就是函数,字符串
module.exports = 'hello'
以下情况会覆盖
module.exports = 'hello'
//以这个为准,后者会覆盖前者
module.exports = function(x,y){
return x + y
}
五、exports与module.exports的原理解析
在 Node 中,每个模块内部都有一个自己的 module 对象
该 module 对象中,有一个成员叫:exports
var module = {
exports: {
对象名:'对象内容',
方法名:function
}
}
//模块中还有一句
var exports = module exports
//默认在代码最后一句
//谁来require我,谁就得到module exports
return module exports
也就是说如果你需要对外导出成员,只需要把导出的成员挂载到
module.exports中
main.js
var fooExports = require('./foo')
console.log(fooExports)
foo.js
module.exports.test1 = 'hello'
module.exports.test2 = 'world'
module.exports.add = function (x, y) {
return x + y
}
module.exports.reduce = function (x, y) {
return x - y
}
foo.js相当于
var module = {
exports: {
test1: 'hello',
test2: 'world',
add: function,
reduce:function,
}
}
执行main.js结果:

再添加一些exports对象与方法
foo.js
module.exports.test1 = 'hello'
module.exports.test2 = 'world'
exports.test3 = 'hi'
exports.test4 = 'node.js'
module.exports.add = function (x, y) {
return x + y
}
module.exports.reduce = function (x, y) {
return x - y
}
exports.a = function(a,b){
return a + b
}
exports.b = function(a,b){
return a + b
}
结果:

我们发现,
exports == module.exports,两者一致,说明可以使用任意一方来导出接口成员
每次导出接口成员的时候都通过module.exports.xxx = xxx的方式很麻烦,点儿的太多了
所以,Node 为了简化操作,专门提供了一个变量:exports 等于 module.exports
exports相当于 module.exports的一个引用
console.log(exports == module.exports) // true
exports.foo = 'bar'
//等价于
module.exports.foo = 'bar'
也就是说在模块中还有这么一句代码
var exports = module.exports
当一个模块需要导出单个成员的时候
直接给 exports 赋值是不管用的
栗子:
exports = 'hello'
是个空对象,什么都没有

一定要记住,最后 return 的是 module.exports
不是 exports
所以你给 exports 重新赋值不管用,
return module.exports
对比去理解的栗子:

没有给exports 重新赋值之前
栗子:
exports.a = 123
// exports = {}
exports.foo = 'bar'

给exports 重新赋值后
栗子:
exports.a = 123
exports = {}
exports.foo = 'bar'
给 exports 赋值会断开和 module.exports 之间的引用,重新赋值以后,跟原来没有关系,后面添加都不管用

exports的修改不影响module.export
栗子:
exports.a = 123
exports = {}
exports.foo = 'bar'
module.exports.b = 456
//module.export一直都没有变,所有可以导出 b:456
//exports的修改不影响module.export

module.exports 重新赋值会断开与exports的引用
栗子:
module.exports = 'hello'
exports.a = '123'

总结:
给 exports 赋值会断开和 module.exports 之间的引用
同理,给 module.exports 重新赋值也会断开
栗子:
// 这里导致 exports !== module.exports
module.exports = {
foo: 'bar'
}
// // 但是这里又重新建立两者的引用关系
exports = module.exports
exports.foo = 'hello'
结果:

栗子:
//{foo: bar}
exports.foo = 'bar'
// {foo: bar, a: 123}
module.exports.a = 123
// exports !== module.exports
// 最终 return 的是 module.exports
// 所以无论你 exports 中的成员是什么都没用
exports = {
a: 456
}
// {foo: 'haha', a: 123}
module.exports.foo = 'haha'
// 没关系,混淆你的
exports.c = 456
// 重新建立了和 module.exports 之间的引用关系了
exports = module.exports
// 由于在上面建立了引用关系,所以这里是生效的
// {foo: 'haha', a: 789}
exports.a = 789
最终结果:

再加上一句:
// // 前面再牛逼,在这里都全部推翻了,重新赋值
// // 最终得到的是 Function
module.exports = function () {
console.log('hello')
}
结果:

归纳总结:
真正去使用的时候:
导出多个成员:exports.xxx = xxx
导出多个成员也可以:module.exports = { }
导出单个成员:module.exports
六、总结exports与module.exports的区别
- 每个模块中都有一个 module 对象
- module 对象中有一个 exports 对象
- 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
- 也就是:
moudle.exports.xxx = xxx的方式 - 但是每次都
moudle.exports.xxx = xxx很麻烦,点儿的太多了 - 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:
exports exports === module.exports结果为trues- 所以对于:
moudle.exports.xxx = xxx的方式 完全可以:expots.xxx = xxx - 当一个模块需要导出单个成员的时候,这个时候必须使用:
module.exports = xxx的方式 - 不要使用
exports = xxx不管用 - 因为每个模块最终向外
return的是module.exports - 而
exports只是module.exports的一个引用 - 所以即便你为
exports = xx重新赋值,也不会影响module.exports - 但是有一种赋值方式比较特殊:
exports = module.exports这个用来重新建立引用关系的
七、require方法加载规则
- 优先从缓存加载

结果:

- 判断模块标识
- 核心模块
核心模块的本质也是文件
核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了
eg:
require('fs')
require('http')
- 路径形式的文件模块(自定义模块)
路径形式的模块:
./当前目录,不可省略
../上一级目录,不可省略
/xxx几乎不用
d:/a/foo.js几乎不用
首位的 / 在这里表示的是当前文件模块所属磁盘根路径
.js后缀名可以省略
require('./foo')相当于require('./foo.js')
- 第三方模块
凡是第三方模块都必须通过 npm 来下载
使用的时候就可以通过require('包名')的方式来进行加载才可以使用
不可能有任何一个第三方包和核心模块的名字是一样的
第三方模块既不是核心模块、也不是路径形式的模块
先找到当前文件所处目录中的node_modules目录
node_modules/art-template
node_modules/art-template/package.json文件
node_modules/art-template/package.json文件中的main属性
main属性中就记录了art-template的入口模块
然后加载使用这个第三方包
实际上最终加载的还是文件
如果
package.json文件不存在或者main指定的入口模块是也没有
则node会自动找该目录下的index.js
也就是说index.js会作为一个默认备选项
如果以上所有任何一个条件都不成立,则会进入上一级目录中的
node_modules目录查找
如果上一级还没有,则继续往上上一级查找
。。。
如果直到当前磁盘根目录还找不到,最后报错:
can not find module xxx
var template = require('art-template')
注意:我们一个项目有且只有一个
node_modules,放在项目根目录中,这样的话项目中所有的子目录中的代码都可以加载到第三方包
不会出现有多个node_modules
总结模块查找机制
- 优先从缓存加载
- 核心模块
- 路径形式的文件模块
- 第三方模块
node_modules/art-template/
node_modules/art-template/package.json
node_modules/art-template/package.json main
index.js备选项
进入上一级目录找node_modules
按照这个规则依次往上找,直到磁盘根目录还找不到,最后报错:Can not find moudle xxx
一个项目有且仅有一个node_modules而且是存放到项目的根目录
参考详细文档: 深入Node.js的模块机制
八、npm
node package manager
- npm 网站 : https://www.npmjs.com/
可以用来搜索第三方包

- npm命令行工具
npm的第二层含义就是命令行工具,只要你安装了node 就相当于安装了npm
npm 也有版本的这个概念
查看npm版本
npm --version
升级npm版本
npm install --global npm
- npm 常用命令
详细参考文档:https://www.cnblogs.com/PeunZhang/p/5553574.html - 解决npm被墙的问题
参考文档:https://blog.csdn.net/qq_45103612/article/details/108569090
九、package.json
建议每一个项目都要有一个package.json (包描述文件,就像产品说明书一样)
这个文件可以通过npm init的方式初始化出来


- 建议每个项目的根目录中都有一个
package.json文件 - 建议执行
npm install 包名的时候都加上--save这个选项,目的是用来保存依赖项信息 - 可以在
package.json文件中通过dependencies选项,查看第三方包的依赖信息 - 如果项目中的
node_modules项目丢失了,只要package.json文件存在,我们只需要npm install,就会自动把package.json中的dependencies中的所有依赖项都下载回来
十、package.json与package-lock.json
npm5以前是不会有package.json这个文件的
npm5以后才加入了这个文件
当你安装包以后,npm都会自动安装package-lock.json这个文件
- npm5以后的版本安装包不需要加上
--save参数,它会自动保存依赖信息 - 当你安装包的时候,会自动创建或更新
package-lock.json这个文件 package-lock.json会保存node_modules中所有包的信息(版本,下载地址)- 这样的话重新
npm install的速度就会提升很多 - 从文件来看,有一个
lock称之为锁 - 这个
lock是用来锁定版本的 - 如果这个项目依赖了1.1.1版本
- 如果你
npm install其实会下载最新版,而不是1.1.1 - 所以
package-loca.json的另外一个作用就是锁定版本号,防止自动升级新版
Node.js学习笔记----day03的更多相关文章
- 一点感悟:《Node.js学习笔记》star数突破1000+
写作背景 笔者前年开始撰写的<Node.js学习笔记> github star 数突破了1000,算是个里程碑吧. 从第一次提交(2016.11.03)到现在,1年半过去了.突然有些感慨, ...
- Node.js学习笔记(3):NPM简明教程
Node.js学习笔记(3):NPM简明教程 NPM常用操作 更新NPM版本 npm install npm -g -g,表示全局安装.我们可以指定更新版本,只需要在后面填上@版本号即可,也可以输入@ ...
- 系列文章--Node.js学习笔记系列
Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- 简介及安装Node.js开发环境 Nodejs学习笔记(二)--- 事件模块 Nodejs学习笔记(三)--- 模块 Nodejs学 ...
- Node.js学习笔记(2):基本模块
Node.js学习笔记(2):基本模块 模块 引入模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在No ...
- Node.js学习笔记(1):Node.js快速开始
Node.js学习笔记(1):Node.js快速开始 Node.js的安装 下载 官方网址:https://nodejs.org/en/ 说明: 在Windows上安装时务必选择全部组件,包括勾选Ad ...
- Node.js学习笔记(4):Yarn简明教程
Node.js学习笔记(4):Yarn简明教程. 引入Yarn NPM是常用的包管理工具,现在我们引入是新一代的包管理工具Yarn.其具有快速.安全.可靠的特点. 安装方式 使用npm工具安装yarn ...
- Node.js学习笔记(一)
1.回调函数 node是一个异步事件驱动的平台,所以在代码中我们经常需要使用回调函数. 例: setTimeout(function(){ console.log('callback is calle ...
- Node.js学习笔记(一):快速开始
最近接了一个node项目,虽然最后顺利完成了,但是由于第一次实战,整个过程是赶出来的,许多地方一知半解.现在项目结束了,就静下心来系统地学一学,理一理,读书不忘拿笔,既然读书了,当然就要记点东西.一方 ...
- Node.js学习笔记(一)基础介绍
什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...
- node.js学习笔记(三)——事件循环
要理解事件循环,首先要理解事件驱动编程(Event Driven Programming).它出现在1960年.如今,事件驱动编程在UI编程中大量使用.JavaScript的一个主要用途是与DOM交互 ...
随机推荐
- Python基础部分:11、文件和光标移动
目录 一.文件操作 1.文件的概念 2.代码打开文件的方式 二.文件读写模式 1.'r' 只读模式 read 2.'w' 只写模式 write 3.'a' 尾部追写模式 add 三.文件操作模式 1. ...
- python中的浅拷贝,深拷贝
直接引用,间接引用 # 1.列表存储的是索引对应值的内存地址,值会单独的开辟一个内存空间 list = ["a","b"] 内存里面存储的就是list[0],l ...
- CSS动画-transition/animation
HTML系列: 人人都懂的HTML基础知识-HTML教程(1) HTML元素大全(1) HTML元素大全(2)-表单 CSS系列: CSS基础知识筑基 常用CSS样式属性 CSS选择器大全48式 CS ...
- Vue3 —— 组件练习题(附源码)
一.定义一个vue分页组件,实现客户端分页功能 1.1.子组件A(页数按钮) <!-- 本组件用于遍历分页的页数按钮 --> <template lang=""& ...
- 833——B题题解
题目链接 题目大意: 给一串字符串(只包含0~9),定义一个最优子串的定义:如果该子串同字符种类数大于最最多字符出现数,那么这个子串可以被称为最优子串. 解题思路: 大眼一看这个数据范围他用n^2的算 ...
- <一>继承的基本意义
1:继承的本质和原理 2:派生类的构造过程 3:重载,覆盖,隐藏 4:静态绑定和动态绑定 5:多态,vfptr,vftable 6:抽象类的设计原理 7:多重继承以及问题 8:虚基类 vbptr 和v ...
- 关于python转义字符在正则匹配中的问题研究
问题 首先看一个问题: import re text = r"\学" print(text) zz = r"\学" result = re.findall(zz ...
- PHP日期加减计算
PHP 标准的日期格式 date("Y-m-d H:i:s"); PHP 简单的日期加减计算 <?php date_default_timezone_set('PRC'); ...
- 【PostgreSQL】PG读取元数据获取表结构及字段类型信息(过程拆解及其他应用场景)
〇.参考链接 一.代码 指定模式的表名和字段 select c.relname 表名, cast ( obj_description (relfilenode, 'pg_class') as varc ...
- 4.10:Spark之wordcount
〇.概述 1.拓扑结构 2.目标 使用spark完成计数实验 一.启动环境 二.新建数据文件 三.查看文件内容 四.启动spark服务 五.编写代码 复制以下代码到shell中(复制后在终端右键-&g ...