一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

十六、知识点补充

1、环境变量
// 1、nodejs自带模块化功能,一个js文件就是一个模块
console.log(this === global) // false

// 2、console.time('start')和console.timeEnd('start')记录时间间隔
console.time('start')
let num = 0
for (let i = 1; i <= 100; i++) {
  num += i
}
console.log(num)
console.timeEnd('start')

// 3、process.env:当前node执行的命令行或脚本窗口设置的变量(mac export,windows set),以及环境变量
//     - package.json配置
/*
{
  "scripts": {
    "start": "set myNodeEnv=develop & node src/main.js"
  }
}
*/
console.log(process.env.myNodeEnv)

// 微任务,优先级比promise.then高
process.nextTick(() => {
})
// 宏任务
setImmediate(({a, ...props}) => {
  console.log(a)
  console.log(props)
}, {a: 1, b: 2, c: 3})
setTimeout((...args) => {
  console.log(args)
}, 0, 'a', 'b', 'c')

function Person(name) {
  this.name = name || '谜团'
}

// 括号可以省略(不推荐)
const person = new Person
console.log(person)
2、pnpm包管理方式
* my-project依赖express,express依赖cookie
* 目录结构
########
my-project
    ∟ node_modules
    |   ∟ .pnpm
    |   |   ∟ cookie@1.0.0
    |   |   |   ∟ node_modules
    |   |   |       ∟ cookie(硬链接)
    |   |   |           ∟ package.json
    |   |   ∟ express@1.0.0
    |   |       ∟ node_modules
    |   |           ∟ cookie(软链接)
    |   |           ∟ express(硬链接)
    |   |               ∟ package.json
    |   ∟ express(软链接)
    ∟ package.json
########
* package.json依赖的包如果当前node_modules没有,则会向上层目录的node_modules找包
3、npm包管理方式(dependencies、devDependencies、peerDependencies)
* my-project依赖cookie,my-project依赖express,express依赖cookie
* 目录结构
########
my-project
    ∟ node_modules
    |   ∟ cookie
    |   |   ∟ package.json(版本:0.6.0)
    |   ∟ express
    |       ∟ node_modules
    |       |   ∟ cookie
    |       |       ∟ package.json(版本:0.5.0)
    |       ∟ package.json
    ∟ package.json
########
* my-project的dependencies和devDependencies都会install
* express的dependencies依赖冲突时会install自定的版本
* express的devDependencies不会install
* peerDependencies在npm-v7会自动安装(依赖冲突会报错),在npm-v7之前则不会(--legacy-peer-deps)
* peerDependencies常用在,比如elementui需要用到vue,可以确定的是用到elementui的项目也会用到vue
  所以elementui就可以把vue放在devDependencies和peerDependencies
4、npm发布包
* 注意切换镜像源:https://registry.npmjs.org/
* 包名不能和已有的包一致
* 注意package.json的main属性配置(作为被依赖包时的入口文件)
* npm addUser:注册账号,如果有账号表示登录,新用户需要校验邮箱
* npm publish:发布
5、内置模块-util模块
const util = require('util')
const fs = require('fs')

function Person() {
  this.name = '冰魂'
}

Person.prototype.age = 18

function Child() {
}

util.inherits(Child, Person) // 只继承公有属性
const child = new Child()
console.log(child.name) // undefined
console.log(child.age) // 18

console.log(util.isArray([]))
console.log(util.isFunction(function () {
}))

