关于NodeJs的一些总结和补充

关于NodeJs的总结和补充

1.谈谈对NodeJs的理解

Node. js是一个基于Chrome v8引擎的服务器端JavaScript运行环境

2.NodeJs的使用场景

  • 快速编写接口和中间件
  • 基于Express框架,可以快速构建Web应用
  • 基于Electron框架,可以快速构建跨平台的桌面应用
  • 基于restify框架,可以快速构建API接口项目
  • 读写和操作数据库。创建使用的命令行工具辅助前端开发
  • etc…

3.为什么要用NodeJs

原因如下。

  • 简单, Node. js用 JavaScript、JSON进行编码,简单好学。
  • 功能强大,非阻塞式I/O,在较慢的网络环境中,可以分块传输数据,事件驱动,擅长高并发访问
  • 轻量级, Node. js本身既是代码又是服务器,前后端使用同一语言。
  • 可扩展,可以轻松应对多实例、多服务器架构,同时有海量的第三方应用组件。

4.NodeJs有哪些全局对象?

  • global
    这是Node.js中最根本的全局对象,类似于浏览器中的window对象。所有的全局变量(除了global本身)都是global对象的属性。
  • process:
    这是一个提供有关当前Node.js进程的信息并与之交互的对象。它提供了诸如环境变量、命令行参数、进程版本、操作系统等信息,并且可以用来退出进程。例如,process.env用于访问环境变量,process.argv用于获取命令行参数。
  • console:
    这是一个用于打印输出到stdout和stderr的对象。它提供了诸如console.log(), console.error() ,console.warn()等方法。
  • Buffer:
    这是一个全局可用的类型,用于处理二进制数据。在Node.js中,由于JavaScript原生不支持二进制数据,因此Buffer类被引入来处理这种情况。例如,读取文件时通常会得到Buffer对象,然后可以将其转换为字符串或其他格式。
  • setImmediate, setTimeout, setInterval, clearTimeout, clearInterval:
    这些是Node.js中的全局函数,用于处理和控制异步操作。setImmediate用于将回调函数排入队列,在当前事件循环结束时执行。setTimeoutsetInterval用于在指定的毫秒数后执行回调函数,或者每隔指定的毫秒数执行回调函数。clearTimeoutclearInterval用于取消由setTimeoutsetInterval设置的定时器。
  • __filename:
    这是一个包含当前模块文件路径的全局变量。
  • __dirname:
    这是一个包含当前模块目录路径的全局变量。
  • module 和 exports/require:
    虽然它们通常被视为模块级别的对象,但在每个模块内部,它们实际上是全局可访问的。module表示当前模块,exports是模块导出的对象,require函数用于引入其他模块。

5.关于NodeJS中的process

5.1 process是什么

process翻译过来是进程的意思,学习过计算机系统的人都知道,进程是计算机系统进行资源分配和调度的基本单位。每个进程享有独立的空间地址和数据栈。也就是说不同的进程间不能直接访问数据,需要经过通信后,才能互相访问数据。process对象是一个全局变量,提供了node进程的信息并对其进行控制。

5.2 process常用的属性和方法

5.2.1 process.cwd()

  • 返回当前Node进程执行的目录
  • 如果一个Node模块A通过NPM发布,项目B中使用了模块A,在A中需要操作B项目下的文件时,就可以用process.cwd来获取B的路径。
console.log(process.cwd())//E:\project\store2

5.2.2 process.env

  • 返回一个对象,存储当前环境有关的所有信息
  • 我们一般会在process.env上面挂代码环境变量
  • 比如process.env.NODE_ENV
  • 在vue-cli的源码中也有process.env.VUE_CLI_DEBUG标明debug模式
console.log(process.env)

5.2.3 process.argv

  • 在终端通过Node执行命令的时候,通过process.agrv可以获取传入的命令行参数,返回值是一个数组
  • index(0);路径
  • index(1);被执行的js文件路径
  • index(2-n);真实传入的命令参数
  • 假设我们有一个node.js,然后在终端运行,并在后面跟上参数2025,输node node.js 2025,回车:
console.log(process.argv)
    // [
    // 'D:\\software\\nodejs\\node.exe',
    //     'E:\\project\\store2\\node.js',
    //     '2025'
    // ]

5.2.4 process.nextTick()

