Node.js - 快速指南


Node.js - 简介

什么是 Node.js?

Node.js 是一个基于 Google Chrome 的 JavaScript 引擎(V8 引擎)构建的服务器端平台。Node.js 由 Ryan Dahl 于 2009 年开发,最新版本为 v0.10.36。Node.js官方文档提供的定义如下 -

Node.js 是一个基于Chrome 的 JavaScript 运行时构建的平台,用于轻松构建快速且可扩展的网络应用程序。Node.js 使用事件驱动的非阻塞 I/O 模型,使其轻量级且高效,非常适合跨分布式设备运行的数据密集型实时应用程序。

Node.js 是一个开源的跨平台运行时环境,用于开发服务器端和网络应用程序。Node.js 应用程序是用 JavaScript 编写的,可以在 OS X、Microsoft Windows 和 Linux 上的 Node.js 运行时内运行。

Node.js 还提供了丰富的各种 JavaScript 模块库,这在很大程度上简化了使用 Node.js 的 Web 应用程序的开发。

Node.js = Runtime Environment + JavaScript Library

Node.js 的特点

以下是使 Node.js 成为软件架构师首选的一些重要功能。

  • 异步和事件驱动- Node.js 库的所有 API 都是异步的,即非阻塞的。它本质上意味着基于 Node.js 的服务器永远不会等待 API 返回数据。服务器在调用下一个 API 后移动到下一个 API,Node.js 的 Events 通知机制可以帮助服务器从上一个 API 调用中获取响应。

  • 非常快- Node.js 库基于 Google Chrome 的 V8 JavaScript 引擎构建,代码执行速度非常快。

  • 单线程但高度可扩展- Node.js 使用带有事件循环的单线程模型。事件机制帮助服务器以非阻塞方式响应,并使服务器具有高度可扩展性,而不是创建有限线程来处理请求的传统服务器。Node.js 使用单线程程序,与 Apache HTTP Server 等传统服务器相比,同一个程序可以为更多数量的请求提供服务。

  • 无缓冲- Node.js 应用程序从不缓冲任何数据。这些应用程序只是以块的形式输出数据。

  • 许可证- Node.js 在MIT 许可证下发布。

谁使用 Node.js?

以下是 github wiki 上的链接,其中包含使用 Node.js 的项目、应用程序和公司的详尽列表。此列表包括 eBay、通用电气、GoDaddy、微软、PayPal、Uber、Wikipins、Yahoo! 和 Yammer 等。

概念

下图描述了 Node.js 的一些重要部分,我们将在后续章节中详细讨论。

Node.js 概念

在哪里使用 Node.js?

以下是 Node.js 证明自己是完美技术合作伙伴的领域。

  • I/O 绑定应用程序
  • 数据流应用
  • 数据密集型实时应用程序 (DIRT)
  • 基于 JSON API 的应用程序
  • 单页应用程序

哪些地方不应该使用 Node.js?

不建议将 Node.js 用于 CPU 密集型应用程序。

Node.js - 环境设置

在线尝试选项

您确实不需要设置自己的环境来开始学习 Node.js。原因很简单,我们已经在线搭建了Node.js环境,这样你就可以在线执行所有可用的示例,边实践边学习。请随意修改任何示例并使用不同选项检查结果。

使用以下示例代码框(在我们的网站上)右上角提供的“实时演示”选项尝试以下示例 -

/* Hello World! program in Node.js */
console.log("Hello World!");

对于本教程中给出的大多数示例,您都会找到“尝试”选项,因此只需使用它并享受学习的乐趣。

本地环境设置

如果您仍然愿意为 Node.js 设置环境,则您的计算机上需要有以下两个软件:(a) 文本编辑器和 (b) Node.js 二进制安装程序。

文本编辑器

这将用于输入您的程序。少数编辑器的示例包括 Windows 记事本、操作系统编辑命令、Brief、Epsilon、EMACS 和 vim 或 vi。

文本编辑器的名称和版本可能因不同操作系统而异。例如,记事本将在 Windows 上使用,vim 或 vi 可以在 Windows 上使用,也可以在 Linux 或 UNIX 上使用。

您使用编辑器创建的文件称为源文件,包含程序源代码。Node.js 程序的源文件通常以扩展名“ .js ”命名。

在开始编程之前,请确保您有一个文本编辑器,并且您有足够的经验来编写计算机程序、将其保存在文件中并最终执行它。

Node.js 运行时

源文件中编写的源代码只是 javascript。Node.js 解释器将用于解释和执行您的 JavaScript 代码。

Node.js 发行版以二进制形式安装,适用于采用 32 位 (386) 和 64 位 (amd64) x86 处理器架构的 SunOS、Linux、Mac OS X 和 Windows 操作系统。

