代码改变世界

Node基础知识点--学习笔记(一)

2015-04-18 21:04  龙恩0707  阅读(3006)  评论(0编辑  收藏  举报

一:建立http服务器;

在D盘建立一个文件夹node,放入app.js,代码如下:

var http = require('http');
http.createServer(function(req,res){
    res.writeHead(200,{'Content-type':'text/html'});
    res.write("<h1>Node.js</h1>");
    res.end("<p>Hello worldA</p>");
}).listen(3000);
console.log("11111");

进入D盘node文件下,使用命令 node app.js 即可输出11111,接着在浏览器端我们输入 127.0.0.1:3000 刷新即可看到浏览器下 打印出 Node.js 和 Hello worldA的文案;

上面的程序调用了nodeJS提供的http模块,对所有http请求答复同样的内容监听了3000端口,在终端运行这个脚本发现并没有退出,而是一直等待,这是因为创建了事件监听器;我们还发现如果我们修改了app.js代码后,需要重新运行下node app.js就可以在浏览器端刷新下就可以看到效果,而不是像php一样,修改了下直接可以看效果,这是因为nodejs只有在第一次引用到某部分时才会去解析脚本文件,以后直接都会去访问内存,避免重复执行,而php不同,因为他总是读取并执行脚本,nodejs相比较,性能是提高了,但是对于开发者不方便调试,因此supervisor可以帮助我们实现这个功能;首先我们需要安装supervisor;安装如下:

现在我们可以使用supervisor命令来运行app.js哦!

进入对应目录后 执行supervisor app.js  也可以执行。之后我们修改代码后,直接刷新下浏览器就可以看到效果了!

二:定时器setTimeout(),setInterval(), process.nextTick()及setImmediate()之间的区别;

  1. setTimeout()与setInterval()和浏览器的api是一样的,分别用于单次和多次定时执行任务。调用setTimeout()与setInterval()创建的定时器会被插入到定时器观察者内部的一个红黑树中,每次Tick执行时候,会从该红黑树中迭代取出定时器对象,检查是否超过定时时间,如果超过,就形成一个事件,它的回调函数将会被执行,但是setTimeout()与setInterval()的缺点是:不精确,不准确,比如说setTimeout()或者setInterval()设定一个任务是5毫秒后执行,但是呢在执行4毫秒后,有一个任务也占了5毫秒的cpu时间片,再次轮到定时器执行时候,时间就已经过期了4毫秒了。
  2. process.nextTick(), 每次执行process.nextTick()时候,它会将回调函数放入到事件队列中,也就是有一个回调函数,在下一次执行Tick时候,将回调函数取出再执行,执行效率是最高的。
  3. setImmediate(); 此方法和process.nextTick()方法一样,都是将回调函数延迟执行;

比如如下代码:

process.nextTick(function(){
    console.log('nextTick延迟执行');
});
setImmediate(function(){
    console.log("setImmediate延迟执行");
});
console.log("正常执行");
其执行结果如下:
正常执行
nextTick延迟执行
setImmediate延迟执行

从上面可以看到,process.nextTick()中的回调函数的执行优先级高于setImmediate(). 因为事件循环对观察者的检查是有先后顺序的,procee.nextTick()属于ide观察者,setImmediate()属于check观察者。在每一个循环中,ide观察者优先于check观察者。

但是在具体实现上,process.nextTick()的回调函数保存在一个数组中,setImmediate()的结果保存在链表中。在行为上,process.nextTick()在每轮循环中都会将数组中的回调函数全部执行完,而setImmediate()在每轮循环中是执行链表中的一个回调函数。如下代码:

// 加入2个nextTick()的回调函数
process.nextTick(function(){
    console.log('nextTick延迟执行1');
});
process.nextTick(function(){
    console.log('nextTick延迟执行2');
});

// 加入2个setImmediate()的回调函数
setImmediate(function(){
    console.log("setImmediate延迟执行1");
    // 进入下次循环
    process.nextTick(function(){
        console.log('强势插入');
    });
});
setImmediate(function(){
    console.log("setImmediate延迟执行2");
});

console.log("正常执行");
// 其执行结果如下:
nextTick延迟执行1
nextTick延迟执行2
setImmediate延迟执行1
强势插入
setImmediate延迟执行2

从上面的结果可以看出,当第一个setImmediate()的回调函数执行完,并没有立即执行第二个,而是进入了下一轮循环,再次按process.nextTick()优先,setImmediate()的顺序执行。之所以这样设计,是为了保证每轮循环能够较快地执行结束,防止cpu占用过多而阻塞后续的I/O.

 4. 使用require.resolve函数查询完整模块名;

首先我们在node文件夹目录下新建app.js, 内容如下:

var str = "aaa";

export.str = str;

定义一个变量,使用export导出变量,供其他模块调用,然后再在相同的目录下新建一个test.js;内容如下:

console.log(require.resolve('./app.js'));

接着我们进入node文件夹目录下,执行test.js代码,如下命令:

可以看到使用require.resolve方法可以打印出模块的完整的路径名。

5. require.cache 对象

在node.js中,定义了一个require.cache对象,该对象代表缓存了所有已被加载模块的缓存区;

我们现在还是以上面的test.js为例:代码改成如下:

//console.log(require.resolve('./app.js'));

console.log(require.cache);

接着我们如下运行代码可以看到我们的缓冲区如下:

6. __filename变量与__dirname变量。

在node.js中,预定义了2个变量,用于获取当前模块文件名的__filename变量与用于获取当前目录名的__dirname变量;

 1.  __filename变量。

    在任何模块文件内部,可以使用__filename变量获取当前模块文件的带有完整绝对路径的文件名。

    还是上面node文件内,其中在app.js内代码改成如下:

    var testMoudle = require('./test.js');

    然后在test.js代码内改成如下:

    console.log(__filename);

   运行结果如下:

2. __dirname变量。

   在任何模块文件内部,可以使用__dirname变量获取当前模块文件所在目录的完整绝对路径。

   还是上面node文件内,其中在app.js内代码改成如下:

   var testMoudle = require('./test.js');

    然后在test.js代码内改成如下:

   console.log(__dirname);

  运行结果如下:

7. 事件处理机制及事件循环机制。

EventEmitter类的各种方法;

方法名与参数 含义
addListener(event,listener) 对指定事件绑定事件处理函数
on(event,listener) addListener方法的别名
once(event,listener) 对指定的事件执行一次的处理函数
removeListener(event,listener) 对指定事件解除事件的处理函数
removeAllListeners([event]) 对指定的事件解除所有的事件处理函数
setMaxListeners(n) 指定事件处理函数的最大数量。
listeners(event) 获取指定事件的所有事件处理函数
emit(event,[arg1],[arg2]) 手工触发指定事件

下面我们来看看使用on(或者addListener)方法绑定的事件处理函数;

在node文件夹内中的test.js改为下面的代码

// 使用on方法绑定事件处理函数
var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
    console.log(req.url);
    res.end();
});
server.listen(1337);

