node.js基础学习

什么是node?

简单来说,node是JS的一种运行环境。在此之前,我们都知道JS可以在浏览器中运行,可以为网页添加各种交互,因此,浏览器也是JS的运行环境。随着Chrome浏览器的发布,带来了全新的V8引擎。经过多年的发展和优化,性能和安全性都已经达到了相当的高度。而 Node.js 则进一步将 V8 引擎加工成可以在任何操作系统中运行 JavaScript 的平台。
在这里插入图片描述
两个运行环境如图所示,他们都包含了JavaScript语言标准ECMAScript,在浏览器环境还有

  • 浏览器对象模型(Browser Object Model,简称 BOM),也就是 window 对象
  • 文档对象模型(Document Object Model,简称 DOM),也就是 document 对象

在node中也有特有的代表不同含义的对象

  • global:和window一样,任何global对象上的属性都可以被全局访问到。
  • process:所有全局执行上下文中的内容都在process对象中,提供了与操作系统的简单接口。
  • buffer:让 JavaScript 也能够轻松地处理二进制数据流,结合 Node 的流接口(Stream),能够实现高效的二进制文件处理。
  • __filename:当前所运行 Node 脚本的文件路径。
  • __dirname:当前所运行 Node 脚本所在目录路径。
模块系统

node中的每一个文件都是一个模块,有着自己的作用域。使用Commonjs模块规范,该模块系统有三个核心的全局对象:require、module和exports。

  • require

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')

  • exports
    exports用于导出当前node模块的内容,例如有一个module_a.js文件
function a {
	console.log('module_a.js')
	}
//导出函数a
exports.a = a

通过将a函数添加到exports对象中,其他模块就可以通过require引入调用。

  • module
    Node内部有个Module的构造函数,每个文件中的module都是其的一个实例。我们在node文件中可以通过console.log(module)来查看其包含的属性,export其实就是对module.exports的引用
单线程

举个例子:假设一个用户分别向node服务器和PHP服务器各同时发起两次请求

//node
var books =['node','php'];
function serveBooks(){
	var html = '<b>' + books.join('</b><br><b>') + '<b>';
	books = [];
	return html;
}
//php
$books = array('node','php');
function serveBooks(){
	$html = '<b>' . books.join('</b><br><b>') . '<b>';
	$books = array()
	return $html;
}

在上述的两个serveBooks函数中,都将books数组重置了。

  • node会将完整的book返回给第一次请求,而第二次则返回一个空的book.
  • PHP两次请求都能返回完整的book

这两者的区别就在于基础架构上。node采用了一个长期运行的线程(单线程),使得第二次请求的book是第一次请求操作过的。而Apache会产生多个线程,每次都会刷新状态,因此不会受到影响。

node为JavaScript引入了一个复杂的概念:共享状态的并发。通俗的说,在node中,要谨慎处理回调函数对当前内存中变量的修改,并且还要注意对错误的处理是否会潜在地修改这些状态。这可能会导致整个进程不可用。

非阻塞IO

既然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确保了单线程执行时,不会有因为数据库访问或者是硬盘访问等操作导致操作会被挂起。

事件循环

node中的事件循环分为三个队列,事件循环会按顺序执行三个队列中的事件。

  1. timer:setTimeout、setInterval 定时器操作
  2. poll:文件读写、网络请求、数据库操作等I\O操作
  3. check:setImmediate

如果timer队列和check队列都为空的时候,循环会在poll队列等待(优先处理IO事件)。
timer事件在node中的最小间隔为1ms,因此与setImmediate事件的执行顺序不确定。我们希望是确定的,当存在这种情况时,会将其同时放在一个IO事件的回调中,这样事件循环就从poll阶段开始,总是会先执行setImmediate了

process.nextTick() 属于微任务,并且总是最先执行.


事件模块

在这里插入图片描述

fs模块

fs模块是唯一一个同时提供同步和异步的API模块,用于对系统文件及目录进行读写操作。

  • fs.readFile(filename,[option],callback) 方法读取文件。
  • fs.writeFile(filename,data,[options],callback)写入内容到文件。
    fs.readfs.write功能类似fs.readFilefs.writeFile,但提供更底层的操作,需要使用fs.open打开文件和fs.close关闭文件。实际应用中多用fs.readFilefs.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

传输控制协议(TCP)是一个面向连接的协议,它保证了两台计算机之间数据传输的可靠性和顺序。

  • 面向连接:当在TCP连接内进行数据传递时,发送的IP数据报包含了标识该连接以及数据流顺序的信息。
  • 面向字节:TCP对字符以及字符编码是不关心的,对消息格式没有严格的约束,具有很好的灵活性。
  • 可靠性:当数据发送之后,发送方在窗口时间内等待“消息送达”的确认消息,否则会重发消息。可以有效解决网络错误或者网络阻塞的情况。
  • 流控制:所谓流控制就是让发送发送速率不要过快,让接收方来得及接收。原理这就是运用TCP报文段中的窗口大小字段来控制,发送方的发送窗口不可以大于接收方发回的窗口大小。
  • 拥堵控制: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

超文本传输协议(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服务器已经搭建成功了。

Connect
  • Node.js为常规的网络应用提供了基本的API。然而,实际情况下,绝大部分网络应用都需要完成一系列类似的操作,这些类似的操作你一定不想每次都重复地基于原始的API去实现。
  • Connect是一个基于HTTP服务器的工具集,它提供了一种新的组织代码的方式来与请求、响应对象进行交互,称为中间件(middleware)。
  • 中间件具有代码复用的好处。在没有使用中间件的情况下,处理请求的这些代码都放在一个简单的事件处理器中(createServer的回调函数中),这将会是一个非常复杂的处理过程。而使用中间件后会将应用拆分为更小单元(让代码有更强大的表达能力),还能够实现很好的重用性。
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);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值