简单来说,node是JS的一种运行环境。在此之前,我们都知道JS可以在浏览器中运行,可以为网页添加各种交互,因此,浏览器也是JS的运行环境。随着Chrome浏览器的发布,带来了全新的V8引擎。经过多年的发展和优化,性能和安全性都已经达到了相当的高度。而 Node.js 则进一步将 V8 引擎加工成可以在任何操作系统中运行 JavaScript 的平台。
两个运行环境如图所示,他们都包含了JavaScript语言标准ECMAScript,在浏览器环境还有
在node中也有特有的代表不同含义的对象
在 ES2015 标准出现以前, JavaScript 语言定义本身并没有模块化的机制,构建复杂应用也没有统一的接口标准。人们通常使用一系列的
而这种方式引入的模块变多以后,就会对全局命名空间的污染以及命名冲突的问题。
node摒弃了采用一堆全局变量的方式,转而引入了一个简单强大的模块系统,该模块系统有三个核心的全局对象:require、module和exports。
require 用于导入其他 Node 模块,其参数接受一个字符串代表模块的名称或路径,通常被称为模块标识符。
绝对模块:node通过在其内部node_modules查到的模块,或者node内置的例如fs这样的模块,可以直接通过名字来requirevar fs = require('fs)
相对模块将require指向一个相对工作目录中的JavaScript文件,比如我们在同一目录中创建名为module_a.js、module_b.js、main.js三个文件,如果我们想要在main.js中引入module_a.js和module_b.js,则需要写作require('./module_a')
function a {
console.log('module_a.js')
}
//导出函数a
exports.a = a
通过将a函数添加到exports对象中,其他模块就可以通过require引入调用。
console.log(module)
来查看其包含的属性,export其实就是对module.exports的引用举个例子:假设一个用户分别向node服务器和PHP服务器各同时发起两次请求
//node
var books =['node','php'];
function serveBooks(){
var html = '' + books.join('
') + '';
books = [];
return html;
}
//php
$books = array('node','php');
function serveBooks(){
$html = '' . books.join('
') . '';
$books = array()
return $html;
}
在上述的两个serveBooks函数中,都将books数组重置了。
这两者的区别就在于基础架构上。node采用了一个长期运行的线程(单线程),使得第二次请求的book是第一次请求操作过的。而Apache会产生多个线程,每次都会刷新状态,因此不会受到影响。
node为JavaScript引入了一个复杂的概念:共享状态的并发。通俗的说,在node中,要谨慎处理回调函数对当前内存中变量的修改,并且还要注意对错误的处理是否会潜在地修改这些状态。这可能会导致整个进程不可用。
既然node执行时只有一个线程,那又是如何做到高并发的,这就要说到node的另一个特点,非阻塞(异步)
//php
print('Hello');
sleep(5);
print('World');
//node
console.log('Hello')
setTimeout(function(){
console.log('stop')
},3000)
console.log('World')
观察上面的两段代码有什么区别
除了语义上的区别(Node.js使用了回调函数)以外,两者最大的区别体现在了阻塞和非阻塞上,在PHP中,sleep()
阻塞了线程的执行,导致World无法输出,而node使用了事件轮询,因此这里的setTimeout是非阻塞的(异步),也就是说在3S之后会正常输出World。
事件轮询:从本质上来说Node会先注册事件,然后不停的询问内核这些事件是否已经分发。当事件分发时,对应的回调函数就会执行。如果没有事件触发,则会继续执行其他代码。
V8引擎执行JavaScript的速度非常快,结合非阻塞IO确保了单线程执行时,不会有因为数据库访问或者是硬盘访问等操作导致操作会被挂起。
fs模块是唯一一个同时提供同步和异步的API模块,用于对系统文件及目录进行读写操作。
fs.readFile(filename,[option],callback)
方法读取文件。fs.writeFile(filename,data,[options],callback)
写入内容到文件。fs.read
和fs.write
功能类似fs.readFile
和fs.writeFile
,但提供更底层的操作,需要使用fs.open
打开文件和fs.close
关闭文件。实际应用中多用fs.readFile
和fs.writeFile
。fs.open(path,flags,[mode],callback)
方法用于打开文件,以便fs.read()
读取。fs.read(fd,buffer,offset,length,position,callback)
接收6个参数。用于关闭文件//异步写法
var fs = require('fs');
fs.readdir(__dirname, function(err, files) {
console.log(files);
});
//同步写法
var fs = require('fs');
console.log(fs.readdirSync(__dirname))
传输控制协议(TCP)是一个面向连接的协议,它保证了两台计算机之间数据传输的可靠性和顺序。
var net = require('net');
var tcp_server = net.createServer(); // 创建 tcp server
var Sockets = {
};
var SocketID = 1;
// 监听 端口
tcp_server.listen(5678,function (){
console.log('tcp_server listening 5678');
});
// 处理客户端连接
tcp_server.on('connection',function (socket){
console.log(socket.address());
Sockets[SocketID] =socket;
SocketID++;
DealConnect(socket)
})
tcp_server.on('error', function (){
console.log('tcp_server error!');
})
tcp_server.on('close', function () {
console.log('tcp_server close!');
})
// 处理每个客户端消息
function DealConnect(socket){
socket.on('data',function(data){
data = data.toString();
// 向所有客户端广播消息
for(var i in Sockets){
Sockets[i].write(data);
}
// socket.write(data);
console.log('received data %s',data);
})
// 客户端正常断开时执行
socket.on('close', function () {
console.log('client disconneted!');
})
// 客户端正异断开时执行
socket.on("error", function (err) {
console.log('client error disconneted!');
});
}
超文本传输协议(HTTP)是属于TCP的上层协议,构建在请求和相应的概念之上。nodejs中的http模块中封装了一个HTPP服务器和一个简易的HTTP客户端,http.Server是一个基于事件的http服务器,http.request则是一个http客户端工具,用于向http服务器发起请求。而上面的createServer方法中的参数函数中的两个参数req和res则是分别代表了请求对象和响应对象。其中req是http.ServerRequest的实例,res是http.ServerResponse的实例,这可以从nodejs中的源码中获取这个信息
//一个简单的http服务器
var http=require("http");
http.createServer(function(req,res){
res.writeHead(200,{
"content-type":"text/plain"
});
res.write("hello World");
res.end();
}).listen(3000);
打开浏览器,输入localhost:3000,如果看到屏幕上的hello World了,这表明这个最简单的nodejs服务器已经搭建成功了。
var http = require('http'),
fs = require('fs');
/**
* 创建服务器
*/
var server = http.createServer(function(req, res) {
// 检查URL是否和服务器目录下的文件匹配,如果匹配,则读取该文件并展示出来
// 请求.jpg图片
if('GET' == req.method
&& '/images' == req.url.substr(0, 7)
&& '.jpg' == req.url.substr(-4)) {
// 返回对应图片
// ...
fs.stat(__dirname + req.url, function(err, stat) {
// 如果检查文件是否存在时发生错误,则终止进程并发送HTTP 404状态码告知无法找到请求的图片。
// 对于stat成功但是路径所表示的并非是文件时,也要做此处理。
if(err || !stat.isFile()) {
res.writeHead(404);
res.end('Not Found');
return;
}
// 发送图片资源
serve(__dirname + req.url, 'application/jpg');
});
} else if('GET' == req.method && '/' == req.url) {
// 发送html文件,默认请求index.html
// ...
// console.log(__dirname);
serve(__dirname + '/index.html', 'text/html');
} else {
// 处理其他请求,返回404错误码
res.writeHead(404);
res.end('Not found');
}
/**
* 根据文件路径来获取文件内容,并添加'Content-Type'头信息
* @param {[type]} path 文件路径
* @param {[type]} type Content-Type头信息
* @return {[type]} [description]
*/
function serve(path, type) {
res.writeHead(200, {
'Content-Type': type});
fs.createReadStream(path).pipe(res);
}
});
server.listen(3000);