NodeJS和浏览器中JS的区别

666 阅读4分钟

在Node.js环境和浏览器环境中使用JavaScript存在一些关键的区别,这些区别主要涉及到运行环境的特性、API的可用性以及代码执行的上下文。以下是Node.js和浏览器中使用JavaScript的主要区别:

1.运行环境

  • 客户端:JavaScript在浏览器中运行时,实现用户交互效果以。
  • 服务端:Node.js是一个服务器端的JavaScript运行环境,主要用于构建服务器端应用程序和处理文件系统等任务。

2.全局对象

  • Node.js中的全局对象是global,类似于浏览器中的window对象。但是,在Node.js中没有DOM和BOM(浏览器对象模型),因此没有浏览器特有的API(如documentwindownavigator等)。
  • 浏览器环境中的全局对象是window,它提供了访问浏览器窗口、文档和其他浏览器功能的API(如documentnavigator等)。

3.模块系统

  • Node.js:使用CommonJS模块系统(即requiremodule.exports),支持模块化开发。可以使用npm(Node Package Manager)管理第三方模块。

    • Node.js也支持ES6模块(通过.mjs扩展名或在package.json中设置"type": "module"),可以使用import和export关键字。
  • 浏览器:使用ES Modules(ES6模块)系统,通过importexport语法实现模块化。现代浏览器已经广泛支持ES Modules,但在旧版本浏览器中可能需要使用构建工具(如Webpack)进行转换和兼容性处理。

4. 内置模块和API

  • Node.js:提供了丰富的内置模块(如httpfspath等)和第三方模块(通过npm安装),可以进行文件操作、网络通信、加密解密、操作系统任务等。
  • 浏览器:浏览器提供了DOM操作相关的API(如documentElementXMLHttpRequestFetch API等),以及许多Web API(如Canvas、WebGL、Web Audio等),用于操作网页内容和实现丰富的客户端功能。

5. 事件循环和异步

浏览器中JS事件循环:

  • 宏任务:setTimeout,setInterval,sctipt整体代码
  • 微任务:promise.then,MutationObserver
  • 执行顺序:一个宏任务(sctipt整体代码)--->清空微任务队列--->页面渲染--->webWorker任务--->一个宏任务

NodeJS的事件循环

NodeJS中,事件循环是基于libuv实现,libuv是一个多平台的专注于异步IO的库。

Node中所有异步的API

  1. 定时器API---Timer队列

    • setTimeout(callback, delay, [arg1], [arg2], ...)
    • setInterval(callback, delay, [arg1], [arg2], ...)
  1. I/O操作API---Poll队列

    • 文件读写
    • 数据库操作
    • 网络请求
  1. node独有---Check队列和nextTick队列

    • nexprocess.nextTick(callback, [arg1], [arg2], ...)
    • setImmediate(callback, [arg1], [arg2], ...)

相应队列

不同的异步API对应的队列在前边已经标出,具体再看看每个队列的作用:

  1. Timer队列:处理定时器回调
  1. Poll队列:处理I/O操作回调,事件循环在空闲的情况下(空闲的情况指的是Timer队列和Check队列为空)在这里暂停,等待新的IO事件(更快处理客户端请求)
  1. Check队列:处理setImmediate回调。
  1. nextTick队列:用于处理process.nextTick回调。在启动事件循环前检测nextTick队列是否为空,若不为空则清空后进入事件循环

注意,在node中,setTimeoutdelay的最小取值是1,即使人为设置为0,最快也要1ms才会加入到Timer队列。

如果系统运行足够快setImmediate会先于setTimeout。当然若系统运行不够快结果就是相反的。

若要确定二者的执行顺序,需要将二者放到一个I/O操作中,在Poll中进行处理:

fs.readFile(__filename,()=>{
  setTimeout(()=>{
    console.log('setTimeout')
  },0);
  setImmediate(()=>{
    console.log('setImmediate')
  })
})

NodeJS中,Timer---Poll---Check称为一个tick,process.nextTick作用就是将回调函数放到每个tick的头部。这样process.nextTick包裹的函数会在下一个tick之前执行。

微任务队列

微任务队列在nextTick队列之后,tick之前,即:nextTick---微任务--Timer---Poll---Check

测试一下

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}

async function async2() {
    console.log('async2')
}

console.log('script start')

setTimeout(function () {
    console.log('setTimeout0')
}, 0)

setTimeout(function () {
    console.log('setTimeout2')
}, 300)

setImmediate(() => console.log('setImmediate'));

process.nextTick(() => console.log('nextTick1'));

async1();

process.nextTick(() => console.log('nextTick2'));

new Promise(function (resolve) {
    console.log('promise1')
    resolve();
    console.log('promise2')
}).then(function () {
    console.log('promise3')
})

console.log('script end')

/**
script start
async1 start
async2
promise1
promise2
script end
nextTick1
nextTick2
async1 end --- 微任务前去执行await后的内容
promise3
setTimeout0
setImmediate
setTimeout2---有300ms延迟,所以晚于setImmediate
 */