node初学小记

115 阅读13分钟

参考菜鸟教程:www.cainiaojc.com/nodejs/node…

node创建第一个应用

步骤1:

使用 require 指令来载入 http 模块,并将实例化的 HTTP 赋值给变量 http,实例如下:

     var http = require("http");

  步骤2:

接下来使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定 8888 端口。 函数通过 request, response 参数来接收和响应数据。

实例如下,在项目根目录下创建一个叫 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/');


nodejs REPL(交互式解释器)

node自带了交互式解释器,可以完成以下任务:

      • 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
      • 执行 - 执行输入的数据结构
      • 打印 - 输出结果
      • 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。

简单的表达式运算;

使用变量;

多行表达式;

下划线(_)变量:获取上一个表达式的运算结果;

REPL命令:

      • ctrl + c - 退出当前终端。
      • ctrl + c 按下两次 - 退出 Node REPL。
      • ctrl + d - 退出 Node REPL.
      • 向上/向下 键 - 查看输入的历史命令
      • tab 键 - 列出当前命令
      • .help - 列出使用命令
      • .break - 退出多行表达式
      • .exit     Exit the repl
      • .load filename - 载入当前 Node REPL 会话的文件内容。
      • .save filename - 保存当前的 Node REPL 会话到指定文件

回调函数

Node.js 异步编程的直接体现就是回调。

异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。

回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。

例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。

回调函数一般作为函数的最后一个参数出现:

   function foo1(name, age, callback) { }

    function foo2(value, callback1, callback2) { }


事件循环

Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。

Node.js 几乎每一个 API 都是支持回调函数的。

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。

Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。

这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)

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

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

Nodejs有多种内置事件,可以通过引入events模块,并通过实例化EventEmitter类来绑定和监听事件,如下:

  // 引入 events 模块

  var events = require('events');

  // 创建 eventEmitter 对象

  var eventEmitter = new events.EventEmitter();

  // 绑定事件及事件的处理程序

   eventEmitter.on('eventName', eventHandler);

  // 通过程序触发事件:

  eventEmitter.emit('eventName');


EventEmitter

events模块只提供了一个对象:events.EventEmitter。EventEmitter的核心就是事件触发与事件监听器功能的封装。

EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。

EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。

当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

如:

     //event.js 文件

     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 参数', 'arg2 参数');

执行以上代码,运行的结果如下:

     $ node event.js 

      listener1 arg1 参数 arg2 参数

      listener2 arg1 参数 arg2 参数


EventEmitter属性介绍

    • 方法
序号方法 & 描述
1addListener(event, listener) 为指定事件添加一个监听器到监听器数组的尾部。
2on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。server.on('connection', function (stream) {  console.log('someone connected!');});
3once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。server.once('connection', function (stream) {  console.log('Ah, we have our first user!');});
4**removeListener(event, listener)**移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。var callback = function(stream) {  console.log('someone connected!');};server.on('connection', callback);// ...server.removeListener('connection', callback);
5removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
6setMaxListeners(n) 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
7listeners(event) 返回指定事件的监听器数组。
8emit(event, [arg1], [arg2], [...]) 按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
    • 类方法
序号方法 & 描述
1listenerCount(emitter, event) 返回指定事件的监听器数量。
    • 事件
序号事件 & 描述
1newListener- event - 字符串,事件名称
  • listener - 处理事件函数该事件在添加新监听器时被触发。 | | 2 | removeListener- event - 字符串,事件名称
  • listener - 处理事件函数从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引 |

Error事件

EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。

当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。

我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

  var events = require('events'); 

  var emitter = new events.EventEmitter(); 

   emitter.emit('error');


Stream

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

Node.js,Stream 有四种流类型:

    • Readable - 可读操作。
    • Writable - 可写操作。
    • Duplex - 可读可写操作.
    • Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

    • data - 当有数据可读时触发。
    • end - 没有更多的数据可读时触发。
    • error - 在接收和写入过程中发生错误时触发。
    • finish - 所有数据已被写入到底层系统时触发。
  • 从流中读取数据
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("程序执行完毕");
  • 写入流
var fs = require("fs");

var data = '菜鸟教程官网地址:www.runoob.com';

// 创建一个可以写入的流,写入到文件 output.txt 中

var writerStream = fs.createWriteStream('output.txt');

// 使用 utf8 编码写入数据

writerStream.write(data,'UTF8');

// 标记文件末尾

writerStream.end();

// 处理流事件 --> data, end, and error

writerStream.on('finish', function() {

    console.log("写入完成。");

});

writerStream.on('error', function(err){

  console.log(err.stack);

});