以下部分将指导您如何在各种操作系统上安装 Node.js 二进制发行版。

下载 Node.js 存档

从Node.js 下载下载最新版本的 Node.js 可安装存档文件。在撰写本教程时,以下是不同操作系统上可用的版本。

操作系统 档案名称
Windows 节点-v6.3.1-x64.msi
Linux 节点-v6.3.1-linux-x86.tar.gz
苹果 节点-v6.3.1-darwin-x86.tar.gz
太阳操作系统 节点-v6.3.1-sunos-x86.tar.gz

在 UNIX/Linux/Mac OS X 和 SunOS 上安装

根据您的操作系统架构,下载存档文件node-v6.3.1- osname.tar.gz并将其解压到/tmp中,最后将解压的文件移动到/usr/local/nodejs目录中。例如:

$ cd /tmp
$ wget http://nodejs.org/dist/v6.3.1/node-v6.3.1-linux-x64.tar.gz
$ tar xvfz node-v6.3.1-linux-x64.tar.gz
$ mkdir -p /usr/local/nodejs
$ mv node-v6.3.1-linux-x64/* /usr/local/nodejs

将 /usr/local/nodejs/bin 添加到 PATH 环境变量。

操作系统 输出
Linux 导出 PATH=$PATH:/usr/local/nodejs/bin
苹果 导出 PATH=$PATH:/usr/local/nodejs/bin
自由BSD 导出 PATH=$PATH:/usr/local/nodejs/bin

在 Windows 上安装

使用 MSI 文件并按照提示安装 Node.js。默认情况下,安装程序使用 C:\Program Files\nodejs 中的 Node.js 发行版。安装程序应在 Windows 的 PATH 环境变量中设置 C:\Program Files\nodejs\bin 目录。重新启动所有打开的命令提示符以使更改生效。

验证安装:执行文件

在您的计算机(Windows 或 Linux)上创建一个名为main.js的 js 文件,其中包含以下代码。

/* Hello, World! program in node.js */
console.log("Hello, World!")

现在使用 Node.js 解释器执行 main.js 文件以查看结果 -

$ node main.js

如果您的安装一切正常,这应该会产生以下结果 -

Hello, World!

Node.js - 第一个应用程序

在创建实际的“Hello, World!”之前 使用 Node.js 的应用程序,让我们看看 Node.js 应用程序的组件。Node.js 应用程序由以下三个重要组件组成 -

  • 导入所需的模块- 我们使用require指令来加载 Node.js 模块。

  • 创建服务器- 类似于 Apache HTTP Server 的服务器,它将侦听客户端的请求。

  • 读取请求并返回响应- 在前面的步骤中创建的服务器将读取客户端(可以是浏览器或控制台)发出的 HTTP 请求并返回响应。

创建 Node.js 应用程序

第 1 步 - 导入所需模块

我们使用require指令加载 http 模块并将返回的 HTTP 实例存储到 http 变量中,如下所示 -

var http = require("http");

第 2 步 - 创建服务器

我们使用创建的http实例并调用http.createServer()方法创建一个服务器实例,然后使用与该服务器实例关联的listen方法将其绑定在端口8081上。向其传递一个带有参数请求和响应的函数。编写示例实现以始终返回“Hello World”。

http.createServer(function (request, response) {
   // Send the HTTP header 
   // HTTP Status: 200 : OK
   // Content Type: text/plain
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Send the response body as "Hello World"
   response.end('Hello World\n');
}).listen(8081);

// Console will print the message
console.log('Server running at http://127.0.0.1:8081/');

上面的代码足以创建一个侦听的 HTTP 服务器,即等待本地计算机上 8081 端口上的请求。

第 3 步 - 测试请求和响应

让我们将步骤 1 和 2 放在一个名为main.js的文件中,并启动我们的 HTTP 服务器,如下所示 -

var http = require("http");

http.createServer(function (request, response) {
   // Send the HTTP header 
   // HTTP Status: 200 : OK
   // Content Type: text/plain
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Send the response body as "Hello World"
   response.end('Hello World\n');
}).listen(8081);

// Console will print the message
console.log('Server running at http://127.0.0.1:8081/');

现在执行 main.js 来启动服务器,如下所示 -

$ node main.js

验证输出。服务器已启动。

Server running at http://127.0.0.1:8081/

向 Node.js 服务器发出请求

在任意浏览器中打开http://127.0.0.1:8081/,观察以下结果。

Node.js 示例

恭喜,您已经启动并运行了第一个 HTTP 服务器,该服务器正在响应端口 8081 上的所有 HTTP 请求。

Node.js - REPL 终端