// 把一个函数promise化
const read = util.promisify(fs.readFile)
read('./src/data.json', 'utf8').then(res => {
  console.log(res)
})
6、Buffer
/*
1、什么是Buffer
    - 缓冲区Buffer是暂时存放输入输出数据的一段内存
    - js语言没有二进制数据类型,而在处理TCP和文件流的时候,必须要处理二进制数据
    - nodejs提供了一个Buffer对象来提供对二进制数据的操作
    - 是一个表示固定内存分配的全局对象,也就是说要放到缓存区中的字节数需要提前确定
    - Buffer好比由一个多位字节元素组成的数组,可以有效的在javascript中表示二进制数据
2、字节
    - buffer(16进制)
    - 1024b = 1k
    - 8bit(8个二进制) = 1b(字节)
    - 1个汉字(3个b)
    - 1个字节转化成十进制是255
    - 1个字节最大转换成16进制是ff
3、常用方法
    - buffer.fill方法,填充buffer中的内容
    - buffer.toString方法,将buffer转化成字符串
    - buffer.slice方法,截取想要的buffer
    - buffer.copy方法,拷贝buffer
    - buffer.concat方法,buffer的拼接方法
    - buffer.isBuffer,判断是否是buffer类型
    - buffer.write,写入字符串
    - buffer.writeInt8,写入长度为8位的整数
    - buffer.writeInt16BE,写入长度为16位的整数,高位在前
    - buffer.readInt16BE,读取长度为16位的整数,高位在前
    - buffer.writeInt16LE,写入长度为16位的整数,低位在前
    - buffer.readInt16LE,读取长度为16位的整数,低位在前
*/

// 创建10个字节的buffer
const buffer1 = Buffer.alloc(10)
console.log(buffer1)

// 10进制转16进制
const buffer2 = Buffer.from([16, 17, 18, 256])
console.log(buffer2)
const buffer5 = buffer2.slice(0, 1);
buffer5[0] = 257 // Buffer里的是内存地址,所以会改变原数组
console.log(buffer2)

// 字符串转buffer
const buffer3 = Buffer.from("你好世界")
console.log(buffer3.length) // 字节长度
console.log(buffer3.toString()) // buffer转字符串

const buffer4 = Buffer.allocUnsafe(10)
buffer4.fill(0)
console.log(buffer4)

const buffer6 = Buffer.from("复仇")
const buffer7 = Buffer.from("之魂")
const buffer8 = Buffer.allocUnsafe(12)
buffer6.copy(buffer8, 0)
buffer7.copy(buffer8, 6)
console.log(buffer8.toString())
console.log(Buffer.concat([buffer6, buffer7]).toString())
7、base64编码算法
// base64进制转化
const buffer = Buffer.from('进');
console.log(buffer)
console.log(buffer.toString('base64'))

// 一个汉字3字节24位,转换成4字节32位
let word3 = ''
for (let i = 0; i < buffer.length; i++) {
  word3 += buffer[i].toString(2)
}
console.log(word3) // 11101000 10111111 10011011

let word4Arr = []
for (let i = 0; i < word3.length / 6; i++) {
  word4Arr.push(word3.slice(i * 6, (i + 1) * 6))
}
console.log(word4Arr) // [ '111010', '001011', '111110', '011011' ]

const base64Map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789' + '+/'

let base64Str = ''
for (let i = 0; i < word4Arr.length; i++) {
  // 转换成索引
  const index = parseInt(word4Arr[i], 2)
  base64Str += base64Map[index]
}
console.log(base64Str)
8、path模块
const path = require('path')

// 路径拼接
console.log(path.join(__dirname, './a', './b'))
// 返回绝对路径(如果以/开头则会解析到根路径)
console.log(path.resolve('./src', './a', './b'))
console.log(path.delimiter) // 当前系统的环境变量分隔符
console.log(path.posix.delimiter) // mac的环境变量分隔符
console.log(path.win32.sep) // windows的路径分隔符
// path.relative() // 获得两个路径之间的相对路径
// 获取文件名
path.basename("戴泽.png", ".png")
// 获取扩展名
path.extname("戴泽.png")
9、实现静态资源服务器
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>node</title>
  <!-- 不支持../和./路径,/开头是绝对资源路径,不以/开头是相对资源路径 -->
  <link rel="stylesheet" href="index.css">
</head>
<body>
实现静态资源服务器
</body>
</html>
const http = require('http')
const fs = require('fs')
const url = require('url')
const path = require('path')
// npm i mime
const mime = require('mime')