console.log("程序执行完毕");
  • 管道流
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("程序执行完毕");
  • 链式流
    • 创建compress.js
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("文件压缩完成。");
    • 创建deCompress.js
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("文件解压完成。");

全局对象

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。

在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。

全局对象与全局变量

global 最根本的作用是作为全局变量的宿主。按照 ECMAScript 的定义,满足以下条 件的变量是全局变量:

    • 在最外层定义的变量;
    • 全局对象的属性;
    • 隐式定义的变量(未定义直接赋值的变量)。

当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。需要注 意的是,在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。

注意:  永远使用 var 定义变量以避免引入全局变量,因为全局变量会污染 命名空间,提高代码的耦合风险。

  • __filename:表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。
  • __dirname 表示当前执行脚本所在的目录。
  • setTimeout
  • clearTimeout(t)
  • setInterval(cb, ms) 可以使用 ****clearInterval(t) ****函数来清除定时器。
  • console 方法: console 用于提供控制台标准输出,它是由 Internet Explorer 的 JScript 引擎提供的调试工具,后来逐渐成为浏览器的实施标准。Node.js 沿用了这个标准,提供与习惯行为一致的 console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符。
  • process 是一个全局变量,即 global 对象的属性。它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。

常用工具

util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足。

    • util.inherits(constructor, superConstructor): 是一个实现对象间原型继承 的函数。
    • util.inspect(object,[showHidden],[depth],[colors]) 是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象。
      • showHidden 是一个可选参数,如果值为 true,将会输出更多隐藏信息。
      • depth 表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多 少。如果不指定depth,默认会递归2层,指定为 null 表示将不限递归层数完整遍历对象。
      • 如果color 值为 true,输出格式将会以ANSI 颜色编码,通常用于在终端显示更漂亮 的效果。
      • 特别要指出的是,util.inspect 并不会简单地直接把对象转换为字符串,即使该对 象定义了toString 方法也不会调用。
    • util.isArray(object) 如果给定的参数 "object" 是一个数组返回true,否则返回false。
    • util.isRegExp(object) 如果给定的参数 "object" 是一个正则表达式返回true,否则返回false。
    • util.isDate(object) 如果给定的参数 "object" 是一个日期返回true,否则返回false。
    • util.isError(object) 如果给定的参数 "object" 是一个错误对象返回true,否则返回false。

文件系统

(注:同步方法和异步方法的区别是:在使用同步方法执行的操作结束之前,不能执行后续代码的执行;而异步方法将操作结果作为回调函数的参数进行返回,方法调用之后,就可以立即执行后续的代码,读取完毕后会调用对应的回调函数。)

    • 打开文件(异步模式下打开文件的语法格式)
      • fs.open(path, flags[, mode], callback)
        • path - 文件的路径。
        • flags - 文件打开的行为。具体值详见下文。
        • mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。
        • callback - 回调函数,带有两个参数如:callback(err, fd)。
    • 获取文件信息
      • fs.stat(path, callback)
        • path - 文件路径。
        • callback - 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象。
    • 写入文件
      • fs.writeFile(file, data[, options], callback)
        • file - 文件名或文件描述符。
        • data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(缓冲) 对象。
        • options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w'
        • callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
    • 读取文件
      • fs.read(fd, buffer, offset, length, position, callback)        
        • fd - 通过 fs.open() 方法返回的文件描述符。
        • buffer - 数据写入的缓冲区。
        • offset - 缓冲区写入的写入偏移量。
        • length - 要从文件中读取的字节数。
        • position - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。
        • callback - 回调函数,有三个参数err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。
    • 关闭文件
      • fs.close(fd, callback)
        • fd - 通过 fs.open() 方法返回的文件描述符。
        • callback - 回调函数,没有参数。
    • 截取文件
      • fs.ftruncate(fd, len, callback)
        • fd - 通过 fs.open() 方法返回的文件描述符。
        • len - 文件内容截取的长度。
        • callback - 回调函数,没有参数。
    • 删除文件
      • fs.unlink(path, callback)
        • path - 文件路径。
        • callback - 回调函数,没有参数。

    • 创建目录
      • fs.mkdir(path[, options], callback)    
        • path - 文件路径。
        • options 参数可以是:
          • recursive - 是否以递归的方式创建目录,默认为 false。
          • mode - 设置目录权限,默认为 0777。
        • callback - 回调函数,没有参数。
    • 读取目录
      • fs.readdir(path, callback)
        • path - 文件路径。
        • callback - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为 目录下的文件数组列表。
    • 删除目录
      • fs.rmdir(path, callback)
        • path - 文件路径。
        • callback - 回调函数,没有参数。