Node基础知识笔记

时间:2021-6-19 作者:admin

全文目录

Node.js简介

  1. Node.js就是运行在服务器端的JavaScript,Node.js是一个基于Chorme JavaScript运行时建立的一个平台。
  2. Node.js时一个事件驱动I/O服务器端JavaScript环境,性能很好

Node 应用简介

  1. 使用Node建立一个应用时,还同时实现了整个HTTP服务器,实际上,Web应用以及对应的Web服务器基本是一样的。
  2. Node.js应用的组成包含三部分:引入require模块,创建服务器,接收请求与相应请求
  3. 引入require模块:我们可以使用require指令来载入Node.js模块
  4. 创建服务器:服务器可以监听客服端的请求,类似与Nginx等HTTP服务器。
  5. 接收请求与响应请求:服务器很容易创建,客户端可以使用浏览器或者终端发送HTTP请求,服务器接受请求后返回响应数据。

Node创建的hello world

  1. 创建一个server.js文件,输入以下内容
    var http = require('http');
    
    http.createServer(function (request, response) {
    
        // 发送 HTTP 头部 
        // HTTP 状态值: 200 : OK
        // 内容类型: text/plain
        response.writeHead(200, {'Content-Type': 'text/plain'});
    
        // 发送响应数据 "Hello World"
        response.end('Hello World\n');
    }).listen(8888);
    
    // 终端打印如下信息
    console.log('Server running at http://127.0.0.1:8888/');
  1. 使用require指令来载入http模块,并将实例化的HTTP赋值给http
  2. 使用http.createServer()方法创建服务器,并使用listen方法绑定8888端口.函数通过request,response参数来接收和响应数据。
  3. 使用 node server.js 执行server.js文件,此时,在浏览器中访问http://127.0.0.1:8888/可以查看到hello world

npm知识点

npm 简介

  1. npm是随同node.js一起安装的报管理工具,能解决node.js代码部署上的很多问题。

npm的使用场景

  1. 使用npm可以从服务器端下载别人编写好的第三方包到本地使用
  2. 使用npm从服务器下载并安装别人编写的命令行程序到本地使用。
  3. 使用npm可以将自己编写的包或者命令行程序上传到npm服务器供别人使用

npm 命令集合

  1. 使用npm install 安装npm
  2. npm -v查看npm是否安装成功。
  3. npm install npm -g 来升级npm4.的版本。
  4. 使用npm配置淘宝镜像:npm install -g --registry=https://registry.npm.taobao.org.
  5. 注意:不用使用npm install -g cnpm --registry=https://registry.npm.taobao.org这种方式配置淘宝镜像,因为cnpm下载下来的第三方包的路径有时会很奇怪,会出现不可预知的错误!
  6. npm的安装分为本地安装local和全局安装globalnpm install express 本地安装express, npm install express -g 全局安装express.
  7. 如果出现此错误:npm err! Error: connect ECONNREFUSED 127.0.0.1:8087 ,解决办法为:$ npm config set proxy null
  8. npm list -g命令查看所有全局安装的模块!
  9. npm list grunt 查看某个模块的版本号.
  10. npm uninstall <Module Name> 卸载npm模块,卸载后,你可以到 /node_modules/ 目录下查看包是否还存在,或者使用以下命令查看:npm ls
  11. npm update <Module Name> 可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。。
  12. npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。
  13. npm search <Module Name> 搜索npm模块
  14. npm help <command>可查看某条命令的详细帮助,例如npm help install.
  15. npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。
  16. npm init 创建一个模板生成package.json。让我们使用 npm 产生 package.json。

npm的具体使用

  1. 使用npm安装的模板语法:npm install <Module Name>,比如使用npm安装常用的Node的web框架express
  2. 在安装好了之后,express包就放在了根目录下的 node_modules 目录中,只需要通过 var express = require('express');的方式就可以引用该包,无需指定第三方包的路径。
  3. npm install express -g,全局安装 express包,不加-g默认为本地安装。

npm 本地安装详解

  1. 将安装包放在./node_modules下,(运行npm命令时所在的目录),如果没有node_modules目录,会在当前执行npm命令时生成node_modules目录。
  2. 可以通过require()来引入本地安装的包。

npm 全局安装详解

  1. 全局安装在安装包放在/uer/local 或者 你的node安装目录中!!注意:node的安装路径中!不要使用中文!!!不要使用中文!!!一定不要使用中文!!如果使用中文,在具体使用第三方包时会出现错误。
  2. 全局安装可以直接在cmd打开的命令行中使用、

