Node相关问题

324 阅读8分钟

问题

1.什么是Node.js,有哪些特征?于其他服务器端对比?有哪些缺点?

简单来说Node.js是运行在服务端的JavaScript。是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行JavaScript的速度非常快,性能非常好。用于搭建高性能的web服务器。

特征:

  • 它是一个JavaScript运行环境
  • 依赖于Chrome V8引擎进行代码解释
  • 事件驱动
  • 非阻塞I/O
  • 轻量,可伸缩,适于实时数据交互应用
  • 单进程,单线程

对比:

  • node无法直接渲染静态页面,提供静态服务
  • node没有根目录的概念
  • node必须通过路由程序指定文件才能渲染文件
  • node比其他服务端性能更好,速度更快

缺点:

  • 不适合CPU密集型应用:由于单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起

    解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起。

  • 只支持单核CPU,不能充分利用CPU

  • 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃。

    原因:单进程,单线程。

    解决方案:Nginx反向代理,负载均衡,开多个进程,绑定多个端口。开多个进程监听同一个端口,使用cluster模块。

  • 开源组件库质量参差不齐,更新快,向下不兼容

  • Debug不方便,错误没有stack trace

2.介绍一下Node事件循环Event Loop的流程

Node.js.是单线程应用程序,通过V8引擎提供的异步执行回调接口,可以处理大量的并发,性能非常高。几乎每一个API都支持回调函数。

  • 在进程启动时,Node便会创建一个类似while(true)的循环,每执行一次循环体的过程我们称为Tick。
  • 每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及相关回调函数。然后进入下一个循环,如果不再有事件处理,就退出循环。

3.什么是事件驱动模型(非阻塞I/O)?

当服务端接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。当第一个请求处理完毕后,就放回处理队列,当到达队列开头,就返回结果给用户。

好处:高效,扩展性强,因为服务端一直接受请求而不等待任何读写操作。(也称之为非阻塞I/O或者事件驱动I/O)

4.如何判断当前脚本运行在浏览器还是node环境中?

通过判断global对象是否为window,如果不是,则没有运行在浏览器中。

5.Node.js能解决什么问题?

对于PHP,JAVA,Python等服务端语言来说,为每个客户端连接都需要创建一个新的线程,而每个线程需要大约2M的内存,理论上,一台具有8G内存的服务器可以同时连接的最大用户数为4000个左右,要让web应用程序支持更多的用户,就需要增加服务器的数量,这样就增加了硬件的成本。服务器所能支持的同时连接最大用户量是一个主要的瓶颈。

6.Node.js的两种机制

  1. 非阻塞型I/O
  2. 事件循环

Node入门

模块化

引用,定义,标识。

模块定义

在 Node 中,一个js文件就是一个模块。定义一个模块就是创建一个js。 在 Node 中,每一个js文件中的js代码都是独立运行在一个函数中。而不是全局作用于,所以一个模块中的变量和函数在其他模块中无法访问。我们可以通过exports来向外部暴露变量和方法,只需要将需要暴露给外部的变量或方法设置为exports的属性即可。

exports.x = "我是x"
exports.y = "我是y"
exports.fn = function() {
    
}

模块引用

通过 require() 函数来引入外部的模块。如果使用相对路径,必须以 . 或 .. 开头。使用 require() 引入模块后,该函数会返回一个对象,这个对象代表的是引入的模块。

模块标识

我们使用 require() 引入外部模块时,使用的就是模块标识,我们可以通过模块标识来找到我们指定的模块。

模块分为两大类:

  • 核心模块:由 node 引擎提供的模块,核心模块的标识就是模块的名字。
var fs = require("fs");
  • 文件模块:由用户自己创建的模块。文件模块的标识就是文件的路径(绝对,相对)。相对路径使用 ./ 或 ../ 开头
var md = require("./md.js");

声明全局标量

在node中有一个全局对象 global,它的作用和网页中的window类似。
在全局中创建的变量都会作为global的属性保存。
在全局中创建的函数都会作为global的方法保存。

