Node.js-json-模块化-全局对象-fs-服务-Buffer
1 node基础知识点
1.1介绍
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,是一个应用程序。
官方网址 https://nodejs.org/en/,中文站 http://nodejs.cn/
node:是一个运行JS的环境。
1.2 作用
- 解析运行 JS 代码
- 操作系统资源,如内存、硬盘、网络
1.3 应用场景
- APP 接口服务 一个接口就是一个地址
- 网页聊天室。
- 动态网站, 个人博客, 论坛, 商城等
- 后端的Web服务,例如服务器端的请求(爬虫),代理请求(跨域)
- 前端项目打包(webpack, gulp)
1.4 相关命令
1 打开 win+r cmd 或者地址输入 cmd回车
2 查看node版本: node -v
进入node环境: node 回车
退出环境: ctr+c
自动查找文件: tab
3 运行JS文件 node 01-my.js:运行当前目录下的 01-my.js
4 清屏:cls
5 显示当前的文件列表 dir
6 切换盘符: c:
7 进入到上级目录: cd..
进入到one文件夹: cd one
注意
在 nodejs 环境下,不能使用 BOM 和 DOM ,也没有全局对象 window,全局对象的名字叫 global
2 json
javascript object notation:对象表示方法,对象标记,数据交换格式
json:javascript object notation js对象的表达方式
json作用:实现前后端之间的通讯所传递的一种数据格式,类似于对象
与对象相同点:json与对象都是有键值对。{} 【】
与对象不同点:
① json属性必须加上双引号
② json最后一个属性结束时,不要加逗号
③ json的属性值不能是函数,NaN,undefined
④ json是前后端通讯时的数据之一,对象是通过类实例化出来的。
[
{
"useName":"xixi",
"age":123
}
]
对象与json是可以相互转换的,创建后缀为.js的文件
const obj = {
a:1,
b:2
}
stringifg:将对象转换为json字符串
const str = JSON.stringify(obj);
console.log(str); //{"a":1,"b":2}
parse:json字符串转换为json对象
const obj2 = JSON.parse(str);
console.log(obj2) //{ a: 1, b: 2 }
3 模块化
3.1 模块化介绍
模块化指的就是将一个大的功能拆分为一个一个小的模块,通过不同的模块的组合来实现一个大功能。
- 在node中一个 js 文件就是一个模块
- 模块内部代码对于外部来说都是不可见的,可以通过两种方式向外部暴露
模块有:内置模块、自定义模块、第三方模块
3.2 引入与导出
模块内对外暴露数据注意以下几点:
1)模块内如果没有暴露数据,引人模块的时候会得到一个空对象。
2)module.exports
可以暴露任意数据。
创建并暴露模块,新建mo.js
const a = 12;
module.exports = a;
导出模块,新建index.js,并在
//mo1.js当中的 module.exports是什么那么mo1就是什么!!
const mo1 = require("./module/mo1.js")
console.log(mo1);//把a的值赋值给mo1 值为12
3.3 引入模块特点
① mo1.js当中的 module.exports是什么那么mo1就是什么
② 后缀.js是可以省略的
注意:node_modules一般存放的是第三方的模块,自定义的模块一般不会放置到该文件夹中,文件名必须叫node_modules。
③ 当js文件不在node_modules文件夹下,是不允许省略前缀的。若没有在当前文件夹找到,则会自动向上上级目录中的 node_modules 查找,一直到根目录。
const mo1 = require("module/mo1");// Cannot find module 'module/mo1'
创建node_modules文件夹,在此文件夹下创建文件mo1.js
const mo1 = require("mo1")
console.log(mo1) //12
④ 当省略.js 会有两种含义:
i 会使用当前文件.js (第一种情况优先被选择)
ii 是一个json文件
iii 会使用当前文件下的index.js
index.js是可以省略不写的。当你只指定文件夹时,默认引入index.js
创建mo2文件夹,在mo2下创建index.js文件,暴露此文件
module.exports = "module->mo->index.js"
引入文件
const mo2 = require("./module/mo2/index")
console.log(mo2) //module->mo->index.js
const mo2 = require("./module/mo2")
console.log(mo2) //module->mo->index.js
⑤ 如果要更改默认值index,将默认值 index.js 指定为 home.js,在package.json中写入home.js
新建mo3文件夹,在文件夹中创建index.js ,home,js , package.json
暴露的文件分别为
module.exports = "module->mo3->index.js"
module.exports = "module->mo3->home.js";
{
"main": "home.js"
}
则引入的文件则更改了默认值
const mo3 = require("./module/mo3")
console.log(mo3) //module->mo3->home.js
⑤ 导入的是对象
const d = 4;
const e = 5;
const f = 6;
module.exports = {
a:1,
b:2,
c:3,
d,
e,
f,
run(){
console.log("run执行啦",this.a,this.b,this.c)
}
}
const mo4 = require("./module/mo4");
console.log(mo4); //{ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, run: [Function: run] }
mo4.run(); //run执行啦 1 2 3
// 使用对象解构赋值
const {a,b,run} = require("./module/mo4");
console.log(a,b);//1 2
run(); //run执行啦 undefined
⑥ 如果直接给module.exports增加属性
module.exports.a = 1;
module.exports.b = 2;
module.exports.c = 3;
module.exports.d = 4;
module.exports.e = 5;
module.exports.run = function () {
console.log(this);
}
const mo5 = require("./module/mo5");
console.log(mo5);
mo5.run(); //{ a: 1, b: 2, c: 3, d: 4, e: 5, run: [Function] }
⑦ 模块化当中,导出时,最终导出的是module.exports。而exports只是module.exports的引用地址相同。
module.exports.a = 1;
module.exports.b = 2;
console.log(module.exports === exports);// true
如果导出对象使用module.exports, 其它 类型可以使用exports。 exports 不要直接导出对象
module.exports = {
c:1000
}
exports = {
c:999
}
console.log(module.exports === exports);// false
⑧ 模块化之间是可以相互依赖
const mo6 = require("../mo6");
exports.str = "mo7->index.js";
exports.a = mo6.a;
const mo7 = require("./module/mo7");
console.log(mo7); //{ str: 'mo7->index.js', a: 1 }
⑨ 可以直接引入json文件
新建文件my.js
exports.str = "json->my.js"
const my = require(".//my.js");// js --> json -->文件夹
console.log(my);// 会将JSON-->JSON对象
4 全局对象 global object
4.1 node 中没有window
node中没有window,document,不支持
4.2 全局对象
node当中每一个JS就是一个模块,每一个模块都有自己的作用域,在该模块当中通过var定义的变量,不会存储在全局对象中(相当于在函数当中定义了变量,这个变量也不会指向window)
global 他是在模块之上的
var a = 1;
console.log(global.a);// undefined
a = 1;
console.log(global.a)
在函数内定义变量a=100,可以输出全局
//暴露
var a = 1;
b = 10000;
console.log(a);
a = 100;
const mo = require("./mo");
console.log(global.b); //10000
console.log(global.a); //1
5 _ ___dirname与____filename
5.1 _dirname
当前js所在的完整目录,结果与node执行时的环境无关。
console.log(__dirname);//完整路径
_dirname并不是全局对象下的属性,而是内置的属性
console.log(global.__dirname);// undefined
5.2 _filename
文件完整的地址,包含文件名字。
console.log(__filename);//本身的名字
_filename是一个绝对地址 ./ …/ …/…/ /
6 fs文件系统
fs 全称为 file system,是 NodeJS 中的内置模块,可以对计算机中的文件进行增删改查等操作。
6.1 读取文件
readFile:第一个参数是读取文件的地址,第二个参数是一个回调函数,读取文件是异步行为。回调函数的第一个参数是错误信息err,第二个参数是读取以后的结果result
第一个参数文件的地址是相对地址,此相对地址相对的不是代码所在的文件,而是相对的node执行环境地址。所以加上_dirname与地址进行拼接。
而模块的地址则是当前代码所在的文件地址,不是node 运行环境地址
新建one.txt文件
const fs = require("fs");
// 第一个参数是读取文件的地址,第二个参数是一个回调函数。读取文件是异步行为。
fs.readFile(__dirname+"/one.txt",function(err,result) {
// console.log(err) //null 表示没有问题
if(err) {
console.log("读取失败",err)//null的布尔值是false
} else {
console.log("领取成功",result.toString()) //领取成功 11111
//不加toString() 输出<Buffer 31 31 31 31 31 0a>
Buffer:是缓冲区。数据还没有自己的格式const fn = require("fs")
}
})
6.1.1 简单读取
- fs.readFile(file, function(err, data){})
- fs.readFileSync(file) 返回值就是读取的内容,写入的返回为undefined
const fs = require("fs");
const result = fs.readFileSync(__dirname+"/my.txt"); //result就是返回的结果
console.log(result.toString());// abc
6.1.2 流式读取
-
fs.createReadStream(); 和流式写入结合使用
两个参数,一个参数为data,end,和回调函数
thunk:数据块 把大的数据分成若干份,分批读取,每次读取64k
const fs = require("fs");
const rs = fs.createReadStream(__dirname+"/123.mp4");
let str = "";
// 将数据分成若干个数据块。
rs.on("data",function (thunk) { // 64k
// console.log("data");
str+=thunk;
console.log(thunk.length);
});
// 读取完毕
rs.on("end",function () {
// console.log("end");
// console.log(str);
})
6.1.3 练习:复制文件
const fs = require("fs");
// 非流式
// fs.readFile(__dirname+"/123.mp4",function (err,result) {
// fs.writeFile(__dirname+"/one.mp4",result,function (err) {
// console.log("成功",err)
// })
// })
// 流式
// 读取流
const rs = fs.createReadStream(__dirname+"/123.mp4");
// 写入流
const ws = fs.createWriteStream(__dirname+"/three.mp4");
// 1
// rs.on("data",function (thunk) {
// ws.write(thunk);
// })
// 简写
rs.pipe(ws);// gulp webpack 管道
6.2 写入文件
writeFile:
第一个参数是写入的文件地址
第二个参数是要写入的内容
第三个参数可以省略,默认**{flag:“w”},可以追加内容{flag:“a”}** ,换行\n
第四个参数是回调函数,当文件写入成功后执行
const fs = require("fs");
fs.writeFile(_dirname+"/one.txt","22222\n",{flag:"a"},function(err) {
console.log("写入成功")
})
6.2.1 简单写入
- fs.writeFile(file, data, [,options], callback);
- fs.writeFileSync(file, data);
- options 选项
encoding
默认值: `‘utf8’flag
默认值:'w'
简单写入特点
① 在当前文件中,如果没有指定文件,那么会创建。然后再写入。
const fs = require("fs")
fs.writeFile(__dirname+"/my.txt","1234",function(err) {
console.log(err) //null 成功写入,自动创建的my文件里就有有1234
})
② 写入时,如果没有文件夹会报异常
③ 设置{flag:“a”}// a:追加 默认是w
④ 设置 encoding 编码格式 ,默认utf-8
fs.writeFile(__dirname+"/my.txt","两岸猿声蹄不住",{flag:"a",encoding:"utf-8"},function (err) {
// err 为 null说明写入成功
console.log(err);
})
⑤ 写入是异步的
fs.writeFile(__dirname+"/my.txt","两岸猿声蹄不住",{flag:"a",encoding:"utf-8"},function (err) {
// err 为 null说明写入成功
console.log(err);
})
console.log("你还好吗?") //先输出你还好吗,再输出null
⑥ 可以同步写入,同步有可能会造成阻塞。
fs.readFile(__dirname+"/123.mp4",function (err,result) {
let t = Date.now();
// 同步
fs.writeFileSync(__dirname+"/my.txt",result);
console.log(Date.now()-t); //0
})
6.2.2 流式写入:文件比较大
- fs.createWriteStream(path[, options])
- path
- options
- flags 默认值:
'w'
encoding
默认值: `‘utf8’
- flags 默认值:
- 事件监听 open close eg: ws.on(‘open’, function(){});
// 数据流。 当数据比较大时,建议使用流式写入。
const fs = require("fs");
const ws = fs.createWriteStream(__dirname+"/my.txt");
ws.on("open",function () {
console.log("打开数据流");
})
ws.write("a");
ws.on("close",function () {
console.log("关闭数据流")
})
ws.write("b"); //写要放到close的上面
ws.write("c")
ws.close();// 关闭数据流
6.3 文件删除:unlink
- fs.unlink(’./test.log’, function(err){}); 异步删除
第一个参数是删除文件的目录,第二个参数是一个回调,用于得到删除之后的结果
fs.unlink(__dirname+"/my1.txt",function (err) {
console.log(err);
})
- fs.unlinkSync(’./move.txt’); 同步删除
fs.unlinkSync(__dirname+"/123.mp4");
6.4 移动文件 + 重命名:rename
- fs.rename(’./1.log’, ‘2.log’, function(err){})
重命名:第一个参数是操作的文件目录,第二个参数是操作的结果
移动:要保证相对应的文件夹是存在的
fs.rename(__dirname+"/one.mp4",__dirname+"/oneone.mp4",function (err) {
console.log(err);
})
移动:要保证相对应的文件夹是存在的,新建a文件夹
fs.rename(__dirname+"/oneone.mp4",__dirname+"/a/a.mp4",function (err) {
console.log(err);
})
- fs.renameSync(‘1.log’,‘2.log’)
同步
fs.renameSync(__dirname+"/a/b/c/a.mp4",__dirname+"/one.mp4");
6.5 文件夹操作
-
mkdir 创建文件夹
第一个参数为目录的地址,一般只能创建一个目录,可使用{recursive:true}递归创建多个文件夹
- path
- options
- recursive 是否递归调用
- callback
fs.mkdir(__dirname+"/b/c/d",{recursive:true},function (err) {
console.log(err);
})
- rmdir 删除文件夹 文件夹必须是空的 如果是要有内容,可使用{recursive:true}递归
fs.rmdir(__dirname+"/b",{recursive:true},function (err) {
console.log(err);
})
- readdir 读取文件夹 目录列表
fs.readdir(__dirname,function (err,result) {
console.log(err,result); })
6.6 判断文件或目录是否存在:exists
· exists
· existsSync 同步
fs.exists(__dirname+"/twi.mp4",function (isHas) {
console.log(isHas)
})
同步判断 · isHas
const isHas = fs.existsSync(__dirname+"/two2.mp4");
console.log(isHas);// false
6.7判断是文件还是文件夹(目录):stat
· stat:
①可输出两个参数,state,isFile() 这是一个布尔值,如果地址是一个文件,就是true
fs.stat(__dirname+"/one.mp4",function (err,state) {
// console.log(err,state);
console.log(state.isFile());// true 是一个布尔值,如果地址是一个文件,那么结果 为true
})
② 如果地址是一个目录,用参数state,isDirectory ,值为布尔值,是的话就为true
fs.stat(__dirname+"/a",function (err,state) {
// console.log(state.isFile());// false
console.log(state.isDirectory());// true 文件是在一个文件夹中
})
7 url
是内置模块,直接引入即可
const url = require("url")
const str = "http://lisi:123456@www.zhangsan.com:8090/a/b/c?userName=zhangsan&age=12#abcdefg"
const result = url.parse(str) //解析地址
// console.log(result)
// 常用
console.log(result.pathname); //站点资源目录 /a/b/c
console.log(result.query) //userName=zhangsan&age=12
// Url {
// protocol: 'http:', 协议
// slashes: true, 包含斜线值为true,否则为false
// auth: null, 访问权限,可设置密码 lisi:123456
// host: 'www.zhangsan.com:8090', 域名+端口号
// port: '8090', 端口号
// hostname: 'www.zhangsan.com', 域名
// hash: '#abcdefg', 片段
// search: '?userName=zhangsan&age=12', 带?的查询
// query: 'userName=zhangsan&age=12', 不带?的查询
// pathname: '/a/b/c', 站点资源目录 url
// path: '/a/b/c?userName=zhangsan&age=12', 资源目录+查询
// href: 'http://www.zhangsan.com:8090/a/b/c?userName=zhangsan&age=12#abcdefg' 完整地址
// }
将字符串转为对象,用split分割遍历。或者直接parse(str,true)
有true就是对象格式,没有true,是字符串格式
console.log(url.parse(str,true).query)
//[Object: null prototype] { userName: 'zhangsan', age: '12' }
// 原来的方法
const path = "userName=zhangsan&age=12";
const arr = path.split("&");
const obj = {};
arr.forEach(item=>{
const [key,value] = item.split("=");// [userName,zhangsan]
obj[key] = value
})
console.log(obj);
简单的例子
const url = require("url");
const str = "/a/b/c?a=1&b=2";
console.log(url.parse(str).pathname);// /a/b/c
console.log(url.parse(str,true).query);// { a: '1', b: '2' }
http默认端口号80
https默认端口号443
输出出现两次,浏览器请求了两次,第一次网址,第二次站标
8 创建服务
createServer:是一个函数,通过该函数可以创建你的站点服务
request:请求对象,所有与请求相关的信息,简写req
response:响应对象,与响应相关的信息,简写res
listen是一个函数,接收三个参数:
① 第一个参数是端口号(必写)
② 第二个参数是指定IP,可以不写,本机默认的IP
③ 第三个参数是指定回调函数,该回调函数会在服务创建成功后执行
注意:
① 代码修改之后,一定一定要重新启动服务
② 服务是可以开启多个的,但是端口号不允许重复(listen EADDRINUSE: address already in use 127.0.0.1:8090: 说明端口号被占用)
③ server.js中的代码不是在浏览器中运行的,而是在NODE环境中执行的
④ node 是没有文件夹容器概念
const http = require("http");
const server = http.createServer(function(req,res) {
console.log(1)
res.end ("成功显示")
})
server.listen(8091,"127.0.0.1",function() {
console.log("success")
})
在地址栏中请求数据,叫get
res.end只支持字符串和buffer
9 Buffer
数据流:指的是对文件进行读写时产生的。A---->B. fs.readFile 流式读取,流式写入
Buffer:fs.readFile()-------->abcdefg <Buffer ox ox
const fs = require("fs")
fs.readFile = (_dirname+"/my/txt",function(err,result) {
//将内容以十六进制的形式进行展现,存储的还是二进制
console.log(result)
})
//针对JS而言,字符是数字表示,计算器会将这些数字转换成二进制
const str = "b";
console.log(str.charCodeAt()) //98 unicode
9.1 Bufferr 介绍
Buffer 是一个和数组类似的对象,不同是 Buffer 是专门用来保存二进制数据的。
特点:
- 大小固定:在创建时就确定了,且无法调整
- 性能较好:直接对计算机的内存进行操作
- 每个元素大小为 1 字节(byte)
9.2 操作
9.2.1 创建 Buffer
-
直接创建
Buffer.alloc()
指定两个参数,第一个参数为字节的个数,第二个参数为初始值const b1 = Buffer.alloc(5,10) console.log(b1) //<Buffer 0a 0a 0a 0a 0a>
-
不安全创建
Buffer.allocUnsafe()
与alloc的区别:allocUnsafe()没有初始值,会使用上一应用程序的遗留数据。速度快
const b3 = Buffer.allocUnsafe(5);
console.log(b3); //<Buffer 3d 00 20 00 31>
- 通过数组和字符串创建
Buffer.from()
只允许写入字符和数组
得到对应的十六进制
const b4 =Buffer.from("45");// <Buffer 34 35>
console.log(b4) //与图对应,十六进制
数组中写字符得到的都是0
const b7 = Buffer.from(["a","b","*"]);// 字符全部都是0.
console.log(b7);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKElzJh2-1621071679909)(C:\Users\王秀\AppData\Roaming\Typora\typora-user-images\image-20210511183528278.png)]
9.2 Buffer 读取和写入
可以直接通过 []
的方式对数据进行处理,可以使用 toString 方法将 Buffer 输出为字符串
- [ ] 对 buffer 进行读取和设置
- toString 将 Buffer 转化为字符串
const b1 = Buffer.from("iloveyou");
console.log(b1);// <Buffer 69 6c 6f 76 65 79 6f 75>十六进制
console.log(b1[0]);// 105 十进制
console.log(b1[1]);//108
b1[0]= 35;
console.log(b1.toString());// buffer转为字符串 #loveyou
9.3 关于溢出
溢出的高位数据会舍弃 保留后位
const b1 = Buffer.alloc(5,256);//十进制转为十六进制为100 ,取后面两位
console.log(b1); //<Buffer 00 00 00 00 00>
9.4 关于中文
一个 UTF-8 的中文字符大多数情况都是占 3 个字节
const b1 = Buffer.from("中国");
console.log(b1); //<Buffer e4 b8 ad e5 9b bd>
9.5 关于单位换算
1Bit 对应的是 1 个二进制位
1 字节 = 8 Bit(byte) 11111111=>255
十进制转二进制 12 除2倒取余=1100
二进制 1100 转十进制 1×2的3次方+1×2的2次方
1024Byte = 1KB
1024KB = 1MB
1024MB = 1GB
1024GB = 1TB
-KKElzJh2-1621071679909)]
9.2 Buffer 读取和写入
可以直接通过 []
的方式对数据进行处理,可以使用 toString 方法将 Buffer 输出为字符串
- [ ] 对 buffer 进行读取和设置
- toString 将 Buffer 转化为字符串
const b1 = Buffer.from("iloveyou");
console.log(b1);// <Buffer 69 6c 6f 76 65 79 6f 75>十六进制
console.log(b1[0]);// 105 十进制
console.log(b1[1]);//108
b1[0]= 35;
console.log(b1.toString());// buffer转为字符串 #loveyou
9.3 关于溢出
溢出的高位数据会舍弃 保留后位
const b1 = Buffer.alloc(5,256);//十进制转为十六进制为100 ,取后面两位
console.log(b1); //<Buffer 00 00 00 00 00>
9.4 关于中文
一个 UTF-8 的中文字符大多数情况都是占 3 个字节
const b1 = Buffer.from("中国");
console.log(b1); //<Buffer e4 b8 ad e5 9b bd>
9.5 关于单位换算
1Bit 对应的是 1 个二进制位
1 字节 = 8 Bit(byte) 11111111=>255
十进制转二进制 12 除2倒取余=1100
二进制 1100 转十进制 1×2的3次方+1×2的2次方
1024Byte = 1KB
1024KB = 1MB
1024MB = 1GB
1024GB = 1TB
平时所说的网速 10M 20M 100M 这里指的是 Bit ,所以理论下载速度 除以 8 才是正常的理解的下载速度