npm 安装时异常的处理

  1. 使用 npm 安 装node_modules总是提示报错:报错:npm resource busy or locked.....。可以先删除以前安装的node_modules使用npm cache clean,然后再重新安装:npm install .

第三方包知识点

第三方包简介

  1. package.json的使用说明:package.json 位于模块的目录下,用于定义包的属性。
  2. name-包名,
  3. version-包的版本号
  4. description-包的描述
  5. homepage-包的官网url
  6. author-包的作者姓名
  7. contributors – 包的其他贡献者姓名。
  8. dependencies – 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
  9. repository – 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
  10. main – main 字段指定了程序的主入口文件,require(‘moduleName’) 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。
  11. .keywords – 关键字

Node.js 回调函数

  1. 回调是一种异步相当于一个函数,回调函书被调用在完成既定任务,

回调函数

  1. Node.js的回调函数:Node.js异步编程的直接体现就是回调,异步编程依托于回调来实现,但是不能说使用了回调后程序就异步化了!回调函数再完成任务后就会被调用,Node使用了大量的回调函数,Node所有的API都支持回调函数。
    2.例如:一边读取文件,一边执行其他命令,再文件读取完成后,将文件内容作为回调函数的参数返回,这样再执行代码时就没有阻塞或者等待文件的I/O操作。这样提高了Node.js的性能,可以处理大量的并发请求。
  2. 回调函数一般作为函数的最后一个参数出现,function foo1(name, age, callbackName){},function foo2(value,callback1,callbackName){}

阻塞代码测试

  1. 在根目录下,创建一个input.txt文件,内容为:百度网址:www.baidu.com
  2. main.js文件中,代码如下:
    var fs = require('fs');在这里需要安装fs的包,如果不安装的话,会报错
    var data = fs.readFileSync('input.txt');
    console.log(data.toString());
    console.log("程序执行结束");
    
	在终端使用 node main.js。
	终端打印如下信息:
        百度网址:www.baidu.com
        程序执行结束
  1. 阻塞测试案例:是在文件读取完成后才执行程序,
  2. 阻塞时按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数.我们就需要写在回调函数内.

非阻塞代码测试

  1. main.js文件中的代码如下:
    var fs = require('fs');
    fs.readFile('input.txt',function(err,data){
        if (err) return console.error(err);
        console.log(data.toString());
    });
    console.log("程序执行结束");
    
	在终端使用 node main.js。
	终端打印如下信息:
        程序执行结束
        百度网址:www.baidu.com
  1. 非阻塞测试案例:我们不需要等待文件读取完,这样就可以在读取文件的同时执行接下来的代码.从而提高程序的性能。
  2. 阻塞时按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数.我们就需要写在回调函数内.

Node.js事件循环

  1. Node.js是单线程应用,但它通过事件和回调的概念支持并发。Node.js每个API都是异步的,作为一个单线程,node使用异步函数调用来维护并发。
  2. Node线程保持一个事件循环,每当任何一个任务完成后得到结果,node就触发通知事件侦听函数来执行相应的事件。
  3. Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

事件驱动程序

  1. Node.js使用事件驱动模型,每当web server接收到请求,就把它关闭进行处理,然后再去服务下一个web请求,党这个请求完成之后,它被放回处理队列,当到达队列开头,这个结果就被返回给用户。

  2. 事件驱动模型非常高效,可扩展性很强,因为web server一直接受请求而不等待任何读写操作(也成为非阻塞式IO或者事件驱动IO)

  3. 在事件驱动模型中,会生成一个主循环来监听事件,每当检测到事件时触发回调函数。

  4. 事件驱动流程图如下所示
    Node基础知识笔记

  5. Node.js有多个内置事件,通过引入events模块,实例化EventEmitter类来绑定和监听事件

    var events = require('events');
    
   
    var eventEmitter = new events.EventEmitter();  // 创建eventmitter对象
    
    // 创建事件处理程序
    var connectHandler = function connected(){
        console.log('连接成功');
        eventEmitter.emit('data_received');// 触发data_received 事件
    }
    
   
    eventEmitter.on('connection',connectHandler); // 绑定connection 事件处理程序
    
    
    eventEmitter.on('data_received',function(){ // 使用匿名函数绑定 data_received 事件
        console.log('数据接受成功');
    })
    
    
    eventEmitter.emit('connection');// 触发 connection 事件
    console.log('程序执行完毕');
    
    在终端执行 node main.js 输出结果:
    连接成功
    数据接受成功
    程序执行完毕