REPL 代表 Read Eval Print Loop,它代表一个计算机环境,例如 Windows 控制台或 Unix/Linux shell,其中输入命令,系统以交互模式响应输出。Node.js 或Node与 REPL 环境捆绑在一起。它执行以下任务 -

  • 读取- 读取用户的输入,将输入解析为 JavaScript 数据结构,并存储在内存中。

  • Eval - 获取并评估数据结构。

  • 打印- 打印结果。

  • Loop - 循环上述命令,直到用户按ctrl-c两次。

Node 的 REPL 功能对于试验 Node.js 代码和调试 JavaScript 代码非常有用。

在线 REPL 终端

为了简化您的学习,我们在线设置了一个易于使用的 Node.js REPL 环境,您可以在其中练习 Node.js 语法 -启动 Node.js REPL 终端

启动 REPL

REPL 可以通过简单地在 shell/控制台上运行节点来启动,无需任何参数,如下所示。

$ node

您将看到 REPL 命令提示符 > 您可以在其中键入任何 Node.js 命令 -

$ node
>

简单表达

让我们在 Node.js REPL 命令提示符下尝试一个简单的数学运算 -

$ node
> 1 + 3
4
> 1 + ( 2 * 3 ) - 4
3
>

使用变量

您可以像任何传统脚本一样使用变量来存储值并稍后打印。如果未使用var关键字,则该值将存储在变量中并打印。而如果使用var关键字,则存储该值但不打印该值。您可以使用console.log()打印变量。

$ node
> x = 10
10
> var y = 10
undefined
> x + y
20
> console.log("Hello World")
Hello World
undefined

多行表达式

Node REPL 支持类似于 JavaScript 的多行表达式。让我们检查一下下面的 do-while 循环的实际效果 -

$ node
> var x = 0
undefined
> do {
   ... x++;
   ... console.log("x: " + x);
   ... } 
while ( x < 5 );
x: 1
x: 2
x: 3
x: 4
x: 5
undefined
>

...当您在左括号后按 Enter 时,会自动出现。Node自动检查表达式的连续性。

下划线变量

您可以使用下划线(_)来获取最后的结果 -

$ node
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined
>

REPL 命令

  • ctrl + c - 终止当前命令。

  • ctrl + c 两次- 终止节点 REPL。

  • ctrl + d - 终止节点 REPL。

  • 向上/向下键- 查看命令历史记录并修改以前的命令。

  • tab 键- 当前命令列表。

  • .help - 所有命令的列表。

  • .break - 退出多行表达式。

  • .clear - 退出多行表达式。

  • .save filename - 将当前的 Node REPL 会话保存到文件中。

  • .load filename - 在当前 Node REPL 会话中加载文件内容。

停止 REPL

如上所述,您需要使用ctrl-c 两次才能退出 Node.js REPL。

$ node
>
(^C again to quit)
>

Node.js-NPM

节点包管理器(NPM)提供两个主要功能 -

  • Node.js 包/模块的在线存储库,可在search.nodejs.org上搜索

  • 用于安装 Node.js 包、对 Node.js 包进行版本管理和依赖项管理的命令行实用程序。

v0.6.3 版本之后,NPM 与 Node.js 可安装程序捆绑在一起。要验证相同的结果,请打开控制台并键入以下命令并查看结果 -

$ npm --version
2.7.1

如果您运行的是旧版本的 NPM,那么将其更新到最新版本非常容易。只需从 root 使用以下命令 -

$ sudo npm install npm -g
/usr/bin/npm -> /usr/lib/node_modules/npm/bin/npm-cli.js
npm@2.7.1 /usr/lib/node_modules/npm

使用 NPM 安装模块

有一个简单的语法来安装任何 Node.js 模块 -

$ npm install <Module Name>

例如,以下是安装著名的 Node.js Web 框架模块express 的命令 -

$ npm install express

现在您可以在 js 文件中使用此模块,如下所示 -

var express = require('express');

全局安装与本地安装

默认情况下,NPM 在本地模式下安装任何依赖项。这里的本地模式是指将包安装在 Node 应用程序所在文件夹中的 node_modules 目录中。本地部署的包可以通过 require() 方法访问。例如,当我们安装express模块​​时,它会在安装express模块​​的当前目录中创建node_modules目录。

$ ls -l
total 0
drwxr-xr-x 3 root root 20 Mar 17 02:23 node_modules

或者,您可以使用npm ls命令列出所有本地安装的模块。

全局安装的包/依赖项存储在系统目录中。此类依赖项可以在任何 Node.js 的 CLI(命令行界面)功能中使用,但不能直接在 Node 应用程序中使用 require() 导入。现在让我们尝试使用全局安装来安装 Express 模块。

$ npm install express -g

这将产生类似的结果,但该模块将全局安装。在这里,第一行显示模块版本及其安装位置。