http.createServer((req, res) => {
  let {pathname, query} = url.parse(req.url, true)
  fs.stat('.' + pathname, (err, stats) => {
    if (err) {
      res.statusCode = 404
      res.end(`${pathname} not found`)
    } else if (stats.isFile()) {
      res.setHeader('Content-Type', mime.getType(pathname) + ';charset=utf-8')
      fs.createReadStream('.' + pathname).pipe(res)
    } else if (stats.isDirectory()) {
      // utf8不加-可能会ie不兼容
      res.setHeader('Content-Type', 'text/html;charset=utf-8')
      let p = path.join('.' + pathname, './index.html')
      fs.createReadStream(p).pipe(res)
    }
  })
}).listen(3000, () => {
  console.log('服务已在3000端口启动')
})
10、express的使用
* express搭建服务
########
const express = require('express')
const app = express()
app.listen(8080)
########
* express路由
########
// 必须method和path全都匹配上执行对应的callback
app[method](path, function () {})
app.all('*', function () {})
########
* 路径参数路由
########
// 将匹配到的结果生成一个对象放到req.params上
app.get('/user/:id/name')
########
* req上的属性
########
req.params:路径参数
req.url:整个的路径
req.path:pathname路径
req.headers:请求头
req.method:请求的方法
req.query:获取请求的参数,问号后面的参数
########
* middleware中间件的作用
    - 处理公共逻辑,扩展req,res
    - 可以决定是否继续执行
    - 开头匹配到就会执行中间件
    - 错误中间件,在页面的最后,参数是4个,第一个参数中是错误
* res新增的方法
    - res.json()
    - res.sendFile():绝对路径path.join/path.resolve
    - res.sendStatus()
    - res.send()
* 路由拆分
########
const express = require('express')
const app = express()
const router = express.Router()
router.get('/login', fn)
app.use('/user', router)
########
* bodyParser
########
// 解析json:application/json
app.use(bodyParser.json())
// 解析表单:application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}))
########
* ejs(前后端分离不使用ejs)
    - app.set('view engine', 'html')
    - app.set('views', 'static')
    - app.engine('html', require('ejs').__express)
    - res.render('index', 渲染的数据)
    - ejs用法
########
<% include '文件名' %>
<%= 变量 %>
<%- 转义变量 %>
<% for (let i = 0; i < 10; i++) { %>
    <li><%= i %></li>
<% } %>
########
* 静态服务中间件
    - app.use(express.static('文件夹'))
* 重定向
    - res.redirect('路径')
11、nodejs监听文件变化
const fs = require('fs')

fs.watchFile('./abc.txt', (curr, prev) => {
  if (curr.mtimeMs !== 0 && prev.mtimeMs === 0) {
    console.log('创建文件')
  } else if (curr.mtimeMs === 0 && prev.mtimeMs !== 0) {
    console.log('删除文件')
  } else if (curr.mtimeMs !== prev.mtimeMs) {
    console.log('修改文件')
  } else {
    console.log('首次执行')
  }
})
12、线程
* 多线程是如何实现的?
    - 并不是真正在同一个时间点执行多个任务,而是通过非常快速的切换时间片来实现
    - 每个线程都会有其独立的上下文(程序计数器、栈空间和局部变量),
      cpu会将时间切片并分配给线程(1ms~2ms是a线程,2ms~3ms是b线程)
* 浏览器的ui线程和js线程是共用一个线程的
* webworker多线程
    - 完全受主线程控制
    - 不能操作DOM
* js内存是有限的(1.7G),大对象存储在单独的内存(Buffer)
13、repl
const repl = require('repl')