Node应用程序工作流程

  1. Node应用程序中,执行异步操作的函数将回调函数作为最后一个参数,回调函数接收错误对象作为第一个参数。
    var fs = require("fs");
    fs.readFile("input.txt",function(err,data){
        if(err){
            console.log(err.stack);
            return;
        }
        console.log(data.toString());
    })
    console.log("程序执行完毕")
  1. readFile()是异步函数用于读取文件,如果读取文件过程中发生错误,错误err对象就会输出错误信息。
  2. 如果没有发生错误就会跳过 err 对象的输出,文件内容就会通过回调函数输出、

进程和线程的概念

  1. 进程是指系统中正在运行的一个应用程序,程序一旦运行就是进程,进程是系统进行资源分配的独立实体,每个进行拥有独立的地址空间。
  2. 一个进程之间可以拥有多个线程,每个线程使用其所属进程的栈空间
  3. 线程是进程的一个实体,是进程的一条执行路径,线程是CPU独立运行和独立调度的基本单位。

Node EventEmitter 事件发射器详解

  1. Node所有的异步I/O操作在完成时都会发送一个事件到事件队列
  2. Node里面很多对象都会分发事件,一个 net.Server 对象会在每次有新连接时触发一个事件,
  3. 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

Node EventEmitter 类`

  1. events 模块只提供了一个对象: events.EventEmitter
  2. EventEmitter 的核心就是事件触发与事件监听器功能的封装。
  3. EventEmitter 对象如果在实例化时发生错误,会触发 error 事件,当添加新的监听器时,newListener事件会被触发,当监听器被移除时,removeListener事件被触发。
    实例一:简单介绍EventEmitter的用法
    
    var EventEmitter = require('events').EventEmitter; 
    var event = new EventEmitter(); 
    event.on('some_event', function() { 
        console.log('some_event 事件触发'); 
    }); 
    setTimeout(function() { 
        event.emit('some_event'); 
    }, 1000);1秒后,输出some_event 事件触发,这是因为event对象注册了事件some_event的一个监听器,然后通过setTimeout在1秒后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。
  1. EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。
  2. 对于每个事件,EventEmitter 支持 若干个事件监听器。当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
    实例二:EventEmitter绑定事件和触发事件
    var events = require('events'); 
    var emitter = new events.EventEmitter(); 
    emitter.on('someEvent', function(arg1, arg2) { 
        console.log('listener1', arg1, arg2); 
    }); 
    emitter.on('someEvent', function(arg1, arg2) { 
        console.log('listener2', arg1, arg2); 
    }); 
    emitter.emit('someEvent', 'arg1 参数 A', 'arg2 参数 B'); 

emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。

EventEmitter 属性介绍:

  1. addListener(event,listener) 为指定事件添加一个监听器到监听器数据的尾部

  2. on(event,listener)为指定事件注册一个监听器,接受一个字符串event和一个回调函数

    server.on('connection', function (stream) {
        console.log('someone connected!');
    });
    
  3. once(event,listener)为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器

       server.once('connection', function (stream) {
         console.log('Ah, we have our first user!');
        });
    
  4. removeListener(event, listener) 移除指定事件的某个监听器,监听器必须时该事件已经注册过的监听器removeListener(event,listener)接受两个参数,第一个是事件名称,第二个是回调函数名称

     var callback = function(stream) {
              console.log('someone connected!');
        };
          server.on('connection', callback);
          server.removeListener('connection', callback);
    
  5. removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。

  6. setMaxListeners(n)默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于改变监听器的默认限制的数量。

  7. listeners(event) 返回指定事件的监听器数组

  8. emit(event,[arg1],[arg2],[...]) 按照监听器的顺序执行每个监听器,如果事件有注册监听返回true,否则返回 false

EventEmitter 方法介绍:

  1. events.emitter.listenerCount(eventName)//推荐返回指定事件的监听器数量。

EventEmitter 事件介绍

  1. newListener(event,listener) 在添加新监听器时被触发,event是字符串,事件名称,listener 处理事件函数

  2. removeListener(event,listener)从指定监听器数组中删除一个监听器,此操作将会改变处于被删监听器之后的那些监听器的索引。

    实例三:通过 connection(连接)事件演示了 EventEmitter 类的应用。
    var events = require('events');
    var eventEmitter = new events.EventEmitter();
    
    // 监听器 #1
    var listener1 = function listener1() {
       console.log('监听器 listener1 执行。');
    }
    
    // 监听器 #2
    var listener2 = function listener2() {
      console.log('监听器 listener2 执行。');
    }
    
    // 绑定 connection 事件,处理函数为 listener1 
    eventEmitter.addListener('connection', listener1);
    
    // 绑定 connection 事件,处理函数为 listener2
    eventEmitter.on('connection', listener2);
    
    var eventListeners = eventEmitter.listenerCount('connection');
    console.log(eventListeners + " 个监听器监听连接事件。");
    
    // 处理 connection 事件 
    eventEmitter.emit('connection');
    
    // 移除监绑定的 listener1 函数
    eventEmitter.removeListener('connection', listener1);
    console.log("listener1 不再受监听。");
    
    // 触发连接事件
    eventEmitter.emit('connection');
    
    eventListeners = eventEmitter.listenerCount('connection');
    console.log(eventListeners + " 个监听器监听连接事件。");
    
    console.log("程序执行完毕。");
    
    输出结果为:
    2 个监听器监听连接事件。
    监听器 listener1 执行。
    监听器 listener2 执行。
    listener1 不再受监听。
    监听器 listener2 执行。
    1 个监听器监听连接事件。
    程序执行完毕。

error事件介绍

  1. EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发error 事件。
  2. error 被触发时,EventEmitter规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
  3. 一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。

继承 EventEmitter

  1. 我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
  2. 具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。
  3. JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

Node Buffer(缓冲区)知识点

  1. JS语言本身只有字符串数据类型,没有二进制数据类型,但是在处理TCP流或者文件流的时候,必须用到二进制数据,因此在Node中定义了一个Buffer类,用来创建一个专门存放二进制数据的缓存区。
    2.“Buffer类是Node的核心库,Buffer为node带来了一种存储元素数据的方法,从而可以让Node处理二进制数据,每当Node中处理I/O操作中移动数据的时候,就可以使用Buffer`类。
  2. 原始数据存储在Buffer类的实例中,一个Buffer类相当于一个整数数组.
  3. 使用 Buffer.from() 接口去创建Buffer对象。

