JavaScript入门教程:
深入学习推荐阅读Mozilla JavaScript:
Node是一个可以让JavaScript运行在服务器端或者说运行在浏览器之外的平台。Node.js中所谓的JavaScript只是Core JavaScript,Node.js不运行在浏览器中,所以不存在JavaScript的浏览器兼容性问题,可以放心地使用JavaScript语言的所有特性。Node.js的JavaScript引擎是V8。Node.js是一个实时Web(Real-time Web)应用开发平台,充分考虑了在实时响应、超大规模数据要求下架构的可扩展性。它不同于传统平台依靠多线程来实现高并发的设计思路,而采用了单线程、异步I/O、事件驱动的程序设计模型。这些特性不仅带来了巨大的性能提升,还减少了多线程程序设计的复杂性,提高了开发效率。
Node.js可以作为服务器向用户提供服务,与PHP、Python、Ruby on Rails相比,它跳过了Apache、Nginx等HTTP服务器,直接面向前端开发。
使用Nodejs可以轻松开发:
具有复杂逻辑的网站;
给予社交网络的大规模Web应用;
Web Socket服务器;
TCP/UDP套接字应用程序;
命令行工具;
交互式终端程序;
带有图形用户界面的本地应用程序;
单元测试工具;
客户端JavaScript编译器;
Node.js内建HTTP服务器支持,这个服务器不仅可以用来调试代码,而且本身就可以部署到产品环境,可以轻易地实现一个网站和服务器的组合。
$./configure$make$sudo make install
$ node --help
Usage: node [options] [ -e script | script.js ] [arguments]
node debug script.js [arguments]
Options:
-v, --version print node's version
-e, --eval script evaluate script
-p, --print evaluate script and print result
-i, --interactive always enter the REPL even if stdin
does not appear to be a terminal
--no-deprecation silence deprecation warnings
--trace-deprecation show stack traces on deprecations
--v8-options print v8 command line options
--max-stack-size=val set max v8 stack size (bytes)
Environment variables:
NODE_PATH ':'-separated list of directories
prefixed to the module search path.
NODE_MODULE_CONTEXTS Set to 1 to load modules in their own
global contexts.
NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL
Documentation can be found at
使用
$node script.js
$node -e "console.log('Hello world');" //要执行的语句作为node -e 的参数直接执行
node的REPL(Read-eval-print-loop)模式:输入-求值-输出-循环
REPL在应用开发时可以测试一个包能否正常使用,单独调用应用的某一个模块,执行简单计算等,带来很大便利。
$node //启动一个交互式shell
>
建立HTTP服务器 -- http 模块
建立一个名为example.js的文件,内容为:
1 var http = require('http') 2 http.createServer(function(req, res) { 3 res.writeHead(200, {'Content-Type': 'Text/html'}); 4 res.write('Node.js
'); 5 res.end('Hello World!
'); 6 }).listen(8124, "127.0.0.1"); 7 console.log('Server running at http://127.0.0.1:8124');
运行$node example.js,打开浏览器访问
运行后不会立即退出,而是一直等待,直到按下Ctrl+C。listen函数创建了事件监听器,使得Node.js不会退出事件循环。
使用supervisor--监视代码代码的改动,并自动重启Node.js,解决了开发中调试的问题。
$sudo npm install -g supervisor
异步式I/O与事件驱动--Node.js最大特点,其异步机制是基于事件的,通过回调函数实现。
Nodejs使用单线程模型,对于所有I/O采用异步式的请求方式,避免频繁的上下文切换。Nodejs在执行过程中维护一个事件队列,程序在执行时进入事件循环等待下一个事件的到来,每个异步式I/O请求完成后会被推送到事件队列,等待程序进行处理。
网络通信、数据库查询-->事件循环<--客户端请求、磁盘I/O
单线程事件驱动的异步式I/O(非阻塞模式):减少了多线程的开销,但是依然保证CPU高利用率和高吞吐量。
异步读取文件readfile.js:
1 var fs = require('fs'); 2 3 function callback(err, data) { 4 if(err) { 5 console.log(err); 6 } else { 7 console.log(data); 8 } 9 } 10 fs.readFile('file.txt', 'utf-8', callback); 11 12 console.log('end.\n');
同步读取readfilesync.js:
1 var fs = require('fs'); 2 var data = fs.readFileSync('file.txt', 'utf-8'); 3 console.log(data); 4 console.log('end.\n');
fs.readFile调用时只是将异步式I/O请求发给操作系统,然后立即返回执行后面的语句,执行完之后进入事件循环监听事件。当fs接收到I/O请求完成的事件时,事件循环主动调用回调函数完成后续工作。
提示:Node.js的异步编程接口习惯是以函数的最后一个参数作为回调函数,通常一个函数只有一个回调函数。回调函数的实际参数中第一个是err,其余参数是其他返回的内容。如果没有错误发生,err值会是 null 或 undefined。如果有错误发生,err通常是Error对象的实例。
与同步I/O函数不同,Node.js中异步函数大多没有返回值。
Node.js中,并不是所有的API都提供同步和异步版本。Node.js不鼓励使用同步I/O.
事件 -- events 模块
Node.js程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数。Nodejs的事件循环对开发者不可见,由libev库实现,并被EventEmitter封装。
事件由EventEmitter对象提供,event对象注册事件some_event监听器,通过setTimeout在1000ms之后向event对象发送事件some_event,此时调用some_event监听器。
event.js
1 var EventEmitter = require('events').EventEmitter; 2 var event = new EventEmitter(); 3 event.on('some_event', function() { 4 console.log('Some event occured.'); 5 }); 6 setTimeout(function() { 7 event.emit('some_event'); 8 }, 1000);结果:
$ node event.js
Some event occured.
模块和包
Node.js提供require函数调用其它模块,而且模块都是基于文件的,文件和模块是一一对应的。一个Node.js文件就是一个模块,这个文件可能是JavaScript代码、JSON或者编译过的C/C++扩展。
例如http是nodejs的一个核心模块,其内部用C++实现,外部用JavaScript封装。通过require函数获取这个模块,然后使用其对象。
1.创建模块:
创建一个文件,exports是模块公开接口,require用于从外部获取一个模块接口即获取模块的exports对象。
module.js
//module.jsvar name;exports.setName = function(thyName) { name = thyName;};exports.sayHello = function() { console.log('Hello ' + name);};
getmodule.js
//getmodule.jsvar myModule = require('./module');myModule.setName('BYVoid');myModule.sayHello();
运行结果:
$ node getmodule.js
Hello BYVoid
2.单次加载
require不会重复加载模块。
loadmodule.js
//loadmodule.jsvar hello1 = require('./module');hello1.setName('No 1');var hello2 = require('./module');hello2.setName('No 2');hello1.sayHello();hello2.sayHello();运行结果:
$ node loadmodule.js
Hello No 2
Hello No 2
变量hello1和hello2指向同一个实例,结果被覆盖。
3.覆盖exports
如果将一个对象Hello封装到模块中,如果使用 exports.Hello = Hello,则在其他文件中需要通过
require('./modulefile').Hello获取Hello对象。
简化方法是使用 module.exports = Hello 代替
hello.js
//hello.jsfunction Hello() { var name; this.setName = function(thyName) { name = thyName; }; this.sayHello = function() { console.log('Hello ' + name); };};module.exports = Hello;getmodule.js
//getmodule.jsvar HelloObj = require('./hello');myModule = new HelloObj();myModule.setName('BYE HELLO');myModule.sayHello();运行结果:
$ node getmodule.js
Hello BYE HELLO
exports本身仅仅是一个普通的空对象,即{},它专门用来声明接口,本质上是通过它为模块闭包的内部建立了一个有限的访问接口。因为没有特殊的地方所以可以用其他东西来代替,如 exports.Hello = Hello 中的Hello对象;但是使用时需要通过require('./modulefile').Hello获取Hello对象。使用module.exports = Hello ,在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的exports。不可以通过对exports直接赋值代替对module.exports赋值。exports实际只是一个和module.exports指向同一个对象的变量,它本身会在模块执行结束后释放,但module不会,因此只能通过指定module.exports来改变访问接口。
创建包
Node.js包是一个目录,其中包含一个JSON格式的包说明文件package.json。严格符合CommonJS规范的包应该具备以下特征:
package.json必须在包的顶层目录下;
二进制文件应该在bin目录下;
JavaScript代码应该在lib目录下;
文档应该在doc目录下;
单元测试应该在test目录下。
作为文件夹的模块--最简单的包
./somepackage/index.js
//somepackage/index.jsexports.hello = function() { console.log('Hello.');};getpackage.js
//getpackage.jsvar somePackage = require('./somepackage');somePackage.hello();运行结果:
$ node getpackage.js
Hello.
2.
添加文件package.json
$ cat ./somepackage/package.json
{
"main" : "./lib/interface.js"
}
新建lib文件夹,将index.js文件重命名为interface.js放入lib文件夹下
$ cat ./somepackage/lib/interface.js
//somepackage/lib/index.jsexports.hello = function() {console.log('Hello.');};再次运行:
$ node getpackage.js
Hello.
Node.js在调用某个包时,会首先检查包中package.json文件的main字段,将其作为包的借口模块,如果package.json文件或main字段不存在,则尝试寻找index.js或index.node作为包的接口。
Node.js包管理器--npm
$npm [install/i] [-g] [package_name]
注意区别本地安装和全局安装,本地模式安装的包可通过require使用,但是不会注册PATH,无法通过命令全局使用;全局模式相反,注册PATH可以直接通过命令使用,但是无法通过require使用。因此,如果要把某个包作为工程运行时的一部分,通过本地模式获取,如果要在命令下使用,则使用全局模式安装。
创建全局链接 npm link
使用 npm link 命令可以使全局安装的包通过require使用。
例如 npm install -g express 全局安装了express,这时在工程目录下运行命令:
$ npm link express
/home/nick/nodejs/somepackage/node_modules/express -> /usr/local/lib/node_modules/express
则在node_modules子目录中发现一个指向安装到全局包的符号链接
express -> /usr/local/lib/node_modules/express/ 这样全局包就可以当作本地包使用了。
在包目录(package.json所在目录)运行 npm link 命令,可以将本地包链接到全局,利用这种方法可以非常方便地在不同的工程间测试要开发的包。
包的发布--npm init
根据交互式问答产生一个符合标准的package.json,创建一个index.js作为包的接口,一个简单的包就制作完成了。
npm adduser -- 创建帐号,用于今后维护自己的包,完成之后使用 npm whoami 检测是否已经取得帐号。然后在package.json所在目录下运行 npm publish 完成发布,可以在 search 自己刚刚发布的包,通过npm install package_name就可以安装自己发布的包了。
如果包有更新,只需修改package.json文件的version字段,重新 npm publish;如果对包不满意,可以使用 npm unpublish 命令取消发布。
调试
1.命令行调试
Usage: node debug script.js
Commands: run (r), cont (c), next (n), step (s), out (o), backtrace (bt), setBreakpoint (sb), clearBreakpoint (cb), watch, unwatch, watchers, repl, restart, kill, list, scripts, breakOnException, breakpoints, version
debug.js
//debug.jsvar a = 1;var b = 'world';var c = function(x) { console.log('hello ' + x + a);};c(b);$ node debug debug.js
< debugger listening on port 5858
connecting... ok
break in debug.js:2
1 //debug.js
2 var a = 1;
3 var b = 'world';
4
debug> n
break in debug.js:3
1 //debug.js
2 var a = 1;
3 var b = 'world';
4
5 var c = function(x) {
debug> sb('debug.js', 4)
1 //debug.js
2 var a = 1;
3 var b = 'world';
* 4
5 var c = function(x) {
6 console.log('hello ' + x + a);
7 };
8
debug> c
break in debug.js:5
3 var b = 'world';
* 4
5 var c = function(x) {
6 console.log('hello ' + x + a);
7 };
debug> repl
Press Ctrl + C to leave debug repl
> a
1
> b
'world'
2.远程调试--基于V8
node --debug[=port] script.js
node --debug-brk[=port] script.js
node --debug 命令选项启动调试服务器,默认端口55858,指定使用 --debug=1234。使用 -- debug 选项运行脚本,脚步正常执行,不会暂停,执行过程中调试客户端可以连接到调试服务器。如果要求脚步暂停执行等待客户端连接,则使用 --debug-brk 选项,调试服务器在启动之后立刻暂停执行脚步,等待调试客户端连接。
一个终端:
$ node --debug-brk debug.js
debugger listening on port 5858
另一个终端:
$ node debug 127.0.0.1:5858
connecting... ok
debug> n
break in nodejs/debug.js:3
1 //debug.js
2 var a = 1;
3 var b = 'world';
4
5 var c = function(x) {
debug>
3.使用node-inspector
$ sudo npm install -g node-inspector //安装
$ node --debug-brk debug.js //连接要调试的脚本
$ node-inspector //启动node-inspector
Node Inspector v0.7.4
Visit http://127.0.0.1:8080/debug?port=5858 to start debugging.
注:node-inspector(使用了WebKit Web Inspector)只能在Chrome、Safari等WebKit内核的浏览器中使用。
4.在Eclipse中安装V8调试工具调试Node.js代码
启动Eclipse之后,Help-》Install New Software...,Add Repository,添加 Chrome Developer Tools, 用于调试Javascript代码。Debug -> Debug Configurations...,找到Standalone V8 VM, New产生一个新配置,填好Name,Host,Port;然后使用命令 node --debug-brk=port script.js命令启动要调试脚本的调试服务器。