/*
1、node命令或node命令执行此js,会进入repl会话
2、.help
    - .break    退出repl会话中未闭合的代码块
    - .clear    清除本地上下文
    - .editor   进入编辑模式
    - .exit     退出repl
    - .help     打印帮助信息
    - .load     从文件中加载js到repl会话中
    - .save     本次repl会话的所有命令保存到文件中
*/
const context = repl.start().context;
context.msg = 'hello'
context.hello = function () {
  console.log(context.msg)
}
14、console
// node .\bin\index.js > .\bin\node.log
// 把标准输出流输出到文件
console.log('aaa')
console.info('bbb')

// node .\bin\index.js 1> .\bin\node.log 2>&1
// 错误输出
// 把错误输出2重定向到标准输出1中
console.warn('ccc')
console.error('ddd')

// 断言
console.assert(1 !== 1, "报错")

// 浏览器上打印dom节点
console.dir({name: '莱恩'})

// 跟踪当前代码调用栈
console.trace()
15、process
// 当前工作目录
console.log(process.cwd())
// 切换目录
process.chdir("..")
console.log(process.cwd())

/*
// V8引擎最大使用内存量是1.7个G
{
  rss: 25833472, // 常驻内存
  heapTotal: 4726784, // 堆内存的总申请量
  heapUsed: 2891224, // 已经使用的量
  external: 998251, // 外部内存的使用量,比如:Buffer
  arrayBuffers: 9386
}
*/
console.log(process.memoryUsage())
16、event
const {EventEmitter} = require('events')

const event = new EventEmitter()
// 最大监听器数,默认10,0表示不限制
event.setMaxListeners(0)
const listener = (arg) => {
  console.log(arg)
}
console.log(event.addListener === event.on) // true
event.on('abc', listener)
event.emit('abc', 1)
console.log(event.listeners('abc')[0] === listener) // true
17、util
const util = require('util')

const obj = {
  name: '火女',
  age: 16,
  friend: {
    name: '飞机',
    age: 17,
    friend: {
      name: '冰魂',
      age: 18
    }
  }
}
console.log(obj)
console.log(util.inspect(obj, {depth: 1}))
18、module
/*
{
  id: 模块id,入口模块的id永远为.
  exports: 导出对象,默认是一个空对象
  parent: 父模块,此模块是谁哪个模块来加载的
  filename: 当前模块的绝对路径
  loaded: 是否加载完成
  children: 此模块加载了哪些模块
  paths: 第三方模块的加载路径
}
*/
console.log(module)
/*
{
  resolve: 只想知道模块的路径,但又不想加载这个模块
  main: main主要的,其实指就是入口模块
  extensions: 模块类型(.js|.json|.node)
  cache: 模块缓存对象,key是文件的绝对路径
}
*/
console.log(require)
console.log(exports === module.exports) // true
console.log(this === exports) // true
// 加载模块传入的参数:exports, module, require, __filename, __dirname
19、StringDecoder
const buffer = Buffer.from("你好世界")
const buffer1 = buffer.slice(0, 5)
const buffer2 = buffer.slice(5)
const {StringDecoder} = require('string_decoder')
const stringDecoder = new StringDecoder();
console.log(stringDecoder.write(buffer1))
console.log(stringDecoder.write(buffer2))
20、fs
const fs = require('fs')

/*
fs.open('./bin/javascript.txt', 'r', 0o666, (err, fd) => {
  const buff1 = Buffer.alloc(3);
  fs.read(fd, buff1, 0, 3, null, function () {
    console.log(buff1)
    const buff2 = Buffer.alloc(3);
    fs.read(fd, buff2, 0, 3, null, function () {
      console.log(buff2)
    })
  })
})
*/

/*
// 标准输入:0
process.stdin.on('data', data => {
  console.log(data)
})
// 标准输出:1
console.log('你好')
process.stdout.write('你好')
// 错误输出:2
console.error('你好')
process.stderr.write('你好')
// 打开:fd++,从3开始
// r:读;w:写;a:追加;r+:覆盖
fs.open('./bin/java.txt', 'r+', 0o666, (err, fd) => {
  fs.write(fd, Buffer.from('0123456789'), 0, 3, 3, function (err, written) {
    console.log(written)
    // 强行把缓存区的数据写入文件
    fs.fsync(fd, function () {
      // 关闭:fd--
      fs.close(fd, function () {
      })
    })
  })
})
*/