因为node是基于事件循环的,在一个时间点只能处理一件事情,process.nextTick()的作用是定义出一个动作,并让这个动作在下一个事件轮询的时间点上执行。

function say(){
    
    
    console.log('nextTick-say')
}
process.nextTick(say)
console.log('console.log-say')
// console.log-say
// nextTick-say

say被放在下一次事件循环处理了,所以后打印。当然我们也可以使用setTimeout来实现:

function say(){
    
    
    console.log('nextTick-say')
}
setTimeout(say,0)
console.log('console.log-say')
// console.log-say
// nextTick-say

区别在于,process.nextTick()在一次事件循环结束call stack清空后再调用callback;而setTimeout并不知道事件循环什么时候结束,所以调用callback的时机不确定。

5.2.5 其它

process.stdinprocess.stdoutprocess.stderrprocess.onprocess.archprocess.platform process.exit

process.stdin 和 process.stdout 是两个至关重要的模块,它们直接与操作系统的标准输入和标准输出流进行交互。它们使得 Node.js 应用可以与用户或其他进程进行数据通信。

process.stdout
属性返回一个对象,表示标准输出。该对象的write方法等同于console.log,可用在标准输出向用户显示内容。

process.stdout.write('Hello World')

输出结果为:
在这里插入图片描述
process.stdin
返回一个对象,表示标准输入。

process.stdout.write('请输入数据:')
process.stdin.on('data',(data)=>{
    
    
     process.stdout.write("输入的数据是:"+data.toString().trim())
     process.exit(0) //结束进程
 })

打印结果为:

在这里插入图片描述

5.2.6 总结

在这里插入图片描述

console.log('进程号',process.pid)
console.log('操作系统',process.platform)
console.log('Node版本',process.version)
console.log('所有命令行参数:',process.argv)

在这里插入图片描述

6.console有哪些常用方法?

  • console.log/console.info/console.error/console.warning
  • console.time/console.timeEnd
  • console.trace
  • console .table。

7.Node.js有哪些定时功能?

  • setTimeout/clearTimeout,
  • setInterval/clearInterval
  • setImmediate/clearImmediate
  • process.nextTick

8.谈谈Node.js的fs模块

fs 模块是Node.js官方提供的、用来操作文件的模块。fs(filesystem),该模块提供本地文件的读写能力,基本上是POSIX文件操作命令的简单包装。可以说,所有与文件的操作都是通过fs核心模块实现。

这个模块对所有文件系统操作提供异步(不具有sync 后缀)和同步(具有 sync 后缀)两种操作方式,而供开发者选择。

fs.watch和fs.watchFile有什么区别?
二者主要用来监听文件变动,fs.watch利用操作系统原生机制来监听,可能不适用网络文件系统;fs. watchFile则定期检查文件状态变更,适用于网络文件系统,但是与fs.watch相比有些慢,因为它不采用实时机制。

9.谈谈Node.js中的Buffer

在Node应用中,需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer(缓冲区)就是在内存中开辟一片区域(初次初始化为8KB),用来存放二进制数据。

简单来讲,Nodejs不能控制数据传输的速度和到达时间,只能决定何时发送数据,如果还没到发送时间,则将数据放在Buffer中,即在RAM中,直至将它们发送完毕。

Buffer是用来存储二进制数据,其的形式可以理解成一个数组,数组中的每一项,都可以保存8位二进制:00000000,也就是一个字节。

关于buffer详情可见我写的博客https://blog.csdn.net/fageaaa/article/details/146610452

10.谈谈Node.js的Stream

流(Stream),是一个数据传输手段,是端到端信息交换的一种方式,而且是有顺序的,是逐块读取数据、处理内容,用于顺序读取输入或写入输出。

它的独特之处在于,它不像传统的程序那样一次将一个文件读入内存,而是逐块读取数据、处理其内容,而不是将其全部保存在内存中。

流可以分成三部分:

  • source 源数据
  • dest 目的地
  • pipe 运输管道

在source和dest之间有一个连接的管道pipe,它的基本语法是source.pipe(dest),source和dest就是通过pipe连接,让数据从source流向了dest。

在NodeJS,几乎所有的地方都使用到了流的概念,分成四个种类:

  • 可写流:可写入数据的流。例如 fs.createWriteStream() 可以使用流将数据写入文件
  • 可读流: 可读取数据的流。例如fs.createReadStream() 可以从文件读取内容
  • 双工流: 既可读又可写的流。例如 net.Socket
  • 转换流: 可以在数据写入和读取时修改或转换数据的流。例如,在文件压缩操作中,可以向文件写入压缩数据,并从文件中读取解压数据。

