CommonJS规范
每一个js文件都是一个模块,js文件的名字就是模块的名字。每个模块内部使用的变量名和函数名互不冲突。
一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable
,还可以exports.variable=variable;
如果要输出一个键值对象{},可以利用exports
这个已存在的空对象{},并继续在上面添加新的键值;如果要输出一个函数或数组,必须直接对module.exports
对象赋值。
一个模块要引用其他模块暴露的变量,用var ref = require('module_name');
就拿到了引用模块的变量。在引用模块时要把相对路径写对,直接写模块名会依次在内置模块、全局模块和当前模块下查找。
全局对象
node.js
的全局对象global
相当于浏览器环境中的全局对象window
。
根据全局对象判断JavaScript的执行环境;
if(typeof(window)==='undefined'){
console.log('node.js');
}else{
console.log('browser');
}
node.js的process对象
process=global.process
,代表当前Node.js进程;
process.nextTick(function(){});
nextTick中的函数不会立刻执行,而是要等到下一次事件循环;
process.on('exit',function(){});
在程序即将退出时执行该回调函数。
内置模块
fs模块
文件系统模块,负责读写文件。fs模块同时提供了异步和同步的方法。
异步方法
因为JavaScript的单线程模型,执行IO操作时,JavaScript代码无需等待,而是传入回调函数后,继续执行后续JavaScript代码。
$.getJSON('http://example.com/ajax',function(data){
console.log('IO结果返回执行...');
});
console.log('不等待IO结果直接执行后续代码...');
同步方法
同步方法:同步的IO操作则需要等待函数返回。
var data=getJSONSync('http://example.com/ajax');
同步操作的好处是代码简单,缺点是程序将等待IO操作,在等待时间内,无法响应其它任何事件。而异步读取不用等待IO操作,但代码较麻烦。
异步与同步
由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。
服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。
异步读文件
'use strict';
var fs=require('fs');
fs.readFile('sample.txt','utf-8',function(err,data){
if(err){
console.log(err);
}else{
console.log(data);
}
});
console.log('start process');
sample.txt文件必须在当前目录,utf-8为文件的编码;
node.js标准的回调函数,第一个参数代表错误信息,第二个参数代表结果;
当读取二进制文件时,不传入文件编码时,回调函数的data参数将返回一个Buffer对象。在Node.js中,Buffer对象就是一个包含零个或任意个字节的数(注意和Array不同)。
Buffer对象与String之间的转换:
Buffer->String:var text=data.toString('utf-8');
String->Buffer:var buf=Buffer.from(text,'utf-8');
同步读文件
'use strict';
var fs=require('fs');
try{
var data=fs.readFileSync('sample.txt','utf-8');
console.log(data);
}catch(err){
console.log(err);
}
console.log('start');
异步写文件
'use strict'
var fs=require('fs');
var data='write data into file';
fs.writeFile('sample.txt',data,function(err){
if(err){
console.log(err);
}else{
console.log('done!');
}
});
writeFile()
的参数依次为文件名、数据和回调函数。如果传入的数据是String
,默认按UTF-8编码写入文本文件,如果传入的参数是Buffer
,则写入的是二进制文件。回调函数由于只关心成功与否,因此只需要一个err参数。
同步写文件
'use strict';
var fs=require('fs');
var data='write date into file';
fs.writeFileSync('output.txt',data);
获取文件大小、创建时间等信息
fs.stat()
返回一个Stat对象,能告诉我们文件或目录的详细信息。
//异步
'use strict';
var fs=require('fs');
fs.stat('sample.txt',function(err,stat){
if(err){
console.log(err);
}else{
console.log('isFile:'+stat.isFile());
console.log('isDirectory:'+stat.isDirectory());
if(stat.isFile()){
console.log('size:'+stat.size);
console.log('birth time:'+stat.birthtime);
console.log('modified time:'+stat.mtime);
}
}
});
//同步
'use strict';
var fs=require('fs');
try{
var stat=fs.statSync('sample.txt');
console.log('isFile:'+stat.isFile());
console.log('isDirectory:'+stat.isDirectory());
if(stat.isFile()){
console.log('size:'+stat.size);
console.log('birth time:'+stat.birthtime);
console.log('modified time:'+stat.mtime);
}
}catch(err){
console.log(err);
}
流读写文件
所有能读取数据的流都继承自stream.Readable
;所有能写入数据的流都继承自stream.Writable
;
//读操作
'use strict';
var fs = require('fs');
var rs = fs.createReadStream('sample.txt', 'utf-8')
rs.on('data', function (chunk) {//data事件表示流的数据已经可以读取。
console.log('DATA:');
console.log(chunk);
});
rs.on('end', function () {//end事件表示这个流已经到了末尾,没有数据可读。
console.log('END');
});
rs.on('error', function (err) {//error表示事件出错。
console.log('ERROR: ' + err);
});
//写操作
var ws=fs.createWriteStream('sample.txt','utf-8');
ws.write('write data into file with stream');
ws.end;
pipe
一个Readable
流和一个Writable
流串起来后,所有数据自动从Readable
流进入Writeable
流,这种操作叫pipe。
默认情况下,当Readable
流的数据读取完毕,end事件触发后,将自动关闭Writable
流。如果我们不希望自动关闭Writable
流,需要传入参数
readable.pipe(writable,{end:false});
Node.js实现HTTP服务器程序
处理TCP连接,解析HTTP都由http模块完成了,HTTP请求和HTTP响应都分别封装在http模块中的request
对象和response
对象中。
//简单的HTTP服务程序
'use strict';
var http=require('http');
var server=http.createServer(function(request,response){
console.log(request.method+":"+request.url);
response.writeHead(200,{'Content-Type':'text/html'});
response.end('<h1>hello world</h1>');
});
server.listen(8080);
console.log('Server is running at http://127.0.0.1:8080/');
Node.js实现文件服务器
解析URL需要用到Node.js提供的url模块,通过parse()将一个字符串解析为一个Url对象;
处理本地文件目录需要使用Node.js提供的path模块,使用path模块可以正确处理操作系统相关的文件路径;
//简单的文件服务器
'use strict';
var
fs=require('fs'),
url=require('url'),
path=require('path'),
http=require('http');
// 从命令行参数获取root目录,默认是当前目录
var root = path.resolve(process.argv[2] || '.');
console.log(process.argv[2]);
console.log('Static root dir: ' + root);
var server=http.createServer(function(request,response){
var pathname = url.parse(request.url).pathname;
var filepath = path.join(root, pathname);
fs.stat(filepath, function (err, stats) {
if (!err && stats.isFile()) {
console.log('200 ' + request.url);
response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
//response.write('<meta charset="utf-8"/> ');
fs.createReadStream(filepath).pipe(response);//由于response对象本身是一个Writable Stream,直接用pipe()方法就实现了自动读取文件内容并输出到HTTP响应
} else {
console.log('404 ' + request.url);
response.writeHead(404);
response.end('404 Not Found');
}
});
});
server.listen(8080);
console.log('Server is running at http://127.0.0.1:8080/');
node.js的加密模块crypto
crypto模块的目的是为了提供通用的加密和哈希算法,Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口。
MD5和SHA1
const crypto=require('crypto');
const hash=crypto.createHash('md5');//098f6bcd4621d373cade4e832627b4f6
//const hash=crypto.createHash('sha1');//a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
hash.update('test');//update()方法默认字符串编码为UTF-8,也可以传入Buffer
console.log(hash.digest('hex'));//以十六进制的方式输出