然后执行node test.js 运行如下:

可以看到控制台输出了两个目标的url地址,其中第一个url地址为用户输入的客户端请求的目标url地址,”/” 代表用户输入的目标url地址为web应用程序的根目录,第二个目标url地址为浏览器为页面在收藏夹中的显示图标(默认为favicon.ico)而自动发出请求的目标url地址,但是我们把代码写成如下可以屏蔽收藏夹的请求。

var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log(req.url);
    }
    res.end();
});
server.listen(1337);

如下所示:

我们还可以通过多个on方法的执行对同一个事件绑定多个事件处理函数,代码如下:

var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log('接收到客户端请求。');
    }
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log(req.url);
    }
    res.end();
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log('发送响应完毕!');
    }
    res.end();
});
server.listen(1337);

运行效果如下:

2. 自定义事件并将其触发;

// 自定义事件并将其触发
var http = require('http');
var server = http.createServer();
server.on('customEvent',function(arg1,arg2,arg3){
    console.log('自定义事件被触发');
    console.log(arg1);
    console.log(arg2);
    console.log(arg3);
});
server.emit('customEvent','自定义参数1','自定义参数2','自定义参数3');
server.listen(1337);

执行如下:

3. 获取指定事件的事件处理函数的数量.

EventEmitter类自身拥有一个listenerCount方法,可用来获取某个对象的指定事件的事件处理函数的数量。代码如下:

EventEmitter.listenerCount(emitter,event);

在listenerCount方法中,使用2个参数,其中第一个参数用于指定需要获取那个对象的事件处理函数的数量,第二个参数用于指定需要获取那个事件的处理函数的数量。

代码如下:

var http = require('http');
var events = require('events');
var server = http.createServer();

// 为server服务器在接收到客户端请求时触发的request事件绑定多个事件处理函数
server.on('request',function(req,res){
    if(req.url !== '/favicon.cio') {
        console.log('接收到客户端请求');
    }
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.cio') {
        console.log(req.url);
    }
    res.end();
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.cio') {
        console.log('发送响应完毕');
    }
});
server.listen(1337);
console.log(events.EventEmitter.listenerCount(server,'request'));