// 递归创建目录
function mkdirp(dir) {
  const dirs = dir.split('/');
  (function next(index) {
    if (index > dirs.length) return
    const current = dirs.slice(0, index).join('/');
    fs.access(current, fs.constants.R_OK, function (err) {
      if (err) {
        fs.mkdir(current, 0o666, function () {
          next(index + 1)
        })
      } else {
        next(index + 1)
      }
    })
  })(1);
}

mkdirp('./a/b/c')
const fs = require('fs')
/*
bom用于标记一个文本文件使用unicode编码,位于文本文件头部,bom字符对应的二进制字节如下:
    - fe ff:utf16be
    - ff fe:utf16le
    - ef bb bf:utf8
*/
fs.readFile('./bin/java.txt', function (err, data) {
  if (!err) {
    if (data[0] === 0xef && data[1] === 0xbb && data[2] === 0xbf) {
      data = data.slice(3)
    }
    console.log(data.toString())
  }
})
const fs = require('fs')
const iconv = require('iconv-lite')
fs.readFile('./bin/java.txt', function (err, data) {
  // 把一个gbk编码的buffer转变成utf8字符串
  // gbk编码不在nodejs自身支持范围内
  const str = iconv.decode(data, 'gbk');
  console.log(str)
})
const fs = require('fs')

fs.truncate('./bin/java.txt', 3, (err) => {
  console.log(err)
})
21、net
const net = require('net')
// 当客户端连接上来的时候会执行对应的回调函数
// socket其实是一个可读可写流,是一个双工流
const server = net.createServer({},)
server.on('connection', function (socket) {
  // 表示客户端连接的总数量是2个
  // server.maxConnections = 2
  // 获取当前有多少个客户端正在连接服务器
  server.getConnections((error, count) => {
    console.log(`欢迎光临,现在连接的客户端总数量是${count}个,客户端连接的总数量是${server.maxConnections}`)
  })
  console.log(socket.address())
  socket.setEncoding('utf8')
  // 如何获取可读流里的数据?
  socket.on('data', function (data) {
    console.log(data)
  })
  // 服务端收到客户端发出的关闭连接请求时,会触发end事件
  // 在这个地方客户端没有真正关闭,只是开始关闭。当真正关闭的时候还会触发一个close事件
  socket.on('end', function () {
    // socket.destroy()
    console.log('客户端已关闭')
    // close服务器端有一个方法叫close,close的意思是如果执行了此方法,那么此客户端将不再接收新的连接
    // 但是也不会关闭现有服务器
    // 一旦调用此方法,则当所有的客户端关闭跟本服务器的连接后,将关闭服务器
    server.unref()
  })
  setTimeout(function () {
    // 在5秒之后会关闭掉此服务器,不再接收新的客户端了
    server.close()
  }, 5000)
  // hasError如果为true表示异常关闭,否则表示正常关闭
  socket.on('close', function (hasError) {
    console.log('客户端真正关闭', hasError)
  })
  socket.on('error', function (err) {
    console.log(err)
  })
})
server.on('close', function () {
  console.log('服务器端已关闭')
})
server.on('error', function (err) {
  console.log(err)
})
// cmd命令行:telnet localhost 8080
server.listen(8080, function () {
  console.log(server.address())
  console.log('服务器端已经启动')
})
const net = require('net')
const socket = new net.Socket()

socket.connect(8080, function () {
  socket.write('hello')
})
socket.setEncoding('utf8')
socket.on('data', function (data) {
  console.log(data)
})
setTimeout(() => {
  // 要求关闭跟服务器的连接
  socket.end()
}, 5000)
posted on 2024-03-18 14:37  一路繁花似锦绣前程  阅读(28)  评论(0编辑  收藏  举报