express@4.12.2 /usr/lib/node_modules/express
├── merge-descriptors@1.0.0
├── utils-merge@1.0.0
├── cookie-signature@1.0.6
├── methods@1.1.1
├── fresh@0.2.4
├── cookie@0.1.2
├── escape-html@1.0.1
├── range-parser@1.0.2
├── content-type@1.0.1
├── finalhandler@0.3.3
├── vary@1.0.0
├── parseurl@1.3.0
├── content-disposition@0.5.0
├── path-to-regexp@0.1.3
├── depd@1.0.0
├── qs@2.3.3
├── on-finished@2.2.0 (ee-first@1.1.0)
├── etag@1.5.1 (crc@3.2.1)
├── debug@2.1.3 (ms@0.7.0)
├── proxy-addr@1.0.7 (forwarded@0.1.0, ipaddr.js@0.1.9)
├── send@0.12.1 (destroy@1.0.3, ms@0.7.0, mime@1.3.4)
├── serve-static@1.9.2 (send@0.12.2)
├── accepts@1.2.5 (negotiator@0.5.1, mime-types@2.0.10)
└── type-is@1.6.1 (media-typer@0.3.0, mime-types@2.0.10)

您可以使用以下命令检查全局安装的所有模块 -

$ npm ls -g

使用package.json

package.json 存在于任何 Node 应用程序/模块的根目录中,用于定义包的属性。让我们打开node_modules/express/中的express包的package.json

{
   "name": "express",
      "description": "Fast, unopinionated, minimalist web framework",
      "version": "4.11.2",
      "author": {
      
         "name": "TJ Holowaychuk",
         "email": "tj@vision-media.ca"
      },
   
   "contributors": [{
      "name": "Aaron Heckmann",
      "email": "aaron.heckmann+github@gmail.com"
   }, 
   
   {
      "name": "Ciaran Jessup",
      "email": "ciaranj@gmail.com"
   },
   
   {
      "name": "Douglas Christopher Wilson",
      "email": "doug@somethingdoug.com"
   },
   
   {
      "name": "Guillermo Rauch",
      "email": "rauchg@gmail.com"
   },
   
   {
      "name": "Jonathan Ong",
      "email": "me@jongleberry.com"
   },
   
   {
      "name": "Roman Shtylman",
      "email": "shtylman+expressjs@gmail.com"
   },
   
   {
      "name": "Young Jae Sim",
      "email": "hanul@hanul.me"
   } ],
   
   "license": "MIT", "repository": {
      "type": "git",
      "url": "https://github.com/strongloop/express"
   },
   
   "homepage": "https://expressjs.com/", "keywords": [
      "express",
      "framework",
      "sinatra",
      "web",
      "rest",
      "restful",
      "router",
      "app",
      "api"
   ],
   
   "dependencies": {
      "accepts": "~1.2.3",
      "content-disposition": "0.5.0",
      "cookie-signature": "1.0.5",
      "debug": "~2.1.1",
      "depd": "~1.0.0",
      "escape-html": "1.0.1",
      "etag": "~1.5.1",
      "finalhandler": "0.3.3",
      "fresh": "0.2.4",
      "media-typer": "0.3.0",
      "methods": "~1.1.1",
      "on-finished": "~2.2.0",
      "parseurl": "~1.3.0",
      "path-to-regexp": "0.1.3",
      "proxy-addr": "~1.0.6",
      "qs": "2.3.3",
      "range-parser": "~1.0.2",
      "send": "0.11.1",
      "serve-static": "~1.8.1",
      "type-is": "~1.5.6",
      "vary": "~1.0.0",
      "cookie": "0.1.2",
      "merge-descriptors": "0.0.2",
      "utils-merge": "1.0.0"
   },
   
   "devDependencies": {
      "after": "0.8.1",
      "ejs": "2.1.4",
      "istanbul": "0.3.5",
      "marked": "0.3.3",
      "mocha": "~2.1.0",
      "should": "~4.6.2",
      "supertest": "~0.15.0",
      "hjs": "~0.0.6",
      "body-parser": "~1.11.0",
      "connect-redis": "~2.2.0",
      "cookie-parser": "~1.3.3",
      "express-session": "~1.10.2",
      "jade": "~1.9.1",
      "method-override": "~2.3.1",
      "morgan": "~1.5.1",
      "multiparty": "~4.1.1",
      "vhost": "~3.0.0"
   },
   
   "engines": {
      "node": ">= 0.10.0"
   },
   
   "files": [
      "LICENSE",
      "History.md",
      "Readme.md",
      "index.js",
      "lib/"
   ],
   
   "scripts": {
      "test": "mocha --require test/support/env 
         --reporter spec --bail --check-leaks test/ test/acceptance/",
      "test-cov": "istanbul cover node_modules/mocha/bin/_mocha 
         -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
      "test-tap": "mocha --require test/support/env 
         --reporter tap --check-leaks test/ test/acceptance/",
      "test-travis": "istanbul cover node_modules/mocha/bin/_mocha 
         --report lcovonly -- --require test/support/env 
         --reporter spec --check-leaks test/ test/acceptance/"
   },
   
   "gitHead": "63ab25579bda70b4927a179b580a9c580b6c7ada",
   "bugs": {
      "url": "https://github.com/strongloop/express/issues"
   },
   
   "_id": "express@4.11.2",
   "_shasum": "8df3d5a9ac848585f00a0777601823faecd3b148",
   "_from": "express@*",
   "_npmVersion": "1.4.28",
   "_npmUser": {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
   },
   
   "maintainers": [{
      "name": "tjholowaychuk",
      "email": "tj@vision-media.ca"
   },
   
   {
      "name": "jongleberry",
      "email": "jonathanrichardong@gmail.com"
   },
   
   {
      "name": "shtylman",
      "email": "shtylman@gmail.com"
   },
   
   {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
   },
   
   {
      "name": "aredridel",
      "email": "aredridel@nbtsc.org"
   },
   
   {
      "name": "strongloop",
      "email": "callback@strongloop.com"
   },
   
   {
      "name": "rfeng",
      "email": "enjoyjava@gmail.com"
   }],
   
   "dist": {
      "shasum": "8df3d5a9ac848585f00a0777601823faecd3b148",
      "tarball": "https://registry.npmjs.org/express/-/express-4.11.2.tgz"
   },
   
   "directories": {},
      "_resolved": "https://registry.npmjs.org/express/-/express-4.11.2.tgz",
      "readme": "ERROR: No README data found!"
}