执行如下:

4. EventEmitter类自身所拥有的事件。

在events模块中,为EventEmitter类本身定义了2个事件,newListener事件与removeListener事件。任何时候,当对继承了EventEmitter类的子类实列对象绑定事件处理函数时,都将触发EventEmitter类的newListener事件。如下代码:

var http = require('http');
var server = http.createServer();

server.on('removeListener',function(e,f){
    console.log("对"+e+"事件取消事件的处理函数");
    console.log(f);
});
server.on('newListener',function(e,f){
    console.log("对"+e+"事件添加事件的处理函数");
    console.log(f);
});
var testFunction = function(){
    // 为server服务器在接收到客户端请求时触发的request事件绑定多个事件处理函数
    server.on('request',function(req,res){
        if(req.url !== '/favicon.cio') {
            console.log('接收到客户端请求');
        }
    });
    server.on('request',function(req,res){
        if(req.url !== '/favicon.cio') {
            console.log(req.url);
        }
        res.end();
    });
    server.on('request',function(req,res){
        if(req.url !== '/favicon.cio') {
            console.log('发送响应完毕');
        }
    });
};
server.on('request',testFunction);
server.removeListener('request',testFunction);
server.listen(1337);

运行如下:

5. 在Node.js中使用调试器。

在node.js中,提供了一个在命令行界面中可以使用的调试器,可以利用该调试器来进行一些应用程序的简单调试,列如显示代码,变量及函数的返回值。

一:在命令行窗口中使用调试器;

在命令行窗口中,可以使用node debug 命令来启用调试器,代码如下所示:

node  debug <需要被执行的脚本文件名>

比如现在在app.js加入如下JS代码:

console.log("hello world");
function foo() {
console.log("hello foo");
return 100;
}
var bar = 'this is a pen.';
var http = require('http');
var i = foo();
console.log(i);

截图如下:

然后在命令行中使用 node debug 命令调试该脚本文件,如下:

如果我们想要继续执行下面的代码,我们可以在debug命令后输入 “cont”命令或 ”c”命令(“continue”命令的缩写),以继续执行剩余脚本。如下所示:

但是如果我们不需要执行完剩余的所有脚本代码,我们可以在 ”debug”命令后输入”next”命令或”n” 命令,将程序执行到下一句可执行代码之前。如下所示:

但是如果我们想要进入函数内部的话,我们可以在上面的debug后面输入”step”命令或”s”命令,程序将会暂停在函数内第一行代码之前。如下图所示:

比如JS代码先把require js去掉 如下代码:

console.log("hello world");
function foo() {
console.log("hello foo");
return 100;
}
var bar = 'this is a pen.';
//var http = require('http');
var i = foo();
console.log(i);

执行如下:

当使用”step”命令或”s”命令进入函数内部后,可以继续使用 ”next”命令或”n” 命令逐句执行函数内部的每一行代码,如下图所示:

在函数内部代码被逐句执行的时候,我们可以使用 “out”命令或”o” 命令立即执行完函数内剩余的所有代码,程序将被暂停在调用函数的代码之后的下一句代码之前。如下图所示:

等等,书上还有很多命令,我这边就忽略了,我想现在我们可以来学习一下使用更好的一款调式工具---node-inspector ,它通过web网页与用户进行交互,比node.js中自带的调式器更美观,更方便。

首先我们需要安装node-inspector,

   1. 在命令行窗口中输入如下命令

      npm install –g  node-inspector  如下所示:

看到如上信息,说明已经安装成功,如果安装不成功的话,也不要急,重新运行下,因为由于网络的原因引起的,所以会导致安装不成功,我们可以测试下是否安装成功,如下命令:

node-inspector 如果出现如下,说明安装成功了!

在使用node-inspector调试工具进行调试时候,首先在命令行窗口中输入如下所示的命令以调试脚本工具。

node  --debug-brk[=port]  filename

比如我现在的demo,我现在要进入我D盘文件夹node内,调试app.js,那么我需要进入node文件夹内,输入如上的命令来打开调试工具;如下命令:

node  --debug-brk[=port] app.js

如下:

接着我们需要打开第二个命令行窗口执行 node-inspector命令以启动调试工具,如下所示:

接着我们需要在浏览器地址栏中输入http://127.0.0.1:8080/debug?port=5858 网址及端口号(8080用于指定node-inspector的web端口,port=5858用于指定node-inspector的调试端口,我们也可以在node-inspector安装目录下的config.json文件中修改node-inspector的web端口和调试端口。)

打开浏览器如下图所示: