day3:Node.js 基础知识

编程入门 行业动态 更新时间:2024-10-13 12:23:57

day3:Node.js <a href=https://www.elefans.com/category/jswz/34/1769428.html style=基础知识"/>

day3:Node.js 基础知识

day3:Node.js 基础知识

文章目录

    • day3:Node.js 基础知识
          • 创建第一个应用
          • 事件循环机制
          • 异步编程
          • 模块系统
          • 函数与回调函数
          • 路由和全局对象

创建第一个应用

实例如下,在你项目的根目录下创建一个叫 helloworld.js 的文件,并写入以下代码:

var http = require('http');http.createServer(function (request, response) {// 发送 HTTP 头部 // HTTP 状态值: 200 : OK// 内容类型: text/plainresponse.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/');

使用 node 命令执行以上的代码:

[root@node3 nodejs]# node helloworld.js 
Server running at http://127.0.0.1:8888/
事件循环机制

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

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

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

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

EventEmitter类

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

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

以下程序绑定事件处理程序:

// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

我们可以通过程序触发事件:

// 触发事件
eventEmitter.emit('eventName');

实例

创建 main.js 文件,代码如下所示:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();// 创建事件处理程序
var connectHandler = function connected() {console.log('连接成功。');// 触发 data_received 事件 eventEmitter.emit('data_received');
}// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){console.log('数据接收成功。');
});// 触发 connection 事件 
eventEmitter.emit('connection');console.log("程序执行完毕。");

接下来让我们执行以上代码:

[root@node3 nodejs]# node test1.js 
连接成功。
数据接收成功。
程序执行完毕。

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。

Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都events.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 参数'); 

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

[root@node3 nodejs]# node event.js 
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数

以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。

运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。

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

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

异步编程

回调

在代码中,异步编程的直接体现就是回调。异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。我们首先可以看看以下代码。

function heavyCompute(n, callback) {var count = 0,i, j;for (i = n; i > 0; --i) {for (j = n; j > 0; --j) {count += 1;}}callback(count);
}heavyCompute(10000, function (count) {console.log(count);
});console.log('hello');-- Console ------------------------------
100000000
hello

可以看到,以上代码中的回调函数仍然先于后续代码执行。JS本身是单线程运行的,不可能在一段代码还未结束运行时去运行别的代码,因此也就不存在异步执行的概念。

function heavyCompute(n) {var count = 0,i, j;for (i = n; i > 0; --i) {for (j = n; j > 0; --j) {count += 1;}}console.log("function heavy:"+count);
}var t = new Date();setTimeout(function () {console.log("setTimeout:" +(new Date() - t));}, 1000);heavyCompute(50000);-- Console ------------------------------
function heavy:2500000000
setTimeout:5329
模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。

引入模块

在 Node.js 中,引入一个模块非常简单,如下我们创建一个 main.js 文件并引入 hello 模块,代码如下:

var hello = require('./hello');
hello.world();

以上实例中,代码 require(‘./hello’) 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。

Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

接下来我们就来创建 hello.js 文件,代码如下:

exports.world = function() { #类似Java public修饰词console.log('Hello World');
}

在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了.

有时候我们只是想把一个对象封装到模块中,格式如下:

module.exports = function() {// ...
}

例如:

//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('BYVoid'); 
hello.sayHello(); 

Node.js 的 require 方法中的文件查找策略如下

exports 和 module.exports 的使用

如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(类似class,包含了很多属性和方法),就用 module.exports

函数与回调函数

在 JavaScript中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。

Node.js 中函数的使用与 JavaScript 类似,举例来说,你可以这样做:

function say(word) {console.log(word);
}function execute(someFunction, value) {someFunction(value);
}execute(say, "Hello");

以上代码中,我们把 say 函数作为 execute 函数的第一个变量进行了传递。这里传递的不是 say 的返回值,而是 say 本身!

这样一来, say 就变成了execute 中的本地变量 someFunction ,execute 可以通过调用 someFunction() (带括号的形式)来使用 say 函数。