Package.json 的属性

  • name - 包的名称

  • version - 包的版本

  • 描述- 包的描述

  • 主页- 包的主页

  • 作者- 包的作者

  • 贡献者- 包的贡献者姓名

  • 依赖项- 依赖项列表。NPM 会自动将此处提到的所有依赖项安装到包的 node_module 文件夹中。

  • 存储库- 存储库类型和包的 URL

  • main - 包的入口点

  • 关键词- 关键词

卸载模块

使用以下命令卸载 Node.js 模块。

$ npm uninstall express

NPM 卸载软件包后,您可以通过查看 /node_modules/ 目录的内容或键入以下命令来验证它 -

$ npm ls

更新模块

更新package.json并更改要更新的依赖版本并运行以下命令。

$ npm update express

搜索模块

使用 NPM 搜索包名称。

$ npm search express

创建模块

创建模块需要生成package.json。让我们使用 NPM 生成 package.json,这将生成 package.json 的基本框架。

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See 'npm help json' for definitive documentation on these fields
and exactly what they do.

Use 'npm install <pkg> --save' afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (webmaster)

您需要提供有关您的模块的所有必需信息。您可以借助上述package.json文件来了解所需的各种信息的含义。生成 package.json 后,使用以下命令使用有效的电子邮件地址向 NPM 存储库站点注册。

$ npm adduser
Username: mcmohd
Password:
Email: (this IS public) mcmohd@gmail.com

现在是时候发布您的模块了 -

$ npm publish

如果您的模块一切正常,那么它将发布在存储库中,并且可以像任何其他 Node.js 模块一样使用 NPM 进行安装。

Node.js - 回调概念

什么是回调?

回调是函数的异步等效项。完成给定任务时调用回调函数。Node 大量使用回调。Node 的所有 API 都是以支持回调的方式编写的。

例如,读取文件的函数可能会开始读取文件并立即将控制权返回到执行环境,以便可以执行下一条指令。一旦文件I/O完成,就会调用回调函数,同时向回调函数传递文件的内容作为参数。因此文件 I/O 不会发生阻塞或等待。这使得 Node.js 具有高度可扩展性,因为它可以处理大量请求,而无需等待任何函数返回结果。

阻止代码示例

创建一个名为input.txt的文本文件,其中包含以下内容 -

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

使用以下代码创建一个名为main.js的 js 文件-

var fs = require("fs");
var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("Program Ended");

现在运行 main.js 来查看结果 -

$ node main.js

验证输出。

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!
Program Ended

非阻塞代码示例

创建一个名为 input.txt 的文本文件,其中包含以下内容。

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

更新 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("Program Ended");

现在运行 main.js 来查看结果 -

$ node main.js

验证输出。

Program Ended
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

这两个示例解释了阻塞和非阻塞调用的概念。

  • 第一个示例显示程序会阻塞,直到读取文件,然后才继续结束程序。

  • 第二个例子显示程序不等待文件读取并继续打印“Program Ended”,同时程序没有阻塞地继续读取文件。

