node学习笔记
1、Node,可以让javascript运行在服务器端的平台。
是一个为实时Web(Real-time Web)应用开发而诞生的平台。充分考虑了实时响应,超大规模数据要求下架构。
2、摒弃了传统平台依靠多线程来实现高并发的设计思路,而采用了单线程、异步式I/O,事件驱动式的程序设计模型。
不仅带来了巨大的性能提升,还减少了多线程程序设计复杂性,进而提高了开发效率。
3、单线程事件驱动的异步I/O。
单线程事件驱动的异步I/O比传统的多线程阻塞式I/O到底好在哪里:简而言这,异步式I/O少了多线程的开销。对操作
系统来说,创建一个线程的代价是十分昂贵的,需要给它分配内存、列入调度,同时在线程切换的时候还要执行内存换页,
CPU的缓存被清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。
4、Node.js 的事件循环机制
Node.js 在什么时候会进入事件循环呢?答案是 Node.js 程序由事件循环开始,到事件循
环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,程序入口就是
事件循环第一个事件的回调函数。
5、模块(Module)和包(Package)是 Node.js 最重要的支柱。
6、事件:Node.js所有的异步I/O操作在完成时都会发送一个事件到事件队列。在开发者看来,事件由EventEmitter对象提供。
前面提到的fs.readFile和http.createServer的回调函数都是通过EventEmitter来实现的。
7、我们可以把要执行的语句作为node -e的参数直接执行。例如:node -g "cosole.log('Hello World');"
8、使用node的REPL模式。REPL(Read-eval-print loop),即输入-求值-输出循环。运行无参数node将会启动一
个JavaScrit的交互式shell:
例如:
进入repl模式后,会出现一个">"提示符提示你输入命令,输入后按回车,Node.js将会解析并执行命令。如果你执行了一个函数,那么REPL还会在下面显示这个函数的返回值。上面的undefined就是console.log的返回值。如果你输入了一个错误的指令,REPL 则会立即显示错误并输出调用栈。在任何时候,连续按两次 Ctrl + C 即可推出Node.js 的 REPL 模式。
node 提出的 REPL 在应用开发时会给人带来很大的便利,例如我们可以测试一个包能否正常使用,单独调用应用的某一个模块,执行简单的计算等。
9、事实上脚本文件的扩展名不一定是js,例如我们将脚本保存为script.txt,使用node script.txt命令同样可以运行。扩展名使用.js只是一个约定而已,遵循了JavaScript脚本一贯的命名习惯。
10、 在开发 Node.js 实现的 HTTP 应用时会发现,无论你修改了代码的哪一部份,都必须终止Node.js 再重新运行才会奏效。这是因为 Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入。Node.js的这种设计虽然有利于提高性能,却不利于开发调试,因为我们在开发过程中总是希望修改后立即看到效果,而不是每次都要终止进程并重启。supervisor 可以帮助你实现这个功能,它会监视你对代码的改动,并自动重启 Node.js。
使用方法很简单,首先使用 npm 安装 supervisor:
$ npm install -g supervisor
如果你使用的是 Linux 或 Mac,直接键入上面的命令很可能会有权限错误。原因是 npm
需要把 supervisor 安装到系统目录,需要管理员授权,可以使用 sudo npm install -g
supervisor 命令来安装。
使用 supervisor 命令启动 app.js:
$ supervisor app.js
当代码被改动时,运行的脚本会被终止,然后重新启动。supervisor 这个小工具可以解决开发中的调试问题。
11、回调函数
让我们看看在 Node.js 中如何用异步的方式读取一个文件,下面是一个例子:
//readfile.js
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
console.log('end.');
运行的结果如下:
end.
Contents of the file.
要想理解结果,我们必须先知道在 Node.js 中,异步式 I/O 是通过回调函数来实现的。fs.readFile 接收了三个参数,第一个是文件名,第二个是编码方式,第三个是一个函数,我们称这个函数为回调函数。JavaScript 支持匿名的函数定义方式, 譬如我们例子中回调函数的定义就是嵌套在fs.readFile 的参数表中的。这种定义方式在 JavaScript 程序中极为普遍,与下面这种定义方式实现的功能是一致的:
//readfilecallback.js
function readFileCallBack(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
}
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', readFileCallBack);
console.log('end.');
fs.readFile 调用时所做的工作只是将异步式 I/O 请求发送给了操作系统,然后立即
返回并执行后面的语句,执行完以后进入事件循环监听事件。当 fs 接收到 I/O 请求完成的
事件时,事件循环会主动调用回调函数以完成后续工作。因此我们会先看到 end.,再看到
file.txt 文件的内容。
12、模块和包
模块(Module)和包(Package)是Node.js最重要的支柱。
Node.js提供了require函数来调用其他模块,而且模块都是基于文件的,机制十分简单。
我们经常把Node.js的模块和包相提并论,因为模块和包是没有本质区别的,两个概念也时常混淆。如果要辨析,那么可以把包理解成是实现了某个功能模块的集合,用于发布和维护。
13、模块是Node.js应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个Node.js文件就是一个模块,这个文件可能是JavaScript代码、JSON或者编译过的C/C++扩展。
例如:var http=require(‘http’),其中http就是Node.js的一个核心模块,其内部是用C++实现的,外部用javaScript封装。我们通过require函数获取这个模块,然后才能使用其中的对象。
14、Node.js提供了exports和require两个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口,即所获取模块的exports对象。
让我们以一个例子来了解模块。创建一个 module.js 的文件,内容是:
//module.js
var name;
exports.setName = function(thyName) {
name = thyName;
};
exports.sayHello = function() {
console.log('Hello ' + name);
};
在同一目录下创建 getmodule.js,内容是:
//getmodule.js
var myModule = require('./module');
myModule.setName('BYVoid');
myModule.sayHello();
运行node getmodule.js,结果是:
Hello BYVoid
在以上示例中,module.js 通过 exports 对象把 setName 和 sayHello 作为模块的访
问接口,在 getmodule.js 中通过 require('./module') 加载这个模块,然后就可以直接访
问 module.js 中 exports 对象的成员函数了。
这种接口封装方式比许多语言要简洁得多,同时也不失优雅,未引入违反语义的特性,
符合传统的编程逻辑。在这个基础上,我们可以构建大型的应用程序,npm 提供的上万个模
块都是通过这种简单的方式搭建起来的。
15、单次加载
上面这个例子有点类似于创建一个对象,但实际上又和对象有本质区别,因为require不会重复加载模块,也就是说无论调用多少次require,获取的模块都是同一个。
看下列代码:
//loadmodule.js
var hello1 = require('./module');
hello1.setName('BYVoid');
var hello2 = require('./module');
hello2.setName('BYVoid 2');
hello1.sayHello();
运行后发现结果是Hello BYVoid 2,这是因为变量hello1,hello2指向的是同一对象,因为hello1.setName的结果被hello2.setName覆盖,最终输入结果是由后者决定的。