1.Node.js是什么
Node.js是一个让JS运行在服务器端的开发平台,它可以作为服务器向用户提供服务。Node.js中的
javascript只是Core javascript,或者说是ECMAJavaScript的一个实现。
2.Node.js能做什么
JS是为客户端为生,而Node.js是为网络而生。利用它可以轻松的开发出很多网站、社交应用、服务
器等等。
Node.js内建有一个HTTP服务器支持,可以实现一个网站和服务器的组合。
3.异步式I/O和事件驱动
Node.js最大的特点就是采用异步式I/O和事件驱动的架构设计。对于所有I/O都采用异步式的请求方
式,避免了上下文切换所带来的耗费系统资源、死锁、同步、低速连接攻击等问题。Nodejs在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件的带来。每个异步式IO请求完成以后会被推送到事件队列,等待程序进程进行处理。 Node.js进程在同一时刻只会处理一个事件,完成以后立
即进入事件循环检查并处理后面的事件。这样,CPU和内存同一时刻只会处理一个事件并且尽可能让耗时的I/O操作并行执行。
缺点就是不符合传统开发者的变成思维,往往需要把一个完整的逻辑拆分成为一个个事件,增加了开
发和调试难度。
4.console.log()相当于C的printf(),也可以接受任意个参数,支持%d,%s的变量引用
eg: console.log('%s:%d','hello',25);
// hello:25
// undefined(这个是返回值 )
5.在终端输入node 回车 便可以进入REPL模式 可以直接写入JS代码运行
6.与传统服务器语言不同的是(以PHP为例)
浏览器--HTTP服务器--PHP解释器
而Node.js直接将服务器抽离,直接面向浏览器用户
浏览器--Node
7.创建一个http服务器
//app.js
var http = require('http'); //调用HTTP模块
http.createServer(function(req, res) { //创建服务器
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Node.js</h1>');
res.end('<p>Hello World</p>');
}).listen(3000); //监听3000端口号
console.log("HTTP server is listening at port 3000.");
在终端运行 node app.js(注意要找到当前目录下)
会发现现在NOED一直在等待,进程不会退出事件循环。就无法实现实时刷新页面,不利于调试,因此可以安装一个模块supervisor
eg:$npm install -g supervisor
退出CTRL+C当前栈,运行 supervisor app.js
当代码改动的时候,运行的脚本被停止,然后重新启动,刷新页面,就可以看到实时刷新的数据。
8.阻塞与线程
1.同步式I/O(阻塞式I/O)
线程在执行中如果遇到磁盘读写或网络通信,通常要耗费较长时间。这时OS会剥夺这个线程的CPU控制权,使其暂停执行。同时将资源让给其他的工作线程,这种线程调度方式称之为阻塞,当I/O操作完毕时,OS将这个线程的阻塞状态解除,回复CPU对其的控制权,令其继续执行。
1.异步式读取文件
var fs = require('fs'); //请求FS模块
fs.readFile('file.txt','utf-8',function(err,data){ //读取文件
(当前目录/指定目录下的文件名 文件格式 回调函数(错误,文件里的数据))
if(err){
console.log(err);
}else{
console.log(data);
}
});
console.log('end');
可以看到先打印end然后在打印err或者data;
2.同步式读取文件
var fs = require('fs');
fs.readFileSync('file.txt','utf-8',function(err,data){//选择同步模式读取文件
if(err){
console.log(err);
}else{
console.log(data);
}
});
可以看到先打印err或者data再打印end
2.异步式I/O(非阻塞式I/O)
针对所有I/O操作不采用阻塞策略,当线程遇到I/O操作时,只是把请求发送给OS,继续执行下一条语句,当OS完成I/O操作时,以事件的形式通知执行I/O操作的线程,线程在特定的时候处理这个事件。因此,node有事件循环机制,不断的检查有没有未处理的事件,依次进行处理。
3.二者区别:
非阻塞模式下,一个线程永远在执行计算操作,CPU利用率为100%,I/O以事件的形式通知。
阻塞模式下,多线程往往能提高系统吞吐量,因为一个线程阻塞还有其他线程。
具体:
同步式I/O 异步式I/O
利用多线程提高吞吐量 单线程可实现提高吞吐量
通过事件片分割和线程调度利用多核CPU 通过功能划分利用多核
需要操作系统调度多线程使用多核CPU 可以将单线程绑定到单核CPU
难以充分利用CPU资源 充分利用CPU资源
内存轨迹大,数据局部性弱 内存轨迹小,数据局部性强
符合线性的编程思维 不符合传统变成思维
4.在node环境下可以直接运行JS代码
比如:node //进入node环境
Console.log(‘NEVER GIVE UP’);//在终端上打印出NEVER GIVE UP
//第二行undefined是返回值
PS:不能用alert,因为alert是window下的一个方法
或者运行JS文件
比如hello.js文件
直接 node hello.js便可以执行
9.Node所有的异步I/O操作在完成时都会发送一个事件到事件队列。然后通过轮询机制触发事件。
//event.js
var EventEmitter = require('events').EventEmitter;//请求事件对象
var event = new EventEmitter(); //实例化对象
event.on('some_event', function() { //注册事件some_event的一个监听器
console.log('some_event occured.');
});
setTimeout(function() { //1000MS后向event对象发送事件
event.emit('some_event');
}, 1000);
2.事件的循环机制
1.node.js程序是由事件循环开始,到事件循环结束。所有的逻辑都是基于事件的回调函数
2.事件的回调函数在执行的过程中,可以能会发出I/O请求或者直接触发(emit)事件,执行完毕以后再返回事件循环。事件循环会检查事件队列中有没有未处理的事件,直
到检测不到时才退出事件循环,进程结束。
10.模块和包是NODE最重要的支柱。开发一个具有一定规模的程序不可能只用一个文件,通常需要把各个功能拆分、安装,然后组合起来,模块证实为了实现这种方式而诞生。包和模块本质上没有区别,只不过包是由多个模块组成,是在模块基础上提供更高层的抽象。
//module.js
1.加载模块
var name;
exports.setName = function(thyName) { //将setName暴露给外界
name = thyName;
};
exports.sayHello = function() { //将sayHello暴露给外界
console.log('Hello ' + name);
};
//getmodule.js
var myModule = require('./module');//请求加载module.js这个模块
myModule.setName('Andy'); //调用module.js里面的方法
myModule.sayHello();
最终输出Hello Andy
2.单次加载
var myModule1 = require('./module');
myModule.setName('Andy');
var myModule2 = require('./module');
myModule.setName('King');
myModule.sayHello();//Hello King
因为他们指向了同一个实例,myModule1被myModule2覆盖
3.覆盖exports
function Hello() {
var name;
this.setName = function (thyName) {
name = thyName;
};
this.sayHello = function () {
console.log('Hello ' + name);
};
};
exports.Hello = Hello; //前Hello是一个暴露属性给外界 后面的hello是指 function Hello
这样想获取hello对象就需要require('./filename').Hello来获取hello对象
!麻烦。
如果最后一行这样引用module.exports = Hello;这样只要var Hello = require('./hello');就
可以了.
注意:在外部引用该模块时,其接口对象就是要输出Hello对象本身,而不是原先的expots.
exports本身一个普通的空对象{},专门用于声明接口,可以用其他东西来代替,譬如Hello对象。但是i不可以通过对exports直接复制代替module.exports赋值,虽然他们指向同一个变量,但是
exports本身会在模块执行结束以后释放,module不会,因此只能通过module.exports来改变接口。
11.node debug XXX.js可以进入debug调试
node --debug[=port] script.js
node --debug-brk[=port] script.js可以进入远程调试 默认端口为5858