Buffer与字符编码

  1. Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。
  2. 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。
    const buf = Buffer.from('runoob', 'ascii');
    
    // 输出 72756e6f6f62
    console.log(buf.toString('hex'));
    
    // 输出 cnVub29i
    console.log(buf.toString('base64'));

Node支持的字符编码

  1. ascii – 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。
  2. utf8 – 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。
  3. utf16le – 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。
  4. ucs2 – utf16le 的别名
  5. base64 – Base64 编码。
  6. latin1 – 一种把 Buffer 编码成一字节编码的字符串的方式。
  7. binary – latin1 的别名。
  8. hex – 将每个字节编码为两个十六进制字符。

Buffer 提供了 API 来创建 Buffer 类:

  1. Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
  2. Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
  3. Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
  4. Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
  5. Buffer.from(buffer):复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
  6. Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例

利用Buffer写入Node缓冲区

  1. 写入 Node 缓冲区的语法:buf.write(string[, offset[, length]][, encoding]).
  2. string – 写入缓冲区的字符串。
  3. offset– 缓冲区开始写入的索引值,默认为 0 。
  4. length – 写入的字节数,默认为 buffer.length
    5.encoding – 使用的编码。默认为 ‘utf8’ 。
  5. 如果 buf没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。
  6. 返回实际写入的大小。如果buffer 空间不足, 则只会写入部分字符串。
    实例一:简单的缓冲区演示
    buf = Buffer.alloc(256);
    len = buf.write("www.baidu.com");
    
    console.log("写入字节数 : "+  len);

利用Buffer从缓冲区中读取数据

  1. 读取 Node 缓冲区数据的语法:buf.toString([encoding[, start[, end]]])
  2. encoding– 使用的编码。默认为 ‘utf8’ 。
    3.start – 指定开始读取的索引位置,默认为 0。
  3. end– 结束位置,默认为缓冲区的末尾。
  4. 返回值为:解码缓冲区数据并使用指定的编码返回字符串。