在函数中定义一个全局变量:

a = 10;
console.log(global.a);//10

var a = 10;//依然是局部标量
console.log(global.a);//undefined

番外

arguments.callee:这个属性保存的是当前执行的函数对象

console.log(arguments.callee)//[Function]

console.log(arguments.callee+"")//拼串会调用 toString,把函数结果打印出来

实际上模块中的代码都是包装在一个函数中执行的,并且在函数执行时,同时传递了5个实参。

//当 node 在执行模块中的代码时,它会首先在代码的最顶部,添加如下代码:
function (exports, require, module, _filename, _dirname){

//在代码的最底部,添加如下代码
}
  • exports:该对象用来将变量或函数暴露到外部
  • require:是函数,用来引入外部的模块
  • module:module代表的是当前模块本身,exports就是module的属性。也可用module.exports导出。
    module.exports == exports
  • _filename:当前模块的完整路径
  • _dirname:当前模块所在文件夹的完整路径

exports和module.exports区别

  • 通过exports只能使用 . 的方式来向外暴露内部变量。
  • 而module.exports既可以通过 . 的形式,也可用直接赋值。module.exports={}

Buffer(缓冲区):存储二进制数据

Buffer的结构和数组很想,操作的方法也和数组类似。
数组中不能存储二进制的文件,而Buffer就是专门用来存储二进制数据。
使用buffer不需要引入模块,可直接使用。
在buffer中存储的都是二进制数据,但是在显示时都是以16进制的形式。 buffer中的每一个元素的范围是从 00 - ff(十进制的 0 - 255)(二进制的 00000000 - 11111111),占内存的8位也就是一个字节
Buffer的大小一旦确定,则不能修改。uBffer实际上是对底层内存的直接操作。

计算机中 一个 0 或一个 1 我们称为1位(bit)
8bit = 1byte(字节)
1024byte = 1kb 1024kb = 1mb

  • 从结构上看Buffer非常像一个数组,它的元素为16进制的两位数。
  • 实际上一个元素就表示内存中的一个字节。
  • 实际上Buffer中的内存不是通过JS分配的,而是在底层通过C++申请的。
  • 也就是我们可以直接通过Buffer来创建内存中的空间。

将一个字符串转换成buffer:Buffer.from(str)

var str = "hello";
//将一个字符串保存在buffer中
var buf = Buffer.from(str);
console.log(buf); //<Buffer 68 65 6c 6c 6f>

var cstr = "你好"
console.log(buf.length); // 6 占用内存的大小  一个汉字占用3个字节
console.log(str.length); // 2 字符串的长度

创建一个指定大小的buffer:Buffer.alloc(size) 分配空间并清空数据

var buf2 = Buffer.alloc(5) //创建一个5字节的buffer
//通过索引来操作buf中的元素
buf2[0] = 88; // <Buffer 58 00 00 00 00>   58是88的十六进制
buf2[1] = 0xaa; // <Buffer 58 aa 00 00 00>

//只要数字在控制台或页面输出,一定是10进制
console.log(buf2[1]) // 170

//转换成16进制字符串
console.log(buf2[1].toString(16)) // aa

// 遍历和数组操作一样,输出也是10进制
for(var i = 0; i < buf2.length; i++){
    console.log(buf2[i])
}

创建一个指定大小的buffer,但Buffer中可能含有敏感数据:Buffer.allocUnsafe(size)

var buf2 = Buffer.alloc(5) //分配空间并清空数据
console.log(buf2) //<Buffer 00 00 00 00 00>

var buf3 = Buffer.allocUnsafe(5) // 分配空间,但不清空数据
console.log(buf3) //<Buffer 21 00 ac 00 00>

将缓冲区中的数据转换为字符串:buf.toString()

var buf4 = Buffer.from("我爱你呀") // 将一个字符串转换成buffe
console.log(buf4.toString()) // 我爱你呀

fs(文件系统File System):通过Node来操作系统中的文件