nodejs学习小记
Posted 菜菜粥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nodejs学习小记相关的知识,希望对你有一定的参考价值。
以下是我借助网站学习平台, 粗略的学习下nodejs, 学完后,基本能做到服务端一些罗辑的实现, 用到 module, files, router, 外加js的一些理论的组合, 就可以完成一些简单的交互程序
Node.js 就是运行在服务端的 JavaScript , node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js 适合I/O 密集型的应用,而不是计算密集型的应用, 因为一个Node.js 进程只有一个线程,因此在任何时刻都只有一个事件在执行。
**
一. Node.js 创建HTTP服务器
**
在你的项目的根目录下创建一个叫 server.js 的文件,并写入以下代码:
//第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量。
var http = require('http');
//接下来我们调用 http 模块提供的函数: createServer 。这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。
http.createServer(function (request, response)
response.writeHead(200, 'Content-Type': 'text/plain');
response.end('Hello World\\n');
).listen(8888);
console.log('Server running at http://127.0.0.1:8888/');
**
分析:
这个模块对于网络交互很重要 这里的request是一个对象 ,包含了请求完成后的内容, 可以通过console.log(util.inspect(request))来把这个对象的内容转成字符串来查看 类似如下的代码, response也是一个对象,这两个对象都是在回调函数中被赋值的, 用来实现客户端和服务端的通信
var http = require('http');
var util = require('util');
http.createServer(function (request, response)
response.writeHead(0, 'Content-Type': 'text/plain');
console.log(util.inspect(response,true));
response.end('Hello World\\n');
).listen(3000);
console.log('Server running at http://127.0.0.1:8888/');
二.模块系统
**
Node.js提供了一个简单的模块系统,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。
Node.js 提供了exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
//main.js
var hello = require('./hello');
hello.world();
//hello.js
exports.world = function()
console.log('Hello World');
hello.js 通过 exports 对象把 world 作为模块的访 问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问main.js 中 exports 对象的成员函数了。
//hello.js
function Hello()
varname;
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();
模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function()。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。
Node.js 的 require方法中的文件查找策略如下:由于Node.js中存在4类模块(原生模块和3种文件模块),尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:
尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存中加载已经存在的模块。
equire方法接受以下几种参数的传递:
http、fs、path等,原生模块。
./mod或../mod,相对路径的文件模块。
/pathtomodule/mod,绝对路径的文件模块。
mod,非原生模块的文件模块
分析:
使用模块可以使得代码重复利用,同时便于代码维护,同时你可以简化工作, 只要专心完成该模块的任务即可, 毕竟人是一个生物,是有极限,只有合理的分配,才发挥人的效率
三 .Node.js 事件
Node.js 所有的异步I/O 操作在完成时都会发送一个事件到事件队列
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。 你可以通过require(“events”);来访问该模块
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就 是事件发射与事件监听器功能的封装。
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', 'byvoid', 1991);
运行的结果是:
listener1 byvoid 1991
listener2 byvoid 1991
EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', function (stream)
console.log('Ah, we have our first user!');
);
EventEmitter.on(event, listener)、emitter.addListener(event, listener) 为指定事件注册一个监听器,接受一个字 符串 event 和一个回调函数 listener。
server.on('connection', function (stream)
console.log('someone connected!');
);
EventEmitter.removeListener(event, listener)移除指定事件的某个监听器,listener 必须是该事件已经注册过的监听器
var callback = function(stream)
console.log('someone connected!');
;
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定 event,则移除指定事件的所有监听器。
EventEmitter 定义了一个特殊的事件 error,它包含了”错误”的语义,我们在遇到 异常的时候通常会发射 error 事件。
当 error 被发射时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。
我们一般要为会发射 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:
var events = require('events');
var emitter = newevents.EventEmitter();
emitter.emit('error');
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包fs、net、 http 在内的,只要是支持事件响应的核心模块都是EventEmitter 的子类
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。
其次javascript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系
因为nodejs是基于js的服务端脚本,所以也会有类似闭包的引用,它的事件就用类似js的事件去学就好了 更多的实验 有待考证 这里先略过
四 . Node.js 路由
我们要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码 我们需要的所有数据都会包含在request对象中
简单的例子
// 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 再改变下路由, 在终端就有信息, 显示的就是你的路由pathname
分析:
路由 我知道在MVC 架构中 很重要,它是分配控制器的重要的东西,当然这里的路由也很重要,通过处理路由,可以让不同的代码执行它相应的工作,因为web应用的url这个东西,是非常重要的,很多的交互都可以通过url来实现请求
五 .Node.js 全局对象
process 是一个全局变量,即 global 对象的属性。
process.argv是命令行参数数组,第一个元素是 node,第二个元素是脚本文件名, 从第三个元素开始每个元素是一个运行参数。
// argv.js
console.log(process.argv);
终端运行 node argv.js 1991 name=byvoid –v “Carbo Kuo”
结果为[ ‘node’, ‘/home/byvoid/argv.js’, ‘1991’, ‘name=byvoid’, ‘–v’, ‘Carbo Kuo’ ]
console.log 接受若干 个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。
console.log('Hello world');
console.log('byvoid%diovyb');
console.log('byvoid%diovyb', 1991);
输出的结果
Hello world
byvoid%diovyb
byvoid1991iovyb
分析:
全局变量, 和c,c++等程序中的全局变量的定义差不多,不过我感觉,预设封装出来还是挺有必要的,能简化一定的罗辑, 但是要注意,不要设置过多的全局变量,以免造成混乱。 同时,使用命名空间来实现管理变量和对象,对于程序的维护等等方面都有好处
六 .Node.js 常用工具 util
util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足。
util.inherits(constructor, superConstructor)是一个实现对象间原型继承 的函数。 其实这里也可以直接用 superConstructior = new constructor() 这么写
util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换 为字符串的方法,通常用于调试和错误输出。它至少接受一个参数 object,即要转换的对象 特别要指出的是,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。
七. Node.js 文件系统
Node.js 文件系统封装在 fs 模块是中,它提供了文件的读取、写入、更名、删除、遍历目录、链接等POSIX 文件系统操作。
与其他模块不同的是,fs 模块中所有的操作都提供了异步的和 同步的两个版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。我们以几个函数为代表,介绍 fs 常用的功能,并列出 fs所有函数 的定义和功能
fs.readFile(filename,[encoding],[callback(err,data)]) 读取文件函数语法
- filename(必选),表示要读取的文件名。
- encoding(可选),表示文件的字符编码
- callback 是回调函数,用于接收文件的内容
如果不指 定 encoding,则 callback 就是第二个参数.回调函数提供两个参数 err 和 data,err 表 示有没有错误发生,data 是文件内容。如果指定了 encoding,data 是一个解析后的字符 串,否则 data 将会是以 Buffer 形式表示的二进制数据
varfs = require('fs');
fs.readFile('content.txt', function(err, data)
if(err)
console.error(err);
else
console.log(data);
);
假设content.txt 中的内容是UTF-8 编码的 Text 文本文件示例
运行结果为
<Buffer 54 65 78 74 20 e6 96 87 e6 9c ac e6 96 87 e4 bb b6 e7 a4 ba e4 be 8b>
如果你指定编码
var fs = require('fs');
fs.readFile('content.txt', 'utf-8', function(err, data)
if (err)
console.error(err);
else
console.log(data);
);
那么运行结果则是:
Text 文本文件示例
fs.open
fs.open(path, flags, [mode], [callback(err, fd)])是POSIX open 函数的 封装,与C 语言标准库中的 fopen 函数类似。它接受两个必选参数,path 为文件的路径, flags 可以是以下值。
- r :以读取模式打开文件。
- r+ :以读写模式打开文件。
- w :以写入模式打开文件,如果文件不存在则创建。
- w+ :以读写模式打开文件,如果文件不存在则创建。
- a :以追加模式打开文件,如果文件不存在则创建。
- a+ :以读取追加模式打开文件,如果文件不存在则创建
fs.read
fs.read(fd, buffer, offset, length, position, [callback(err, bytesRead, buffer)])
- fd: 读取数据并写入 buffer 指向的缓冲区对象。
- offset: 是buffer 的写入偏移量。
- length: 是要从文件中读取的字节数。
- position: 是文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。
- callback:回调函数传递bytesRead 和 buffer,分别表示读取的字节数和缓冲区对象。
var fs = require('fs');
fs.open('content.txt', 'r', function(err, fd)
if(err)
console.error(err);
return;
var buf = new Buffer(8);
fs.read(fd, buf, 0, 8, null, function(err, bytesRead, buffer)
if(err)
console.error(err);
return;
console.log('bytesRead: ' + bytesRead);
console.log(buffer);
)
);
这里是从文件content.txt的当前文件的起始指针读取8个bytes长度的数据54 65 78 74 20 e6 96 87 后面的那个函数参数中的bytesread即为回调函数返回的文件内容的长度, buffer即为文件包含的内容 都是用16进制位来表示
运行结果是:
bytesRead: 8
<Buffer 54 65 78 74 20 e6 96 87>
一般来说,除非必要,否则不要使用这种方式读取文件,因为它要求你手动管理缓冲区 和文件指针,尤其是在你不知道文件大小的时候,这将会是一件很麻烦的事情。
分析:
文件读写的所有操作,当然是重要的,因为一个应用必然会有data的产生和使用,所以如何save和read也是重要的, 这里就要掌握每个参数的作用,已经每个函数对象的具体使用情景就可以了,
八. Node.js GET/POST请求
获取GET请求内容
var http = require('http');
var url2 = require('url');
var util = require('util');
http.createServer(function(req, res)
res.writeHead(200, 'Content-Type': 'text/plain');
res.end(util.inspect(url2.parse(req.url, true)));
).listen(3000);
在浏览器中访问http://localhost:3000/user?name=zhw&email=zh@zh.cc 然后查看返回结果:
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?name=zhw&email=zh@zh.cc',
query: name: 'zhw', email: 'zh@zh.cc' ,
pathname: '/user',
path: '/user?name=zhw&email=zh@zh.cc',
href: '/user?name=zhw&email=zh@zh.cc'
获取POST请求内容
POST请求的内容全部的都在请求体中,http.ServerRequest并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。
var http = require('http');
var querystring = require('querystring');
var util = require('util');
http.createServer(function(req, res)
var post = ''; //定义了一个post变量,用于暂存请求体的信息
req.on('data', function(chunk) //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
post += chunk;
);
req.on('end', function() //在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
post = querystring.parse(post);
res.end(util.inspect(post));
);
).listen(3000);
分析:
这个对get和post请求的操作,也是和路由息息相关的,因为你要对get和poat请求到的东西,进行相应的分析操作,实线路由的选择等等
一个web应用总的来说,应该就是客户端,服务端, 客户端和服务端的交互,这个交互才是核心,如何交互的快,稳定,效率高,才是重要的,其他的什么的语言都是用来完成和解决相应的问题, 语言应该实在问题的实际需要上,发展而来的,当然发展一门语言,也不是简单空口谈谈而已 我觉得趋势就是 更加简单和更加高效
以上是关于nodejs学习小记的主要内容,如果未能解决你的问题,请参考以下文章