实例一:从缓冲区中读取数据的演示
    buf = Buffer.alloc(26);
    for (var i = 0 ; i < 26 ; i++) {
      buf[i] = i + 97;
    }
    
    console.log( buf.toString('ascii'));       // 输出: abcdefghijklmnopqrstuvwxyz
    console.log( buf.toString('ascii',0,5));   //使用 'ascii' 编码, 并输出: abcde
    console.log( buf.toString('utf8',0,5));    // 使用 'utf8' 编码, 并输出: abcde
    console.log( buf.toString(undefined,0,5)); // 使用默认的 'utf8' 编码, 并输出: abcde

将 Buffer 转换为 JSON 对象

  1. Node Buffer转换为 JSON 对象的函数语法格式:buf.toJSON()
  2. 当字符串化一个 Buffer实例时,JSON.stringify()会隐式地调用该 toJSON()
  3. 返回值为JSON对象
实例一:将 Buffer 转换为 JSON 对象
    const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
    const json = JSON.stringify(buf);
    
    // 输出: {"type":"Buffer","data":[1,2,3,4,5]}
    console.log(json);
    
    const copy = JSON.parse(json, (key, value) => {
      return value && value.type === 'Buffer' ?
        Buffer.from(value.data) :
        value;
    });
    
    // 输出: <Buffer 01 02 03 04 05>
    console.log(copy);

Buffer 缓冲区合并

  1. Node 缓冲区合并的语法:Buffer.concat(list[, totalLength])
  2. list – 用于合并的 Buffer对象数组列表。
  3. totalLength – 指定合并后Buffer对象的总长度。
  4. 返回一个多个成员合并的新 Buffer对象。
实例一:简单的Buffer缓冲区合并
    var buffer1 = Buffer.from(('百度'));
    var buffer2 = Buffer.from(('www.baidu.com'));
    var buffer3 = Buffer.concat([buffer1,buffer2]);
    console.log("buffer3 内容: " + buffer3.toString());