11.关于EventEmitter

11.1 EventEmitter简介

EventEmitter是 Node.js中一个实现观察者模式的类,主要功能是订阅和发布消息,用于解决多模块交互而产生的模块之间的耦合问题。

11.2 如何实现一个EventEmitter?

可通过3步实现 EventEmitter定义一个子类,通过寄生组合式继承,继承 EventEmitter。

var Util= require('util' );
var EventEmitter= require ('events');
function  IcktEmitter () {
    
    
    EventEmitter.apply(this, arguments)
}
Util.inherits(IcktEmitter, EventEmitter);


var ie = new IcktEmitter ( ) ;
ie.on('icketang',function(data){
    
    
    console.log('接收到消息',data )
})
ie.emit('icketang','来自有课网的消息');

12.Nodejs的子进程

12.1 exec、 execFile、 spawn和fork都是做什么用的?

方法 区别 适用场景
exec 执行 shell 命令,创建 shell进程 执行简单命令,获取输出和错误信息
execFile 执行可执行文件 执行编译好的二进制文件,获取输出和错误信息
fork 衍生 Node.js 子进程,执行 JS 文件 创建独立的子进程并与父进程通过消息通信
spawn 执行命令,不创建 shell 进程 执行复杂命令,流式处理输入输出

12.2 如何实现一个简单的命令行交互程序?

