1.简述 node.js 的特点以及使用的场景
node.js 的特点:
- 它是一个 JavaScript 运行环境
- 依赖于 chrome V8 引擎进行代码解释
- 事件驱动
- 非阻塞 I/O
- 轻量、可伸缩、适用实时数据交互应用
- 利用单线程做多线程的事
node.js 使用场景:
- 轻量级、高性能的 web 服务
- 前后端 JavaScript 同构开发
- 便捷高效的前端工程化
2.简述 Buffer 的使用,包括多种创建方式,实例方法,静态方法
Buffer 特点:
- 无须 require 的一个全局变量
- 实现 node.js 平台下的二进制数据操作
- 不占据 V8 堆内存大小的内存空间(直接由 c++层面分配)
- 内存的使用有 node 来控制,由 V8 的 GC 回收
- 一般配合 Stream 流使用,充当数据缓冲区
创建 Buffer 实例:
- alloc:创建指定字节大小的 buffer
- allocUnsafe:创建指定大小的 buffer(不安全)
- from:接收数据,创建 buffer
Buffer 实例方法
- fill: 使用数据填充 buffer
- write: 想 buffer 中写入数据
- toString: 将 buffer 转为字符串
- slice: 截取 buffer
- indexOf: 在 buffer 中查找数据
- copy: 拷贝 buffer 中的数据
Buffer 静态方法
- concat: 将多个 buffer 拼接成一个新的 buffer
- isBuffer: 判断当前数据是否为 buffer
3.写出 5 个以上文件操作的 API, 并且用文字说明其功能
- readFile: 从指定文件中读取数据(小文件)
- read: 读大文件
- writeFile:向指定文件中写入数据
- appendFile:追加的方式向指定文件中写入数据
- copyFile: 将某个文件中的数据拷贝至另一个文件
- watchFile: 对指定文件进行监控
- stat: 通过异步模式获取文件信息的语法格式
- access:查询权限
- mkdir: 创建目录
- readdir: 读取目录
- rmdir: 删除目录
4.简述使用流操作的优势,以及 node 中的流分类
流处理数据的优势
- 时间效率: 流的分段处理可以同事操作多个数据 chunk
- 空间效率: 无需加载大量的数据到内存中即可进行处理
- 使用方便: 流配合管理,扩展程序变得简单
node.js 中的流分类
- readable: 可读流, 可读流是数据可以被消费的源的抽象。一个例子就是 fs.createReadStream 方法。
- writeable: 可写流, 可读流是数据可以被写入目标的抽象。一个例子就是 fs.createWriteStream 方法。
- duplex: 双向流, 即可读也可写,一个例子是 TCP socket
- tranform: 转换流,转换流是基于双向流的,可以在读或者写的时候被用来更改或者转换数据。一个例子是 zlib.createGzip 使用 gzip 算法压缩数据。你可以将转换流想象成一个函数,它的输入是可写流,输出是可读流。你或许也听过将转换流成为“通过流(through streams)
5.在数据封装与解封的过程中,针对应用层、传输层、网络层、数据链路层、物理层 5 层分别做了什么事情?
- 应用层: 为特定应用程序提供数据传输服务,例如 http,DNS 等,数据单位为报文,右键,http,域名
- 传输层: 为进程提供数据传输服务,由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议. 运输层包括两种协议: 传输控制协议 TGP,提供面向链接,可靠的数据传输服务,数据单位为报文;用户数据包协议 UDP,提供无连接,尽量大努力的数据传输服务,数据单位为用户数据报,TCP 主要提供完整性服务,UDP 主要提供及时性服务
- 网络层: 为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。ip 地址,子网划分等用来确定目标网络
- 数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。频分复用、码分复用用来确定目标主机
- 物理层: 考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。调制解调
代码题
1.统计指定目录中文件总大小。要考虑目录中还有子目录的情况。可以同步编码,异步更好。
// 异步计算文件大小
const fs = require('fs')
const path = require('path')
const { promisify } = require('util')
const stat = promisify(fs.stat)
const readdir = promisify(fs.readdir)
async function calcFileSizeAsync(dirPath, cb) {
let fileSize = 0
async function getFileSize(dirPath) {
const statObj = await stat(dirPath)
if (statObj.isDirectory()) {
const files = await readdir(dirPath)
const dirs = files.map((item) => path.join(dirPath, item))
for (let i = 0; i < dirs.length; i++) {
await getFileSize(dirs[i])
}
} else {
fileSize += statObj.size
}
}
await getFileSize(dirPath)
cb && cb(fileSize)
}
calcFileSizeAsync('test', (size) => {
console.log('异步计算文件大小:' + size)
})
2.编写单向链表类并且实现队列的入列出列操作。
/**
* 单向链表
* 01 node + head + null
* 02 head --> null
* 03 size
* 04 next element
*
* 05 增加 删除 修改 查询 清空节点
*/
class Node {
constructor(element, next) {
this.element = element
this.next = next
}
}
class LinkedList {
constructor(head, size) {
this.head = null
this.size = 0
}
_getNode(index) {
if (index < 0 || index >= this.size) {
throw new Error('cross the border')
}
let currentNode = this.head
for (let i = 0; i < index; i++) {
currentNode = currentNode.next
}
return currentNode
}
add(index, element) {
if (arguments.length === 1) {
element = index
index = this.size
}
if (index < 0 || index > this.size) {
throw new Error('cross the border')
}
if (index === 0) {
// 保存原有head 的指向
let head = this.head
this.head = new Node(element, head)
} else {
let prevNode = this._getNode(index - 1)
prevNode.next = new Node(element, prevNode.next)
}
this.size++
}
remove(index) {
let rmNode = null
if (index < 0 || index >= this.size) {
throw new Error('cross the border')
}
if (index === 0) {
rmNode = this.head
if (!rmNode) {
return undefined
}
this.head = rmNode.next
} else {
let prevNode = this._getNode(index - 1)
rmNode = prevNode.next
prevNode.next = rmNode.next
}
this.size--
return rmNode
}
set(index, element) {
let node = this._getNode(index)
node.element = element
}
get(index) {
return this._getNode(index)
}
clear() {
this.head = null
this.size = 0
}
}
class Queue {
constructor() {
this.linkedList = new LinkedList()
}
enQueue(data) {
this.linkedList.add(data)
}
deQueue() {
return this.linkedList.remove(0)
}
}
const qe = new Queue()
qe.enQueue('node1')
qe.enQueue('node2')
qe.enQueue('node3')
3.基于 Node 写出一静态服务器。接收请求并且响应特定目录(服务器目录)中的 html、css、js、图片等资源
const http = require('http')
const url = require('url')
const path = require('path')
const fs = require('fs')
const mime = require('mime')
const server = http.createServer((req, res) => {
// console.log('请求进来了')
// 1 路径处理
let { pathname, query } = url.parse(req.url)
pathname = decodeURIComponent(pathname)
let absPath = path.join(__dirname, pathname)
// console.log(absPath)
// 2 目标资源状态处理
fs.stat(absPath, (err, statObj) => {
if (err) {
res.statusCode = 404
res.end('Not Found')
return
}
if (statObj.isFile()) {
// 此时说明路径对应的目标是一个文件,可以直接读取然后回写
fs.readFile(absPath, (err, data) => {
res.setHeader('Content-type', mime.getType(absPath) + ';charset=utf-8')
res.end(data)
})
} else {
fs.readFile(path.join(absPath, 'index.html'), (err, data) => {
res.setHeader('Content-type', mime.getType(absPath) + ';charset=utf-8')
res.end(data)
})
}
})
})
server.listen(1234, () => {
console.log('server is start.....')
})