Buffer 缓冲区比较

  1. 缓冲区比较语法:buf.compare(otherBuffer);
  2. otherBuffer– 与 buf对象比较的另外一个 Buffer 对象。
  3. 返回一个数字,表示 buf在 `otherBuffer 之前,之后或相同。
实例一:Buffer缓冲区比较
    var buffer1 = Buffer.from('ABC');
    var buffer2 = Buffer.from('ABCD');
    var result = buffer1.compare(buffer2);
    
    if(result < 0) {
       console.log(buffer1 + " 在 " + buffer2 + "之前");
    }else if(result == 0){
       console.log(buffer1 + " 与 " + buffer2 + "相同");
    }else {
       console.log(buffer1 + " 在 " + buffer2 + "之后");
    }
输出结果为;ABC在ABCD之前

Buffer拷贝缓冲区

  1. Node 缓冲区拷贝语法:buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
  2. targetBuffer– 要拷贝的 Buffer 对象。
  3. targetStart – 数字, 可选, 默认: 0
  4. sourceStart – 数字, 可选, 默认: 0
  5. sourceStart – 数字, 可选, 默认: 0
  6. 没有返回值
实例一:Buffer拷贝缓冲区
    var buf1 = Buffer.from('abc');
    var buf2 = Buffer.from('BAIDU');
    
    //将 buf2 插入到 buf1 指定位置上
    buf2.copy(buf1, 2);
    
    console.log(buf1.toString());
输出结果为:abBAIDUc

Buffer 缓冲区裁剪

  1. Node 缓冲区裁剪语法:buf.slice([start[, end]])
  2. start– 数字, 可选, 默认: 0
  3. end – 数字, 可选, 默认: buffer.length
  4. 返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 startend的位置剪切。
实例一:缓冲区裁剪
    var buffer1 = Buffer.from('baidu');
    // 剪切缓冲区
    var buffer2 = buffer1.slice(0,2);
    console.log("buffer2 content: " + buffer2.toString());
输出结果为:bd

Buffer 缓冲区长度

  1. Node 缓冲区长度计算语法:buf.length;
  2. 返回 Buffer 对象所占据的内存长度。
实例一:返回缓冲区长度
    var buffer = Buffer.from('www.baidu.com');
    //  缓冲区长度
    console.log("buffer length: " + buffer.length);

Node strean(流)知识点

  1. Stream是一个抽象接口,Node 中有很多对象实现了这个接口。
  2. 例如,对http 服务器发起请求的request对象就是一个Stream,还有stdout(标准输出)
  3. 所有的 Stream 对象都是 EventEmitter 的实例。

Stream有四种流类型:

  1. Readable – 可读操作,
  2. Writable – 可写操作,
  3. Duplex – 可读可写操作,
  4. Transform – 操作被写入数据,然后读出结果。

Stream 常用的事件有

  1. data – 当有数据可读时触发。
  2. end – 没有更多的数据可读时触发。
  3. error – 在接收和写入过程中发生错误时触发。
  4. finish – 所有数据已被写入到底层系统时触发。

从流中读取数据

实例一:从流中读取数据,创建一个input.txt,写入内容为:百度网址:www.baidu.com
    var fs = require("fs");
    var data = '';
    
    // 创建可读流
    var readerStream = fs.createReadStream('input.txt');
    
    // 设置编码为 utf8。
    readerStream.setEncoding('UTF8');
    
    // 处理流事件 --> data, end, and error
    readerStream.on('data', function(chunk) {
       data += chunk;
    });
    
    readerStream.on('end',function(){
       console.log(data);
    });
    
    readerStream.on('error', function(err){
       console.log(err.stack);
    });
    
    console.log("程序执行完毕");
    
输出结果为:
    程序执行完毕
    百度网址:www.baidu.com

写入流

实例二:写入流和读出流都使用input.txt文件
    var fs = require("fs");
    var data = '度娘很强大!';
    
    // 创建一个可以写入的流,写入到文件 output.txt 中
    var writerStream = fs.createWriteStream('input.txt');
    
    // 使用 utf8 编码写入数据
    writerStream.write(data,'UTF8');
    
    // 标记文件末尾
    writerStream.end();
    
    // 处理流事件 --> finish、error
    writerStream.on('finish', function() {
        console.log("写入完成。");
    });
    
    writerStream.on('error', function(err){
       console.log(err.stack);
    });
    
    console.log("程序执行完毕");
输出结果:
    写入完成,
    注意:input.txt文件不为空时,写入的数据将会覆盖原本的数据

管道流

  1. 管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
    Node基础知识笔记

  2. 我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。

实例一:管道流的实现,创建input.txt文件,写入内容:百度 www.baidu.com ,创建 output.txt 文件 :
    var fs = require("fs");
    
    // 创建一个可读流
    var readerStream = fs.createReadStream('input.txt');
    
    // 创建一个可写流
    var writerStream = fs.createWriteStream('output.txt');
    
    // 管道读写操作
    // 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
    readerStream.pipe(writerStream);
    
    console.log("程序执行完毕");
输出结果:
    input.txt中的内容会输出到output.txt中,如果output.txt中存在内容,那么从input.txt中读取出来的内容将会覆盖output.txt中的内容

链式流

  1. 链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。
实例一:利用管道流和链式流来压缩文件
    var fs = require("fs");
    var zlib = require('zlib');
    
    // 压缩 input.txt 文件为 input.txt.gz
    fs.createReadStream('input.txt')
      .pipe(zlib.createGzip())
      .pipe(fs.createWriteStream('input.txt.gz'));
      
    console.log("文件压缩完成。");
输出结果:压缩完成
当前目录下生成了 input.txt 的压缩文件 input.txt.gz。
实例二:利用管道流和链式流来解压文件
    var fs = require("fs");
    var zlib = require('zlib');
    
    // 解压 input.txt.gz 文件为 input.txt
    fs.createReadStream('input.txt.gz')
      .pipe(zlib.createGunzip())
      .pipe(fs.createWriteStream('input.txt'));
      
    console.log("文件解压完成。");
输出结果:解压完成

Node 模块系统

  1. Node模块系统的作用时为了让Node.js文件可以相互调用。
  2. 模块是Node.js应用的基本组成部分,文件和模块是对应的,一个Node.js文件就是一个模块。Node.js文件可能是JS代码,JSON或者是其它语言。

引入Node模块

  1. 在 Node.js 中,引入一个模块非常简单:var hello = require('./hello');,require('./hello')引入当前目录下的hello.js文件。
  2. Node.js 提供了exportsrequire两个对象,其中exports是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的exports 对象。
hello.js文件中的内容
    function Hello() { 
        var name; 
        this.setName = function(thyName) { 
            name = thyName; 
        }; 
        this.sayHello = function() { 
            console.log('Hello ' + name); 
        }; 
    }; 
    module.exports = Hello;
    
main.js文件中的内容
    var Hello = require('./hello'); 
    hello = new Hello(); 
    hello.setName('baidu'); 
    hello.sayHello(); 

  1. hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(’./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。
  2. 模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。

服务器端的模块详解

  1. 我们尝试自己写了各简单的模块,但是呢,我们一般使用第三模块,这时候,下载下列的第三模块是如何放置的呢
  2. Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。
    var http = require("http");
    http.createServer(...);

Node 模块加载详解

  1. Node.js 中存在 4 类模块(原生模块和3种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:
    Node基础知识笔记

从文件模块缓存中加载

  1. 尽管原生模块与文件模块的优先级不同,但是都会优先从文件模块的缓存中加载已经存在的模块。

从原生模块加载

  1. 原生模块的优先级仅次于文件模块缓存的优先级。
  2. require方法在解析文件名之后,优先检查模块是否在原生模块列表中。
  3. http模块为例,尽管在目录下存在一个 http/http.js/http.node/http.json文件,require("http")都不会从这些文件中加载,而是从原生模块中加载。
  4. 原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。

从文件加载

  1. 当文件模块缓存中不存在,而且不是原生模块的时候,Node.js 会解析require 方法传入的参数,并从文件系统中加载实际的文件.
  2. require方法接受以下几种参数的传递:
  3. http、fs、path等,原生模块。
  4. ./mod或../mod,相对路径的文件模块。
  5. /pathtomodule/mod,绝对路径的文件模块。
  6. mod,非原生模块的文件模块。

exports 和 module.exports 的区别

  1. 如果先使用 exports 对外暴露属性或方法,再使用 module.exports 暴露对象,会使得 exports 上暴露的属性或者方法失效。
  2. exports 仅仅是 module.exports 的一个引用

Node.js函数

  1. 在JS中,一个函数可以作为另一个函数的参数,先定义一个函数,然后传递,也可以在传递参数的时候直接定义。
  2. Node.js函数的使用和JS类似。
    function say(word) {
      console.log(word);
    }
    
    function execute(someFunction, value) {
      someFunction(value);
    }
    
    execute(say, "Hello");
  1. 我们把 say 函数作为 execute 函数的第一个变量进行了传递。这里传递的不是 say 的返回值,而是 say 本身!
  2. say 就变成了execute 中的本地变量 someFunction ,execute 可以通过调用 someFunction() (带括号的形式)来使用 say 函数。
  3. 因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。

Node.js匿名函数

  1. 可以把一个函数作为变量传递。但不一定要绕这个”先定义,再传递”的圈子,可以直接在另一个函数的括号中定义和传递这个函数:
    function execute(someFunction, value) {
      someFunction(value);
    }
    
    execute(function(word){ console.log(word) }, "Hello");
    
  1. 在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。

函数传递是如何让HTTP服务器工作的

  1. 简单的HTTP服务器
    var http = require("http");
    
    http.createServer(function(request, response) {
      response.writeHead(200, {"Content-Type": "text/plain"});
      response.write("Hello World");
      response.end();
    }).listen(3000);
    
  1. createServer 函数传递了一个匿名函数。
    var http = require("http");
    
    function onRequest(request, response) {
      response.writeHead(200, {"Content-Type": "text/plain"});
      response.write("Hello World");
      response.end();
    }
    
    http.createServer(onRequest).listen(3000);
    

Node.js路由

  1. 我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。
  2. 因此需要查看 HTTP 请求,从中提取出请求的 URL 以及 GET/POST 参数。这一功能应当属于路由还是服务器
  3. 我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。
实例一:路由的基本演示
Router.js文件中的代码
    function route(pathname) {
        console.log("About to route a request for " + pathname);
      }
       
    exports.route = route;

server.js 文件中的代码
    var http = require("http");
    var url = require("url");
     
    function start(route) {
      function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        console.log("Request for " + pathname + " received.");
     
        route(pathname);
     
        response.writeHead(200, {"Content-Type": "text/plain"});
        response.write("Hello World");
        response.end();
      }
     
      http.createServer(onRequest).listen(8888);
      console.log("Server has started.");
    }
     
    exports.start = start;
    
index.js 文件中的代码:

    var server = require("./server");
    var router = require("./router");
     
    server.start(router.route);
    
在终端执行 node index.js ,此时浏览器访问 http://127.0.0.1:8888/,输出结果为 Hello World,
注意:在 http://127.0.0.1:8888/ + ·url·,你将会看到应用输出相应的信息(在终端查看),这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由:

Node.js全局对象

  1. JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。
  2. 在浏览器JavaScript中,通常window是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。
  3. 在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。

全局对象与全局变量

  1. global 最根本的作用是作为全局变量的宿主。
  2. 按照 ECMAScript 的定义,满足以下条件的变量是全局变量:
  3. 在最外层定义的变量;
  4. 全局对象的属性
  5. 隐式定义的变量(未定义直接赋值的变量)
  6. 当定义一个全局变量的时候,这个变量同时也会成为全局对象的属性,反之也是这样
  7. 在Node.js中,不能在最外层定义变量,因为所有用户代码都是属于当前模块的,而模块本身不是最外层上下文
  8. 注意:不要使用 var 定义变量,避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险。

全局变量的属性介绍

  1. __filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。
  2. __dirname表示当前执行脚本所在的目录。
  3. setTimeout(cb, ms) 全局函数在指定的毫秒(ms)数后执行指定函数(cb)。:setTimeout() 只执行一次指定函数。返回一个代表定时器的句柄值。
  4. clearTimeout( t ) 全局函数用于停止一个之前通过setTimeout() 创建的定时器。 参数t 是通过 setTimeout()函数创建的定时器。
    5.process 是一个全局变量,即 global对象的属性。它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。

Node.js常用工具

  1. util 是Node的一个核心模块,提供常用函数的集合,用于弥补JS功能上的不足。
  2. 使用方法:const util = require('util');,直接引入就好。

util.callabckify 介绍

  1. util.callbackify(original)async异步函数(或者一个返回值为 Promise 的函数)转换成遵循异常优先的回调风格的函数
  2. 例如将 (err, value) => ...回调作为最后一个参数。 在回调函数中,第一个参数为拒绝的原因(如果 Promise解决,则为 null),第二个参数则是解决的值。
实例一:util.callabckify 用法简介
    const util = require('util');
    
    async function fn() {
      return 'hello world';
    }
    const callbackFunction = util.callbackify(fn);
    
    callbackFunction((err, ret) => {
      if (err) throw err;
      console.log(ret);
    });
输出结果为:hello world

util.inherits简介

  1. util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数。
  2. JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript 没有提供对象继承的语言级别特性,而是通过原型复制来实现的。
实例一:定义了一个基础对象 Base 和一个继承自 Base 的 Sub,Base 有三个在构造函数内定义的属性和一个原型中定义的函数,通过util.inherits 实现继承。

    var util = require('util'); 
    function Base() { 
        this.name = 'base'; 
        this.base = 1991; 
        this.sayHello = function() { 
        console.log('Hello ' + this.name); 
        }; 
    } 
    Base.prototype.showName = function() { 
        console.log(this.name);
    }; 
    function Sub() { 
        this.name = 'sub'; 
    } 
    util.inherits(Sub, Base); 
    var objBase = new Base(); 
    objBase.showName(); 
    objBase.sayHello(); 
    console.log(objBase); 
    var objSub = new Sub(); 
    objSub.showName(); 
    //objSub.sayHello(); 
    console.log(objSub); 
    
运行结果如下:
    base 
    Hello base 
    { name: 'base', base: 1991, sayHello: [Function] } 
    sub 
    { name: 'sub' }
  1. Sub仅仅继承了Base在原型中定义的函数,而构造函数内部创造的base 属 性和 sayHello 函数都没有被 Sub 继承。而且在原型中定义的属性不会被 console.log作为对象的属性输出。

util.inspect 简介

  1. util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数object,即要转换的对象。
  2. showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。
  3. depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定depth,默认会递归 2 层,指定为 null表示将不限递归层数完整遍历对象。
  4. 如果 colors值为 true,输出格式将会以 ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。
实例一:
    var util = require('util'); 
    function Person() { 
        this.name = 'byvoid'; 
        this.toString = function() { 
        return this.name; 
        }; 
    } 
    var obj = new Person(); 
    console.log(util.inspect(obj)); 
    console.log(util.inspect(obj, true)); 

其他的常用工具

  1. util.isArray(object):如果给定的参数 “object” 是一个数组返回 true,否则返回 false。
  2. util.isRegExp(object) 如果给定的参数 “object” 是一个正则表达式返回true,否则返回false。
    3.util.isDate(object)如果给定的参数 “object” 是一个日期返回true,否则返回false。
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。