当然,因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。


匿名函数

我们可以把一个函数作为变量传递。但是我们不一定要绕这个"先定义,再传递"的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:

function execute(someFunction, value) {someFunction(value);
}execute(function(word){ console.log(word) }, "Hello");

我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。

用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。


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

带着这些知识,我们再来看看我们简约而不简单的HTTP服务器:

var http = require("http");http.createServer(function(request, response) {response.writeHead(200, {"Content-Type": "text/plain"});response.write("Hello World");response.end();
}).listen(8888);

现在它看上去应该清晰了很多:我们向 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(8888);

回调函数

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

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

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

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

阻塞代码实例

创建一个文件 input.txt ,内容如下:

菜鸟教程官网地址:www.runoob

创建 main.js 文件, 代码如下:

var fs = require("fs");var data = fs.readFileSync('input.txt');console.log(data.toString());
console.log("程序执行结束!");

以上代码执行结果如下:

$ node main.js
菜鸟教程官网地址:www.runoob程序执行结束!

非阻塞代码实例

创建一个文件 input.txt ,内容如下:

菜鸟教程官网地址:www.runoob

创建 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.runoob
路由和全局对象

路由

Node.js提供了urlquerystring模块,用于处理和解析URL及查询字符串。

url模块提供了一些实用函数,用于解析和格式化URL。以下是url模块的一些常用方法:

  • url.parse(urlString[, parseQueryString[, slashesDenoteHost]]): 解析给定的URL字符串并返回一个URL对象。如果parseQueryStringtrue,则将查询字符串解析为查询参数对象。如果slashesDenoteHosttrue,则将双斜杠视为主机名的开始。

  • url.format(urlObject): 将URL对象格式化为URL字符串。

    • url.resolve(from, to): 解析一个目标URL相对于一个基础URL的路径。

    例如,使用url.parse()方法解析一个URL:

const url = require(‘url’);


const parsedUrl = url.parse('=string#hash');  
console.log(parsedUrl);

输出结果将是一个URL对象,包含了URL的各个部分的信息。

querystring模块提供了一些函数,用于解析和格式化查询字符串。以下是querystring模块的一些常用方法:

  • querystring.parse(str[, sep[, eq]]): 解析查询字符串为一个键值对对象。sep参数指定键值对的分隔符,默认为&eq参数指定键和值的分隔符,默认为=
  • querystring.stringify(obj[, sep[, eq]]): 将一个键值对对象格式化为查询字符串。参数意义与querystring.parse()相同。

例如,使用querystring.parse()方法解析一个查询字符串:

const querystring = require('querystring');  const queryString = 'name=John&age=30';  
const queryParams = querystring.parse(queryString);  
console.log(queryParams);

输出结果将是一个包含查询参数的对象。

变量作用域

在Node.js中,可以通过变量的声明方式和位置来区分不同的作用域范围。

  1. 全局作用域:在Node.js中,不在任何函数内部直接声明的变量具有全局作用域。这些变量可以在整个应用程序中访问。

例如:

var globalVar = "I am global!";
  1. 局部作用域:在函数内部使用var声明的变量具有局部作用域。这些变量只能在该函数内部访问。

例如:

function myFunction() {  var localVar = "I am local!";  
}
  1. 块级作用域:使用letconst声明的变量具有块级作用域。块级作用域是指在一对花括号{}内定义的作用域。这些变量可以在块级作用域内访问。

例如:

if (true) {  let blockVar = "I am block-scoped!";  
}

需要注意的是,Node.js采用了CommonJS模块系统,每个模块都有自己的作用域。在模块中定义的变量和函数默认情况下是局部的,只能在模块内部访问,但可以通过exports对象将它们导出,使其可以在其他模块中使用。

更多推荐

day3:Node.js 基础知识

本文发布于:2023-12-06 21:28:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1668984.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:基础知识   Node   js

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!