node primer

文章目录
  1. 什么是NodeJS?
    JS本身是一种完整的语言,可以在不同的上下文中执行,浏览器提供了一个上下文,nodejs事实上也是,它使用Google的V8虚拟机,来解释和执行js,使得js可以脱离浏览器环境在后端运行js代码。
    • NodeJS是一个JS脚本解析器,任何操作系统下安装NodeJS本质上做的事情都是把NodeJS执行程序复制到一个目录,然后保证在这个目录在系统PATH环境变量下,以便终端下可以使用node命令。
    • 终端下直接输入node命令可以进入命令交互模式,很适合用来测试一些JS代码片段,比如正则表达式。
    • NodeJS使用CMD模范。主模块作为程序入口点,所有模块在执行过程中只初始化一次,无论require几次。
    • 相较浏览器js解析引擎来说,NodeJS可以解析JS代码,没有浏览器安全级的限制,还提供系统级别的API:如文件的读写、进程的管理、网络通信。
  2. 什么是模块?
    • 在JS中我们可以通过一个var、一个function定义一个全局的函数或者变量,但是有多人协作的时候,或者大量的JS文件批量的引入到一个页面的时候,很容易出现变量被覆盖,方法被重写,特别是存在一些依赖关系的时候,很容易导致页面出错。这是因为JS天生就缺乏一种模块隔离工具,来隔离实现不同功能的JS片段,避免它们相互污染。为此我们经常采用命名空间的方式,把变量和函数限制在某个特定的作用域内,人肉约定一套命名规范来约束代码,从而保证代码的安全执行。比如JQuery里面,有很多的方法,但是你直接是访问不到的,必须调用&符号,来使用trim方法、对dom节点的处理等等。
    • NodeJS通过实现CommonJS的Modules/1.0标准引入了模块(module)概念,模块是NodeJS的基本组成部分。NodeJS模块分为3类,原生(核心)模块(http、fs、path)、文件模块、第三方模块(相对路径、绝对路径、文件名)。核心模块会在Node启动时被预先加载。一个node.js文件就是一个模块,文件路径就是文件名,也就是说文件和模块是一一对应的关系。文件模块分为3类,通过后缀区分,根据后缀决定加载方法(.js、.node、.json)。这个文件可以是JS代码(添加头尾后执行得到Mudule.exports)、JSON(通过fs.readFileSync()进行加载,然后通过JSON.parse()解析得到的对象作为Mudule.exports)或者.node(在不同的平台下,需要重新的编译)。
    • Node提供了exports和require两个对象,其中exports是模块公开的接口,require用于从外部截取一个接口,以及所获取模块的exports。一个模块可以通过module.exports或exports将函数变量等导出,以使其他js脚本通过require()等函数引入并使用。module.exports的用途是替换当前模块的导出对象,例如模块导出对象是一个普通对象,如果想改为一个函数的话,用module.exports覆盖。
  3. Node.js单线程、非阻塞的事件编程模式。
    • 什么是回调:回调时异步编程最基本的方法。对NodeJS来说需要按顺序执行异步逻辑的时候,一般采用后续传递的方式,也就是将后续逻辑封装在回调函数中,作为起始函数的参数,逐层去嵌套。通过这种方式,来让程序按照我们期望的方式走完整个流程。
    • 什么是IO:磁盘的写入和读出,数据的进和出,在nodejs中本质上就是为了文件系统,数据库这些资源提供接口,向文件系统发送一个请求的时候,不用等待硬盘,等硬盘准备好的时候,非阻塞接口会通知到Node。
    • 什么是非阻塞什么是阻塞:对于单线程NodeJS来说可以通过回调的异步编程的方式来达到非阻塞的效果。线程在执行中如果遇到磁盘读写或网络通信(统称I/O接口),通常要耗费较长的时间,这时操作系统会剥夺这个线程的CPU控制权,使其暂停执行,同时将资源让给其他工作线程。这种线程调度方式成为阻塞。与I/O操作完毕时,操作系统将这个线程的阻塞状态解除,恢复其对CPU的控制权,另其继续执行。这种I/O模式是通常的同步式I/O(Synchronous I/O)或阻塞式(blocking I/O).当线程遇到I/O操作时,不会以阻塞的形式等待I/O操作的完成或数据的返回,而是将I/O请求发送给操作系统,继续执行下一条语句。当操作系统完成I/O操作时,以事件的形式通知执行I/O操作的线程,线程会在特定的时候处理这个事件。
      为了处理一部I/O,线程必须有时间循环机制,不断的检查有没有微处理的事件,依次允以处理。阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而在非阻塞模式下,一个线程永远在执行计算操作,这个线程所使用的CPU核心利用率永远是100%,I/O以事件的方式通知。在阻塞模式下,多线程往往能提高系统吞吐量,因为一个线程阻塞时,还有其它线程在工作,多线程可以让CPU资源不被阻塞中的线程浪费。而在非阻塞模式下,线程不会被I/O阻塞,永远在利用CPU。多线程带来的好处仅仅是在多核CPU的情况下利用更多的核,而node.js的单线程也能带来同样的好处。所以Node.js使用单线程、非阻塞的编程模式。 将处理部分写在http.createServer中,按照传统思路,启动服务器后,遇到这段代码会去运行。如果运行时间很长,导致暂停,非常没有效率,如果第二位用户请求服务器,它仍在服务第一个请求,那第二个请求只能在第一个完成之后再应答,这就是阻塞式的SocketIO的问题。
    • 什么是事件、什么是事件驱动:基于事件驱动的回调,NodeJS一事件驱动著名,通过异步的编程达到高吞吐量,高性能的效果。异步编程的直接体现就是回调,但不能说回调后程序就异步化了。请求任何时候都可能到达,但是我们服务器却跑在一个单进程。回调:我们给某个方法传递了一个函数,这个方法在相应事件发生时调用函数来进行回调。(建立了一个监听8888端口的服务器,当有这个服务器的请求发生时,就调用这个方法。)
    • 事件轮询机制:NodeJS的事件循环是靠一个单线程不断查询队列中是否有事件,当他读取到一个事件时,将调用与这个事件关联的javaScript函数。如果这个函数是执行一个IO操作,比如侦听一下8888端口是否有socket链接,NodeJS会启动这个IO操作,但不会等待IO操作结束,而是继续到队列中查看是否有下一个事件,如果有,就处理。
  4. 命令行程序
    DOS–Disk Operating System 磁盘操作系统,主要是一种面向磁盘的系统软件。基本功能是:
    • 执行命令和程序功能;
    • I/O管理功能;
    • 磁盘与文件管理功能;
      DOS界面就是输入win+R -> cmd之后出现的界面,文字的,运行程序必须在这样的界面上输入一条命令,命令是一个字符串,用回车键结束。所以命令行是一行,所以叫命令行,能在DOS界面上运行的程序,就是命令行程序。
  5. 一些模板
    • 文件模板
      Buffer(数据块):提供对二进制数据的操作
      Stream(数据流):当内存中无法一次装下需要处理的数据时,或者一边读取,一边处更加高效时,用到数据流,提供对数据流的操作
      File System(fs 文件操作系统):文件属性读写(fs.sart、fs.chmod)文件内容读(fs.readfile、fs.readdir、fs.writefile)底层文件操作(fs.open、fs.readfs.write)
      Path(路径):简化文件路径相关操作。
    • 网络操作
      HTTP:创建http服务器,监听http客户端请求并返回响应,可以把信息解析成请求头请求体,但不去解析具体的内容。
      HTTPS:需要额外处理SSL证书
      URL:处理http请求时,该模块允许解析URL,生成URL、拼接URL。三个方法(url.parse(urlStr[, parseQueryString][, slashesDenoteHost])url.format(urlObj)url.resolve(from, to))
      QueryString:查询,实现URL参数字符串与参数对象的互相转换,一个和参数相关的帮助类,node.js原生自带,直接require(‘querystring’) 即可使用.此类一共包括4个方法:序列化querystring.stringify(obj, [sep], [eq])、解析querystring.parse(str, [sep], [eq], [options])、转译中文字符querystring.escape、反转译querystring.unescape .[内参数]表示可选参数, [sep]指分隔符 默认& , [eq]指分配符 默认=。
      Zlib:提供数据压缩和解压的功能
      Net:用于创建Socket客户端和Socket服务器。
    • 进程管理
      Process:任何一个进程都有启动进程时使用的命令行参数,有标准输入、标准输出,有运行环境和在NodeJS中,可以通过Process对象感知和控制NodeJS自身进程的方方面面。process不是内置模块,而是一个全局对象,因此在任何地方都可以直接使用。
      Child-Process:模块可以创建和控制子进程最核心.spawn
      Cluster:解决单进程NodeJS Web服务器无法充分利用多核CPU的问题。简化多进程服务器程序的开发,让每个核上运行一个工作进程,并统一通过主进程监听端口和分发请求。
  6. Buffer
    对于JS来说,对Unicode编码的数据很容易处理,但是对于二进制数据就没什么处理方法了,但在一些TCP数据流或者在操作一些文件数据流时,字节流的处理方法是必须的。所以nodeJS提供与String类对等的全局构造函数Buffer–全局自然不用require了,buffer是node中存储二进制数据的中介者。缓存区

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //创建Buffer实例
    var a = new Buffer(10)//size
    a.length//10
    var b = new Buffer([Ox68,Ox65,Ox6c,Ox6c,Ox6f]);
    b;//<Buffer 68 65 6c 6c 6f>
    b.toString();//'hello'
    //给予某一特定数据类型的数据,以及其编码类型(默认为uft8)
    var c = new Buffer('hello')
    c;//<Buffer 68 65 6c 6c 6f>
    c.toString();//'hello'
    var d = new Buffer('hello','utf8')

    Buffer实例类似于整数数组,与字符串是有一定差别的,JS中字符串可以视为只读的,因为每次的修改、复制都会得到一个新的字符串,不会影响之前的字符串(按值传递,对象类似于指针,按址传递)。Buffer与数组也有区别

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var a=[1,2,3,4];
    var b= a.slice(2);
    a;//[1,2,3,4]
    b;//[3,4]
    b[0]=0;
    a;//[1,2,3,4]
    b;//[0,4]
    var a = new Buffer('hello');
    var b= a.slice(3);
    a;//<Buffer 68 65 6c 6c 6f>
    b;//<Buffer 6c 6f>
    b[0]=ox6f;
    a;//<Buffer 68 65 6c 6f 6f>
    b; //<Buffer 6f 6f >

    在将创建的Buffer截取一段之后,对返回的Buffer段进行修改还是会影响原Buffer。类似于Buffer是一个存储对象的数组,数组中非原始值,而是引用值。如果希望有一份独立的Buffer拷贝,只能通过新建一个长度相等的Buffer,在原Buffer上调用Buffer.copy(target Buffer,[targetStart],[souceStart]);

    1
    2
    3
    var a = new Buffer('hello');
    var c = new Buffer(a.length);
    a.copy(c);

    在node中,许多地方均是用Buffer类型表示数据流,以便来处理一些二进制数据(如读取文件数据,http中post传递的数据)

    1
    2
    3
    4
    5
    var fs = require('fs');
    var rs = fs.createReadStream('chinese.md');
    var dataArr = [],len,data;
    rs.on('data',function(chunk){dataArr.push(chunk);len+=chunk.length});
    rs.end('end',function(){data =Buffer.concat(dataArr,len.toString);console.log(data)});

    一个英文字符占一个Buffer单位,一个中文字符占3个Buffer单位,在数据流文侩的时候,将一个中文的三个标识分开在做toString的时候会出现乱码的现象。在拿到分块数据的时候千万不能直接进行类似的toString式转移,将每段Buffer保存,最后在合并成一个大的Buffer在转义。

  7. Node入门书介绍
    带领新手入门Node,完成一个完成的web应用,允许用户浏览页面以及上传文件。使用路由、模块、request、response。讨论如何创建一个框架来对我们的应用的不同的模块进行干净的剥离。

    1
    2
    3
    4
    5
    6
    7
    var http = require("http");
    http.creactServer(function(request,response){
    response.writeHead(200,{"Content-Type":"text/plain"});
    response.writeHead(200,{"hello world!");
    response.end();

    }).listen(8888);
  8. NodeJS事件模块
    NodeJS 没有浏览器的事件冒泡,事件捕获机制,实现了event这个模块,且node中大多数模块都集成了这个模块。只对外暴露一个对象, eventEmitter。对象有两个方法,event.emit–事件发射;event.listener 事件监听。可最多向一个事件添加10个监听(回调方法)。new EventEmitter()对象,具有.on()和.emit()函数,分别负责监听(收)和发出事件。instanc.setMaxListeners(11)设置每个事件的最大监听器个数,默认是10。删除listener不能用匿名函数,要定义一个变量表示函数.emit 方法的返回值为true 或 false, 如果事件监听过为true,没有监听过为false.两种方法获取某一事件的监听器个数:1、instanc.listeners('事件名称').length;2.EventEmitter.listenerCount(instanc, '事件名称').

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var EventEmitter=require('events').EventEmitter;
    //实例
    var instanc=new EventEmitter;
    //接下来可以让 EventEmitter 或让其发射事件
    instanc.on('event',function(param){})
    instanc.emit('event','param');
    //添加监听
    instanc.addEventListener('event',function(){});
    /*
    *1、一次删除某EventEmitter对象的所有事件的所有监听器: *instanc.removeAllListeners();
    *2.一次删除某EventEmitter对象的某一事件的所有监听器:
    *instanc.removeAllListeners('事件名称')
    */

  9. HTTP模块 request
    HTTP - get / request
    http的api里面get就是对request的封装, get能做的request都能做
    http.request(options,[callback])
    host:主机名
    hostname:host别名
    port: 端口号(8080)
    localAddress:绑定本地连接的接口
    method: http请求的字符串(get)
    path:请求的根路径 /
    headers:请求头的对象
    auth:计算认证头的基本认证
    agent:控制agent行为
    keepAlive:保存资源池周围套接字在未来能继续用于其他请求(false)

  10. 作用域和上下文
    作用域与调用函数、访问变量的能力有关,上下文总是this这个关键字有关,是调用当前可执行代码的引用。
    作用域分为:局部作用域和全局作用域,同时作用域往往与变量有关系,处在在局部作用域里可以访问到全局作用域的变量,但在局部作用域外面就访问不到局部作用里面所设定的变量。
    上下文:this总是指向调用这个的方法的对象;js里的this 通常是当前函数的拥有者;this 是js的一个关键字,代表函数运行时自动生成的一个内部对象,只能在函数内部使用.

    1.作为对象的方法
    this在方法内部,this就指向调用这个方法的对象
    2.函数的调用
    this指向执行环境中的全局对象(浏览器->window nodejs->global)
    3.构造函数
    this所在的方法被实例对象所调用,那么this就指向这个实例对象

    更改上下文方法(更改this指向的内容,可方便地实现继承):
    call(list);apply(array);根据call()、apply()改变上下文this指向的特性,也可以方便实现继承
    路由:通过url路径来区分不同的请求,以url路径为基准映射到处理程序上。是指我们要针对不同的URL有不同的处理方式,从一个接口上接收到数据包,根据数据包的目的地址进行定向,并转发到一个接口的过程。