因此,阻塞程序非常按顺序执行。从编程的角度来看,实现逻辑更容易,但非阻塞程序不是按顺序执行的。如果程序需要使用任何要处理的数据,则应将其保存在同一块中以使其顺序执行。

Node.js - 事件循环

Node.js 是一个单线程应用程序,但它可以通过事件回调的概念支持并发。Node.js 的每个 API 都是异步且单线程的,它们使用异步函数调用来保持并发性。节点使用观察者模式。节点线程保持一个事件循环,每当任务完成时,它就会触发相应的事件,通知事件侦听器函数执行。

事件驱动编程

Node.js 大量使用事件,这也是 Node.js 与其他类似技术相比速度相当快的原因之一。一旦 Node 启动它的服务器,它就简单地初始化它的变量,声明函数,然后简单地等待事件发生。

在事件驱动的应用程序中,通常有一个主循环来侦听事件,然后在检测到其中一个事件时触发回调函数。

事件循环

尽管事件看起来与回调非常相似,但不同之处在于,回调函数是在异步函数返回其结果时调用的,而事件处理则采用观察者模式。监听事件的函数充当观察者。每当事件被触发时,它的监听器函数就会开始执行。Node.js 通过事件模块和 EventEmitter 类提供多个内置事件,用于绑定事件和事件侦听器,如下所示 -

// Import events module
var events = require('events');

// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();

以下是将事件处理程序与事件绑定的语法 -

// Bind event and event  handler as follows
eventEmitter.on('eventName', eventHandler);

我们可以通过编程方式触发事件,如下所示 -

// Fire an event 
eventEmitter.emit('eventName');

例子

使用以下代码创建一个名为 main.js 的 js 文件 -

// Import events module
var events = require('events');

// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();

// Create an event handler as follows
var connectHandler = function connected() {
   console.log('connection succesful.');
  
   // Fire the data_received event 
   eventEmitter.emit('data_received');
}

// Bind the connection event with the handler
eventEmitter.on('connection', connectHandler);
 
// Bind the data_received event with the anonymous function
eventEmitter.on('data_received', function() {
   console.log('data received succesfully.');
});

// Fire the connection event 
eventEmitter.emit('connection');

console.log("Program Ended.");

现在让我们尝试运行上面的程序并检查其输出 -

$ node main.js

IT 应产生以下结果 -

connection successful.
data received successfully.
Program Ended.

节点应用程序如何工作?

在节点应用程序中,任何异步函数都接受回调作为最后一个参数,回调函数接受错误作为第一个参数。让我们再次回顾一下前面的例子。创建一个名为 input.txt 的文本文件,其中包含以下内容。

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