var cp = require (' child process )//执行指令
var child= cp .spawn('echo', ['hello, ''] )// child.stdout是输入流, process. stdout是输出流
//子进程的输出流作为当前程序的输入流,然后重定向到当前程序的控制器输出
child. stdout. pipe(process. stdout)

12.3 两个Node.js程序之间如何交互?

通过fork实现父子程序之间的交互。子程序用 process.on、 process. send访问父程序,父程序用 child.on、 child.send访问子程序。

//parent.js
let cp =require('child_process');
let child=cp.fork('./child.js');
child.on('message',function(msg){
    
    
    console.log('子程序发送的数据:',msg)
})
child.send ( '来自父程序发送的数据')
//child.js
process.on('message',function(msg){
    
    
    console.log ('父程序发送的数据:', msg)
    process.send ( '来自子程序发送的数据')
})

在这里插入图片描述

14.如何让一个JavaScript文件变得像Linux命令一样可执行?

创建 JavaScript 文件:例如,创建一个名为 test.js 的文件,内容如下:

console.log("Hello, World!");

在文件的顶部添加 #!/usr/bin/env node,这告诉系统使用 Node.js 来执行这个文件。

#!/usr/bin/env node
console.log("Hello, World!");

使文件可执行:在终端中,运行以下命令来使文件具有执行权限:
在这里插入图片描述
运行文件:现在你可以像运行普通命令一样运行你的 JavaScript 文件了:
在这里插入图片描述

15.谈谈栈和堆的区别

区别如下:
(1)栈( stack)区由编译器自动分配和释放,存放函数的参数值、局部变量的值等。
堆(heap)区一般由程序员分配和释放,若程序员不释放,程序结束时可能由OS回收。
(2)堆(数据结构)可以被看成一棵树,如堆排序。栈(数据结构)是一种先进后出的数据结构。

16.node的三大模块

Node.js 应用由模块组成,采用 CommonJS 模块规范。Node.js中的模块分为三种:

  • 内置模块
  • 第三方模块
  • 自定义模块

其中内置模块如下:

  • FS:文件系统模块
  • path:路径模块
  • OS:操作系统相关
  • net:网络相关
  • http
  • url
  • events 用于处理事件。该模块提供了事件发射器(EventEmitter)的功能,可以用于创建和管理事件。例如,可以使用EventEmitter来订阅和触发事件‌
  • crypto 用于加密和解密数据。该模块提供了加密算法和哈希算法等功能,常用于数据安全处理‌

17.说说 Node 文件查找的优先级以及 Require 方法的文件查找策略?

通过require函数导入其他模块(自定义模块、系统模块、第三方库模块)中的内容,require参数较为简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。
(1) 缓存的模块优先级最高
(2) 如果是内置模块,则直接返回,优先级仅次缓存的模块
(3) 如果是绝对路径 / 开头,则从根目录找
(4) 如果是相对路径 ./开头,则从当前require文件相对位置找
(5) 如果文件没有携带后缀,先从js、json、node按顺序查找
(6) 如果是目录,则根据 package.json的main属性值决定目录下入口文件,默认情况为 index.js
(7) 如果文件为第三方模块

  • require('第三方包名')优先在加载该包的模块的同级目录node_modules中查找第三方包。
  • 找到该第三方包中的package.json文件,并且找到里面的main属性对应的入口模块,该入口模块即为加载的第三方模块。
  • 如果在要加载的第三方包中没有找到package.json文件或者是package.json文件中没有main属性,则默认加载第三方包中的index.js文件。
  • 如果在加载第三方模块的文件的同级目录没有找到node_modules文件夹,或者以上所有情况都没有找到,则会向上一级父级目录下查找node_modules文件夹,查找规则如上一致。
  • 如果一直找到该模块的磁盘根路径都没有找到,则会报错:can not find module xxx

18.自定义本地包和全局包

18.1 什么是包

包就是一个文件夹,用来管理模块和模块之间的各种关系。

18.2 包的规范(了解)

  • package.json必须在包的顶层目录下。
  • 二进制文件应该在bin目录下。
  • JavaScript代码应该在lib目录下。
  • 文档应该在doc目录下。
  • 单元测试应该在test目录下。

18.3 package.json字段分析(了解)

  • name:包的名称,必须是唯一的,由小写英文字母、数字和下划线组成,不能包含空格
  • description:包的简要说明。
  • version:符合语义化版本识别规范的版本字符串。
    • 主版本号:当你做了不兼容的API修改。
    • 子版本号:当你做了向下兼容的功能性新增。
    • 修订号:当你做了向下兼容的问题修正。
  • keywords:关键字数组,通常用于搜索。
  • maintainers:维护者数组,每个元素要包含name、email(可选)、web(可选)字段
  • contributors:贡献者数组,格式与maintainers相同。包的作者应该是贡献者数组的第一个元素。
  • bugs:提交bug的地址,可以是网站或者电子邮件地址。
  • licenses:许可证数组,每个元素要包含type(许可证名称)和URL(连接到许可证文本的地址)字段。
  • repositories:仓库托管地址数组,每个元素要包含type(仓库类型,如git)、url(仓库地址)和path(相对于仓库的路径,可选)字段。
  • dependencies:生产环境包的依赖,一个关键数组,由包的名称和版本号组成。
  • devDependencies:开发环境包的依赖,一个关联数组,由包的名称和版本号组成。
  • main:指定包入口文件,如果没有配置main,默认会将index.js作为入口,如果包中没有index.js,那么就必须配置main。
  • scripts:(1)保存一些常用的指令,可以通过npm run key方式运行。(2)应用场景:每次执行某个js文件都需要传递参数,并且每次传递的参数都是一样的,那么就可以通过将指令保存到script中来简化输入指令的操作。(通过指令执行某个文件的时候可以传递一个参数,并且该文件可以通过process.argv拿到这个参数。scripts可以简化执行命令)

18.4 自定义包实现步骤

  • 创建一个包文件夹。
  • 初始化一个package.json文件。
  • 初始化一个包入口js文件。
    注意点:如果没有配置main,默认会将index.js作为入口,如果包中没有index.js,那么就必须配置main。
  • 根据包信息配置package.json文件。
    注意点:通过scripts可以帮助我们记住指令,然后通过npm run xxx方式,就可以执行该指令。如果指令的名称叫做start或者test,那么执行的时候可以不加run。

全局包:一般全局包都是些工具包比如nrm、yarn、cnpm等,工具包的特点需要自定义指令。

  • 给package.json添加bin属性,告诉系统执行全局命令式时需要执行哪一个JS文件。
  • 在全局命令执行的JS文件中添加#! /usr/bin/envnode。 (node环境执行)
  • 通过npm link 将本地包放到全局方便我们调试。
    在这里插入图片描述
    在这里插入图片描述

19.关于nodejs的中间件

在现代 web 开发中,尤其是在使用 Node.js 和 Express 框架时,中间件(Middleware)这一概念频频出现在开发者的视野中。作为理解和使用这些技术的基础,掌握中间件的概念和应用方法对于开发高效、可维护的应用至关重要。

19.1 什么是中间件

node中间件主要是指封装http请求细节处理的方法,其本质上就是在进入具体的业务处理之前,先让特定过滤器处理;对于Web应用而言,引入Node中间件可以简化和封装一些基础逻辑处理细节。

在这里插入图片描述

19.2 中间件的格式

在这里插入图片描述

注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res。

19.3 封装node中间件示例

19.3.1 创建中间件函数

首先,你需要创建一个函数,该函数接收三个参数:req、res和next。next是一个函数,用于调用下一个中间件。

function myMiddleware(req, res, next) {
    
    
  // 在这里添加你的逻辑
  console.log('Middleware is running');
  // 调用下一个中间件
  next();
}

19.3.2 使用Express应用注册中间件

在你的Express应用中,你可以使用.use()方法来注册中间件。

const express = require('express');
const app = express();
 
// 注册全局中间件
app.use(myMiddleware);
 
// 其他路由或中间件
app.get('/', (req, res) => {
    
    
  res.send('Hello World!');
});
 
app.listen(3000, () => {
    
    
  console.log('Server is running on port 3000');
});

19.3.3 封装为一个模块

为了更好地组织代码,你可以将中间件封装成一个模块。

//myMiddleware.js:
function myMiddleware(req, res, next) {
    
    
  console.log('Middleware is running');
  next();
}
 
module.exports = myMiddleware;

然后在你的主应用文件中引入并使用它:

const express = require('express');
const app = express();
const myMiddleware = require('./myMiddleware'); // 引入中间件模块
 
app.use(myMiddleware); // 使用中间件
 
app.get('/', (req, res) => {
    
    
  res.send('Hello World!');
});
 
app.listen(3000, () => {
    
    
  console.log('Server is running on port 3000');
});

20.Nodejs通过哪些方法可以进行异步流程的控制

20.1 callback回调函数

fs.readFile('/path/to/file', 'utf8', (err, data) => {
    
    
   if (err) throw err;
   console.log(data);
});

20.2 Promises

const promise = new Promise((resolve, reject) => {
    
    
   // 模拟异步操作
   setTimeout(() => {
    
    
       resolve('Success!');
       // 或在出错时调用 reject('Error!')
   }, 1000);
});
 
promise.then(result => {
    
    
   console.log(result); // 输出 "Success!"
}).catch(error => {
    
    
   console.error(error);
});

20.3 async/await

async function fetchAndLog() {
    
    
   const result = await someAsyncFunction(); // 假设这是一个返回 Promise 的异步函数
   console.log(result);
}
 
fetchAndLog().catch(error => {
    
    
   console.error('Error:', error);
});

20.4 流(Streams)

const readableStream = fs.createReadStream('file.txt');
readableStream.on('data', chunk => {
    
    
   console.log(`Received ${
      
      chunk.length} bytes of data.`);
});
readableStream.on('end', () => {
    
    
   console.log('No more data.');
});

20.5 Child Processes(子进程)

const {
    
     exec } = require('child_process');
exec('ls -lh', (error, stdout, stderr) => {
    
    
  if (error) {
    
    
    console.error(`执行出错: ${
      
      error}`);
    return;
  }
  console.log(`stdout: ${
      
      stdout}`);
  console.error(`stderr: ${
      
      stderr}`);
});

21.知道node中的route, middleware, cluster, nodemon, pm2, server-side rendering么?

  • route 路由
  • middleware 中间件
  • cluster 集群
  • nodemon 用于监控文件的变化并自动重启服务器的工具
  • pm2 进程管理工具
  • server-side rendering 服务端渲染

nodemon是什么?(了解)
Nodemon 是 Node.js 生态系统中一款非常实用的开发工具,用于监控文件的变化并自动重启服务器,从而提升开发效率。特别是在后端开发过程中,频繁的代码修改和重启服务器操作极为繁琐,而 Nodemon 通过自动化这些流程,让开发者能够专注于代码本身。本文将详细介绍 Nodemon 的基本使用方法、配置选项及其在实际开发中的应用场景。

pm2是什么?(了解)
‌PM2是一个进程管理工具,专门用于管理和监控Node.js应用程序。‌ PM2不仅可以启动和管理Node.js应用,还可以进行性能监控、自动重启、负载均衡、进程守护和零秒重载等功能。它支持Linux、MacOS和Windows等多个操作系统,是Node.js开发者管理和监控应用程序的首选工具之一‌。