1. Node.Js 环境概述
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,用于在服务器端运行 JavaScript。它使用了一个事件驱动、非阻塞式I/O的模型,使得其轻量且高效。Node.js 的包管理器 npm 是全球最大的开源库生态系统。Node.js 能够响应大量的并发请求,适合运用在高并发、I/O密集、少量业务逻辑的场景。
1.1 安装 NodeJs 环境
NodeJs 下载路径:https://nodejs.org/en
安装完成后可以在命令行中输入 node -v 和 npm -v 检查是否安装成功?
1.2 globalThis 和 global 变量
Node.js 中不能使用 BOM 和 DOM 的 API,可以使用 console 和定时器 API。
Node.js 中的顶级对象是 global,也可以用 globalThis 访问顶级对象。
<ref *1> Object [global] {
global: [Circular *1],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
performance: Performance {
nodeTiming: PerformanceNodeTiming {
name: 'node',
entryType: 'node',
startTime: 0,
duration: 36.77699999511242,
nodeStart: 0.7232999950647354,
v8Start: 2.644999995827675,
bootstrapComplete: 26.97859999537468,
environment: 13.895999997854233,
loopStart: -1,
loopExit: -1,
idleTime: 0
},
timeOrigin: 1698760750401.373
},
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
}
}
console.log(globalThis === global) // true
2. Buffer 缓冲区
Buffer 中文译为『缓冲区』,是一个类似于 Array 的对象,用于表示固定长度的字节序列。换句话说,Buffer 就是一段固定长度的内存空间,用于处理二进制数据。
2.1 alloc() 和 allocUnsafe()
alloc
: 这是 Node.js 中的一个函数,用于在堆中分配内存。它通常用于分配一块具有确定大小的连续内存块。node alloc
函数确保了分配的内存是初始化的,也就是说,分配的内存区域都被初始化为零。这使得它非常适合分配需要清零的内存,例如用于密码学目的的内存。然而,由于它涉及到清零操作,node alloc
的性能可能会略低于 allocUnsafe
。
allocUnsafe
: 这是 V8 JavaScript 引擎(Node.js 的默认 JavaScript 引擎)中的一个函数。它用于在 V8 的堆内存中分配内存,不需要进行初始化。这意味着分配的内存区域可能包含以前分配并释放的垃圾数据。因此,使用 allocUnsafe
分配的内存可能存在数据泄露的风险,特别是在处理敏感数据时。然而,由于不需要进行初始化操作,allocUnsafe
的性能通常会比 node alloc
高。
node alloc
和 allocUnsafe
都是用于分配内存的函数,但它们的使用和安全性有所不同。node alloc
提供了初始化的内存分配,适合需要清零的场景,但性能略低。而 allocUnsafe
则提供了未初始化的内存分配,性能较高,但可能存在数据泄露的风险。选择使用哪个函数取决于你的具体需求和安全性要求。
let buf = Buffer.alloc(10);
console.log(buf)
// <Buffer 00 00 00 00 00 00 00 00 00 00>
let buf_2 = Buffer.allocUnsafe(10000);
console.log(buf_2)
// <Buffer 00 06 f5 ff 5b 02 00 00 70 a9 35 f6 5b 02 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ... 9950 more bytes>
2.2 Buffer.from() 创建 Buffer 实例
let buf = Buffer.from("i love you")
console.log(buf)
// <Buffer 69 20 6c 6f 76 65 20 79 6f 75>
Buffer.from()
是 Node.js 的 Buffer
类的一个静态方法,用于创建一个新的 Buffer 实例从指定的 ArrayBuffer,或者在给定的类型数组中。通过
Buffer.from(arrayBuffer[, byteOffset[, length]])
param | detail | default |
---|---|---|
arrayBuffer | 一个 ArrayBuffer 或共享 ArrayBuffer 对象,将创建 Buffer 对象表示的原始二进制数据 | |
byteOffset (可选) | 从 ArrayBuffer 中开始读取数据的偏移量(以字节为单位) | 默认值为 0 |
length (可选) | 要读取的字节数 | 默认值为 arrayBuffer.length - byteOffset |
let buf1 = Buffer.from(new ArrayBuffer(10));
let buf2 = Buffer.from(new ArrayBuffer(10), 2);
// 从偏移 2 开始的数据
let buf3 = Buffer.from(new ArrayBuffer(10), 2, 5);
// 从偏移 2 开始读取 5 个字节的数据
<Buffer 69 20 6c 6f 76 65 20 79 6f 75> 8 bit 8 位存储,两个 16 进制数,00 ~ ff(0 ~ 255)
let buf = Buffer.from("i love you")
buf.forEach(element => {
console.log(element)
// 105 32 108 111 118 101 32 121 111 117
});
let buff = Buffer.from([105, 32, 108, 111, 118, 101, 32, 121, 111, 117])
console.log(buff.toString())
// i love you
2.3 UTF-8 编码中文字符 buffer
let buf = Buffer.from("你好")
console.log(buf)
// <Buffer e4 bd a0 e5 a5 bd>
Buffer类在 Node.js 中用于处理二进制数据,其本身并不直接支持 UTF-8 编码。但是,你可以使用Buffer类来存储和操作 UTF-8 编码的字符串。
例如,如果你有一个 UTF-8 编码的字符串,你可以使用Buffer类的构造函数创建一个Buffer对象:
let buf = Buffer.from('你好,世界!', 'utf-8');
在这个例子中,‘你好,世界!’ 是 UTF-8 编码的字符串,Buffer.from 方法用于创建一个新的 Buffer 对象。第二个参数 ‘utf-8’ 指定了字符串的编码格式。
你也可以使用 Buffer.allocUnsafe() 或 Buffer.from() 来创建一个新的 Buffer 对象,并通过 toString() 方法将 Buffer 对象转换为 UTF-8 编码的字符串:
let buf = Buffer.allocUnsafe(13);
buf.fill('你好,世界!', 0, 13);
let str = buf.toString('utf-8');
console.log(str); // '你好,世界!'
在这个例子中,我们使用 Buffer.allocUnsafe() 创建了一个新的 Buffer 对象,并使用 fill() 方法将字符串填充到 Buffer 中。然后,我们使用 toString() 方法将 Buffer 对象转换为 UTF-8 编码的字符串。
3. fs 模块读写文件
fs 是 Node.js 的一个内置模块,它是 Node.js 文件系统模块,用于在服务器端操作文件。它提供了一系列的方法和属性,可以满足用户对文件的操作需求。例如,可以读取文件、写入文件、删除文件等。
要在 JavaScript 代码中使用 fs 模块来操作文件,需要先导入 fs 模块。可以使用 const fs = require(‘fs’); 来导入该模块。
3.1 fs 模块常用方法和属性
fs.readFile(path[, options], callback)
:异步地读取文件的内容。path 是文件路径,options 可选,通常不需要指定,callback 是回调函数,它会在文件读取完成后被调用,并包含两个参数:err(错误对象)和 data(文件内容)。
fs.writeFile(path, data[, options], callback)
:异步地将数据写入文件。path 是文件路径,data 是要写入的数据,options 可选,通常不需要指定,callback 是回调函数,它会在文件写入完成后被调用,并包含两个参数:err(错误对象)和 data(写入的数据)。
fs.unlink(path, callback)
:异步地删除文件。path 是要删除的文件路径,callback 是回调函数,它会在文件删除完成后被调用,并包含一个参数:err(错误对象)。
fs.mkdir(path[, options], callback)
:异步地创建目录。path 是要创建的目录路径,options 可选,通常不需要指定,callback 是回调函数,它会在目录创建完成后被调用,并包含一个参数:err(错误对象)。
fs.rename(oldPath, newPath, callback)
:异步地重命名文件或目录。oldPath 是要重命名的文件或目录的路径,newPath 是新的文件或目录的路径,callback 是回调函数,它会在重命名完成后被调用,并包含一个参数:err(错误对象)。
fs.readdir(path[, options], callback)
:异步地读取目录的内容。path 是目录路径,options 可选,通常不需要指定,callback 是回调函数,它会在目录读取完成后被调用,并包含两个参数:err(错误对象)和 files(目录中的文件名列表)。
fs.rmdir(path, callback)
:异步地删除目录。path 是要删除的目录路径,callback 是回调函数,它会在目录删除完成后被调用,并包含一个参数:err(错误对象)。
请注意,这些方法都是异步的,意味着它们不会立即完成。它们通常在回调函数中提供结果,而不是在返回值中提供。因此,需要使用回调函数来处理异步操作的结果。
3.2 fs 流式读写 和 普通读写
const fs = require("fs")
const ws = fs.createWriteStream("helloworld.txt")
ws.write("hello world")
ws.end()
Node.Js 流式读写和普通读写的应用场景
普通读写:适用于小数据量、单次读写操作。例如,读取文件内容并在控制台输出,或者将数据写入文件。
const fs = require("fs")
fs.readFile("helloworld.txt", (err, data) => {
if (err) {
console.log(err)
} else {
console.log(data.toString())
}
})
流式读写:适用于处理大量数据、高并发场景。例如,从网络中读取数据,或者将数据写入到网络中。在流式读写中,数据是按块读取或写入的,而不是一次性加载到内存中,这使得它能够处理大量数据,同时减少内存消耗。
const fs = require("fs")
const rs = fs.createReadStream("helloworld.txt")
rs.on('data', chunk => {
console.log(chunk.toString())
})
3.3 fs 流式拷贝 和 普通拷贝
在Node.js中,文件系统(fs)模块提供了流式拷贝和普通拷贝两种方式。
普通拷贝(fs.readFile和fs.writeFile)是同步的,它们在执行期间会阻塞其他操作,直到整个文件被读取或写入完毕。这种方式适用于小型文件的拷贝,因为它相对简单且易于理解。但是,对于大型文件,普通拷贝可能会导致性能问题,因为它需要一次性将整个文件读入内存或写入磁盘。
流式拷贝(fs.createReadStream和fs.createWriteStream)则是异步的,它们使用了流(Stream)的概念。流是一种可以用于读取或写入数据的通道,它们可以以较小的数据块为单位进行读写,而不需要一次性读取整个文件。这种方式适用于大型文件的拷贝,因为它可以避免一次性读入整个文件而导致的内存占用问题。
流式拷贝
const fs = require('fs')
const process = require('process')
const rs = fs.createReadStream("assets/test.mp4")
const ws = fs.createWriteStream("test_new.mp4")
rs.on("data", chunk => {
console.log(chunk)
ws.write(chunk)
// 65486 more bytes
})
rs.on('end', () => {
console.log(process.memoryUsage())
})
process.memoryUsage() 是 Node.js 中的一个函数,用于返回当前 Node.js 进程的内存使用情况。这个函数返回一个对象,其中包含了 Node.js 进程使用的各种内存资源的使用情况。这些值可以帮助你了解你的 Node.js 进程的内存使用情况,以便于优化你的代码或诊断内存泄漏等问题。
返回的对象属性
obj | detail |
---|---|
rss | Resident Set Size,这是进程在主内存中(即 RAM)占用的空间量,以字节为单位。 |
heapTotal | V8 引擎已申请的堆内存总量,以字节为单位。 |
heapUsed | V8 引擎已使用的堆内存量,以字节为单位。 |
external | 进程使用的外部内存,以字节为单位。 |
{
rss: 20463616, // 20463616 / 1024 / 1024 = 19 MB
heapTotal: 4866048,
heapUsed: 4144648,
external: 281354,
arrayBuffers: 11146
}
普通拷贝示例
const fs = require('fs');
fs.readFile('source.txt', 'utf8', (err, data) => {
if (err) throw err;
fs.writeFile('destination.txt', data, (err) => {
if (err) throw err;
console.log('File has been saved!');
});
});
3.4 fs stat() 状态信息
const fs = require("fs")
fs.stat("test_new.mp4", (err, stat) => {
if (err) {
console.log(err)
} else {
console.log(stat)
console.log(stat.isFile())
}
})
obj method | return value type |
---|---|
stats.size | 文件大小,以字节为单位。 |
stats.mtime | 文件修改时间,是一个 Date 对象。 |
stats.ctime | 文件创建时间,是一个 Date 对象。 |
stats.atime | 文件访问时间,是一个 Date 对象。 |
stats.birthtime | 文件的出生时间,是一个 Date 对象(在某些系统上可能不可用)。 |
stats.uid | 文件的用户 ID。 |
stats.gid | 文件的组 ID。 |
stats.mode | 文件的权限模式。 |
stats.ino | 文件的 inode 号码。 |
stats.dev | 文件的设备号码。 |
stats.nlink | 文件的硬链接数量。 |
stats.isFile() | 如果这是一个文件,返回 true。 |
stats.isDirectory() | 如果这是一个目录,返回 true。 |
stats.isBlockDevice() | 如果这是一个块设备,返回 true。 |
stats.isCharacterDevice() | 如果这是一个字符设备,返回 true。 |
stats.isSymbolicLink() | 如果这是一个符号链接,返回 true(只在 Unix 系统中有效)。 |
stats.isFIFO() | 如果这是一个 FIFO(命名管道),返回 true。 |
stats.isSocket() | 如果这是一个套接字,返回 true。 |
Stats {
dev: 810007100,
mode: 33206,
nlink: 1,
uid: 0,
gid: 0,
rdev: 0,
blksize: 4096,
ino: 1407374883944336,
size: 57077833,
blocks: 111488,
atimeMs: 1698839970416.1738,
mtimeMs: 1698839970416.1738,
ctimeMs: 1698839970416.1738,
birthtimeMs: 1698839007835.6604,
atime: 2023-11-01T11:59:30.416Z,
mtime: 2023-11-01T11:59:30.416Z,
ctime: 2023-11-01T11:59:30.416Z,
birthtime: 2023-11-01T11:43:27.836Z
}
3.5 __dirname 执行路径
在Node.js中,__dirname
是一个全局变量,表示当前正在执行的脚本所在的目录路径。它是一个字符串(String)类型的值,包含了当前脚本所在的目录路径。
这个变量在Node.js中非常有用,因为它可以帮助你在脚本中引用当前目录或子目录中的文件或模块。通过使用__dirname
变量,你可以构建相对路径来引用其他文件或模块,或者执行一些与当前目录相关的操作。
console.log(__dirname)
4. http 模块网络编程
HTTP(Hypertext Transfer Protocol)是一种用于在网络上传输数据的协议,它定义了客户端与服务器之间的通信规则。HTTP协议基于请求/响应模型,客户端向服务器发送请求,服务器响应请求并返回数据。HTTP协议使用明文的方式传输内容,因此不适合传输敏感信息。HTTP协议的版本主要有HTTP/1.0和HTTP/1.1,其中HTTP/1.1是目前最常用的版本。
HTTP协议的特点是简单、灵活、无连接和无状态。简单是指协议的结构简单,易于理解和实现;灵活是指协议具有良好的扩展性,可以在不改变核心协议的情况下添加新的功能;无连接是指每次请求都需要建立连接,而无状态是指服务器不会为每个请求保持状态。
4.1 request 和 response
在计算机科学和网络编程中,request 和 response 是HTTP协议中的核心概念。它们分别表示客户端向服务器发送的请求和服务器对请求的响应。
HTTP请求(request)是由客户端(通常是Web浏览器)向服务器发送的,用于请求访问网页或资源。
方法(GET、POST、PUT、DELETE等)
请求URL(资源的路径和名称)
请求头(header),包含关于请求的附加信息,如内容类型(Content-Type)、字符集(Charset)等
请求体(body),包含要发送的数据,如表单数据或JSON数据等
HTTP响应(response)是由服务器对客户端发送的请求进行响应的结果。
状态码(status code),表示请求的处理结果,如200表示成功,404表示未找到资源等
响应头(header),包含关于响应的附加信息,如内容类型(Content-Type)、字符集(Charset)等
响应体(body),包含服务器返回的数据,如网页内容、JSON数据等
在Web开发中,HTTP请求和响应是实现客户端与服务器之间通信的关键。通过发送HTTP请求来获取网页或资源,并接收HTTP响应来获取结果。
4.2 创建 http server
const http = require("http")
const server = http.createServer((request, response) => {
console.log(request.method)
response.setHeader("content-type", "text/html;charset=utf-8")
response.end("唤醒手腕")
})
server.listen(9000, ()=> {
console.log("server run")
})