创建一个名为 main.js 的 js 文件,其中包含以下代码 -

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err) {
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("Program Ended");

这里 fs.readFile() 是一个异步函数,其目的是读取文件。如果在读取操作期间发生错误,则err 对象将包含相应的错误,否则 data 将包含文件的内容。readFile在读操作完成后将 err 和 data 传递给回调函数,最终打印内容。

Program Ended
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

Node.js - 事件发射器

Node 中的许多对象都会发出事件,例如,每次对等点连接到 net.Server 时,net.Server 都会发出一个事件,fs.readStream 在打开文件时会发出一个事件。所有发出事件的对象都是 events.EventEmitter 的实例。

事件发射器类

正如我们在上一节中看到的,EventEmitter 类位于事件模块中。它可以通过以下代码访问 -

// Import events module
var events = require('events');

// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();

当 EventEmitter 实例遇到任何错误时,它会发出“错误”事件。添加新侦听器时,会触发“newListener”事件;删除侦听器时,会触发“removeListener”事件。

EventEmitter 提供了多个属性,例如onemiton属性用于将函数与事件绑定,emit用于触发事件。

方法

先生。 方法及说明
1

添加监听器(事件,监听器)

在指定事件的侦听器数组末尾添加侦听器。不会检查侦听器是否已添加。传递相同的事件和侦听器组合的多个调用将导致侦听器被多次添加。返回发射器,因此可以链接调用。

2

on(事件,侦听器)

在指定事件的侦听器数组末尾添加侦听器。不会检查侦听器是否已添加。传递相同的事件和侦听器组合的多个调用将导致侦听器被多次添加。返回发射器,因此可以链接调用。

3

一次(事件,侦听器)

向事件添加一次性侦听器。仅在下次触发事件时才调用此侦听器,之后将其删除。返回发射器,因此可以链接调用。

4

移除监听器(事件,监听器)

从指定事件的侦听器数组中删除侦听器。注意 -它更改侦听器后面的侦听器数组中的数组索引。removeListener 最多将从侦听器数组中删除一个侦听器实例。如果任何单个侦听器已多次添加到指定事件的侦听器数组中,则必须多次调用removeListener 才能删除每个实例。返回发射器,因此可以链接调用。

5

移除所有监听器([事件])

删除所有侦听器或指定事件的侦听器。删除在代码中其他位置添加的侦听器并不是一个好主意,特别是当它位于您未创建的发射器上时(例如套接字或文件流)。返回发射器,因此可以链接调用。

6

设置最大监听器(n)

默认情况下,如果为特定事件添加了超过 10 个侦听器,EventEmitters 将打印警告。这是一个有用的默认值,有助于查找内存泄漏。显然,并非所有发射器都应限制为 10 个。此功能允许增加发射器数量。设置为零表示无限制。

7

听众(事件)

返回指定事件的侦听器数组。

8

发出(事件,[arg1],[arg2],[...])

使用提供的参数按顺序执行每个侦听器。如果事件有侦听器,则返回 true,否则返回 false。

类方法

先生。 方法及说明
1

监听器计数(发射器,事件)

返回给定事件的侦听器数量。

活动

先生。 活动及描述
1

新监听器

  • event - 字符串:事件名称

  • 监听器- 功能:事件处理函数

每当添加侦听器时都会发出此事件。当触发此事件时,侦听器可能尚未添加到该事件的侦听器数组中。

2

移除监听器

  • event - 字符串 事件名称

  • 监听器- 函数 事件处理函数

每当有人删除侦听器时都会发出此事件。当触发此事件时,侦听器可能尚未从该事件的侦听器数组中删除。

例子

使用以下 Node.js 代码创建一个名为 main.js 的 js 文件 -

var events = require('events');
var eventEmitter = new events.EventEmitter();

// listener #1
var listner1 = function listner1() {
   console.log('listner1 executed.');
}

// listener #2
var listner2 = function listner2() {
   console.log('listner2 executed.');
}

// Bind the connection event with the listner1 function
eventEmitter.addListener('connection', listner1);

// Bind the connection event with the listner2 function
eventEmitter.on('connection', listner2);

var eventListeners = require('events').EventEmitter.listenerCount
   (eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

// Fire the connection event 
eventEmitter.emit('connection');

// Remove the binding of listner1 function
eventEmitter.removeListener('connection', listner1);
console.log("Listner1 will not listen now.");

// Fire the connection event 
eventEmitter.emit('connection');

eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

console.log("Program Ended.");

现在运行 main.js 来查看结果 -

$ node main.js

验证输出。

2 Listner(s) listening to connection event
listner1 executed.
listner2 executed.
Listner1 will not listen now.
listner2 executed.
1 Listner(s) listening to connection event
Program Ended.

Node.js - 缓冲区

纯 JavaScript 对 Unicode 是友好的,但对于二进制数据则不然。在处理 TCP 流或文件系统时,有必要处理八位字节流。Node 提供 Buffer 类,该类提供实例来存储类似于整数数组的原始数据,但对应于 V8 堆外部的原始内存分配。

Buffer 类是一个全局类,可以在应用程序中访问而无需导入 buffer 模块。

创建缓冲区

节点缓冲区可以通过多种方式构建。

方法一

以下是创建10 个八位字节的未初始化缓冲区的语法 -

var buf = new Buffer(10);

方法2

以下是从给定数组创建缓冲区的语法 -

var buf = new Buffer([10, 20, 30, 40, 50]);

方法三

以下是从给定字符串和可选编码类型创建缓冲区的语法 -

var buf = new Buffer("Simply Easy Learning", "utf-8");

虽然“utf8”是默认编码,但您可以使用以下任何编码“ascii”、“utf8”、“utf16le”、“ucs2”、“base64”或“hex”。

写入缓冲区

句法

以下是写入节点缓冲区的方法的语法 -

buf.write(string[, offset][, length][, encoding])

参数

这是所使用参数的描述 -

  • string -- 这是要写入缓冲区的字符串数据。

  • offset -- 这是开始写入的缓冲区的索引。默认值为 0。

  • length - 这是要写入的字节数。默认为 buffer.length。

  • 编码- 使用的编码。“utf8”是默认编码。

返回值

此方法返回写入的八位字节数。如果缓冲区中没有足够的空间来容纳整个字符串,它将写入字符串的一部分。

例子

buf = new Buffer(256);
len = buf.write("Simply Easy Learning");

console.log("Octets written : "+  len);

执行上述程序时,会产生以下结果 -

Octets written : 20

从缓冲区读取

句法

以下是从节点缓冲区读取数据的方法的语法 -

buf.toString([encoding][, start][, end])

参数

这是所使用参数的描述 -

  • 编码- 使用的编码。“utf8”是默认编码。

  • start - 开始读取的开始索引,默认为 0。

  • end - 结束读取的结束索引,默认为完整缓冲区。

返回值

此方法从使用指定字符集编码编码的缓冲区数据中解码并返回字符串。

例子

buf = new Buffer(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}

console.log( buf.toString('ascii'));       // outputs: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   // outputs: abcde
console.log( buf.toString('utf8',0,5));    // outputs: abcde
console.log( buf.toString(undefined,0,5)); // encoding defaults to 'utf8', outputs abcde

执行上述程序时,会产生以下结果 -

abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde

将缓冲区转换为 JSON

句法

以下是将 Node Buffer 转换为 JSON 对象的方法的语法 -

buf.toJSON()

返回值

此方法返回 Buffer 实例的 JSON 表示形式。

例子

var buf = new Buffer('Simply Easy Learning');
var json = buf.toJSON(buf);

console.log(json);

执行上述程序时,会产生以下结果 -

{ type: 'Buffer',
   data: 
   [ 
      83,
      105,
      109,
      112,
      108,
      121,
      32,
      69,
      97,
      115,
      121,
      32,
      76,
      101,
      97,
      114,
      110,
      105,
      110,
      103 
   ]
}

连接缓冲区

句法

以下是将节点缓冲区连接到单个节点缓冲区的方法的语法 -

Buffer.concat(list[, totalLength])

参数

这是所使用参数的描述 -

  • list - 要连接的 Buffer 对象的数组列表。

  • TotalLength - 这是连接时缓冲区的总长度。

返回值

该方法返回一个 Buffer 实例。

例子

var buffer1 = new Buffer('TutorialsPoint ');
var buffer2 = new Buffer('Simply Easy Learning');
var buffer3 = Buffer.concat([buffer1,buffer2]);

console.log("buffer3 content: " + buffer3.toString());

执行上述程序时,会产生以下结果 -

buffer3 content: TutorialsPoint Simply Easy Learning

比较缓冲区

句法

以下是比较两个节点缓冲区的方法的语法 -

buf.compare(otherBuffer);

参数

这是所使用参数的描述 -

  • otherBuffer - 这是将与buf进行比较的另一个缓冲区

返回值

返回一个数字,指示它是在排序顺序中位于 otherBuffer 之前还是之后,或者与 otherBuffer 相同。

例子

var buffer1 = new Buffer('ABC');
var buffer2 = new Buffer('ABCD');
var result = buffer1.compare(buffer2);

if(result < 0) {
   console.log(buffer1 +" comes before " + buffer2);
} else if(result === 0) {
   console.log(buffer1 +" is same as " + buffer2);
} else {
   console.log(buffer1 +" comes after " + buffer2);
}

执行上述程序时,会产生以下结果 -

ABC comes before ABCD

复制缓冲区

句法

以下是复制节点缓冲区的方法的语法 -

buf.copy(targetBuffer[, targetStart][, sourceStart][, sourceEnd])

参数

这是所使用参数的描述 -

  • targetBuffer - 将复制缓冲区的缓冲区对象。

  • targetStart - 数字,可选,默认值:0

  • sourceStart - 数字,可选,默认值:0

  • sourceEnd - 数字,可选,默认:buffer.length

返回值

无返回值。即使目标内存区域与源内存区域重叠,也会将数据从此缓冲区的区域复制到目标缓冲区中的区域。如果未定义,则 targetStart 和 sourceStart 参数默认为 0,而 sourceEnd 默认为 buffer.length。

例子

var buffer1 = new Buffer('ABC');

//copy a buffer
var buffer2 = new Buffer(3);
buffer1.copy(buffer2);
console.log("buffer2 content: " + buffer2.toString());

执行上述程序时,会产生以下结果 -

buffer2 content: ABC

切片缓冲区

句法

以下是获取节点缓冲区的子缓冲区的方法的语法 -

buf.slice([start][, end])

参数

这是所使用参数的描述 -

  • 开始- 数字,可选,默认:0

  • end - 数字,可选,默认:buffer.length

返回值

返回一个新缓冲区,该缓冲区引用与旧缓冲区相同的内存,但通过开始(默认为 0)和结束(默认为 buffer.length)索引进行偏移和裁剪。负索引从缓冲区末尾开始。

例子

var buffer1 = new Buffer('TutorialsPoint');

//slicing a buffer
var buffer2 = buffer1.slice(0,9);
console.log("buffer2 content: " + buffer2.toString());

执行上述程序时,会产生以下结果 -

buffer2 content: Tutorials

缓冲区长度

句法

以下是获取节点缓冲区大小(以字节为单位)的方法的语法 -

buf.length;

返回值

返回缓冲区的大小(以字节为单位)。

例子

var buffer = new Buffer('TutorialsPoint');

//length of the buffer
console.log("buffer length: " + buffer.length);

当执行上述程序时,它会产生以下结果 -

buffer length: 14

方法参考