1)由于javascript主要是用在browser,而node.js是在server或者你的电脑上写,因此就不会有window / document这两个变量,取而代之的是global / process.
global和window同样,也有不少内置的function和variables,好比setTimeout() / setInterval() / console..., 不过也有不少不一样,这个后面会慢慢讲;javascript
node.js里很重要的一个概念就是modules
。除了一些core modules(好比events/http/fs/url...), 还能够用require()来引进一些自定义的modules。html
好比:java
//stuff.js var counter = function(arr){ return 'There are '+ arr.length+ ' arguments.' }; var adder = function(a,b){ return `the sum is ${a+b};` }; var pi = 3.1415926;
若是要在其余的file里用这个counter function应该怎么作呢?
在stuff.js后面加上:node
//第一种方法 //modules.exports是一个object对象 modules.exports.counter = counter; modules.exports.adder = adder; modules.exports.pi = pi;
上面的方法意思是到了,可是比较复杂,咱们能够用对象的方式来写modules.exportsjquery
//第二种方法 modules.exports = { counter: counter, adder: adder, pi: pi }
还有一种办法直接写在上面的functions里:web
modules.exports.counter = function(arr){ return 'There are '+ arr.length+ ' arguments.' }; modules.exports.adder = function(a,b){ return `the sum is ${a+b};` }; modules.exports.pi = 3.1415926;
而后要引用的时候使用require():json
//main.js var stuff = require('./stuff'); //能够不用写extension name,node本身会补上; //使用一下: console.log(stuff.counter(['hello', 'world']); console.log(stuff.adder(5, stuff.pi);
在node.js里,有一个core module是event,event里只有一个对象,就是EventEmitter, 主要用于封装事件触发和事件监听器的功能(使用javascript里的观察者模式)。
若是用过jquery的话,对这个格式必定不陌生:api
button.on('click', function(){alert('clicked!');}
其实node里的EventEmitter和jQuery的这个事件监听函数的写法也很相似:app
//首先引入EventEmitter对象 //EventEmitter是一个constructor,可使用原型继承 var eventEmitter = require('event').EventEmitter; var event = new eventEmitter(); //绑定事件 event.on('data', function(str){ console.log('something here: '+ str.toString()); }; //触发事件 //前一个param是event的名字,后一个是callback函数须要的变量 event.emit('data', 'Tom wants sth to eat.');
前面提到EventEmitter实际上是一个constructor,能够经过prototype继承;
咱们如今用util这个module来实现node里的inherits:异步
var event = require('event'); var util = require('util'); //创造一个person的constructor,一会来继承eventEmitter; var Person = function(name){ this.name = name; }; //util.inherits用来继承,注意两个params的顺序; util.inherits(Person, event.EventEmitter); //如今咱们来new两个person实例出来: var james = new Person('james'); var ellen = new Person('ellen'); //放在一个叫people的array里: var people = [james, ellen]; //如今用eventEmitter里的on方法绑定事件; //由于Person实例已经继承,因此可使用 people.forEach(function(person){ person.on('speak', function(str){ console.log(`${person} says ${str}`); }); }); //触发事件 james.emit('speak', 'good morning!'); ellen.emit('speak', 'good evening!');
fs类主要是用来处理文件的,有很是多的方法,但在这个小教程里只涉及read和write两项。
首先咱们来区分一下“同步”和“异步”的概念:
“同步”—— blocking; 解析器会一步一步地解析你的code,若是前面那行没操做完就不会操做后面那行;
“异步”——non-blocking;解析器解析完这行代码以前,不会妨碍到后面代码的进行,在异步处理完后经过callback函数对结果进行处理,所以,异步的性能比同步会不少。
//node中同步read和write的写法: var fs = require('fs'); //同步是'readFileSync' var readMe = fs.readFileSync('input.txt', 'utf8'); fs.write('output.txt', readMe); //异步: //err是出现的错误,data是readFile后读取到的数据 fs.readFile('input.txt', 'utf8', function(err, data){ if(err){ console.error(err); }else{ fs.writeFile('output.txt', data); }
能够看到,运用fs的callback函数,能够直接在readFile里写writeFile, 不须要从新定义变量。
再写一个fs中的创建目录和移除目录的方法,一样有同步和异步两种方式:
var fs = require('fs'); //make a directory sync fs.mkdirSync('stuff'); //delete a directory sync fs.rmdirSync('stuff'); //make a directory async fs.mkdir('stuff', function(){ fs.readFile('readMe.txt', 'utf8', function(err,data){ fs.writeFile('./stuff/writeMe/txt', data); }); }); //remove a directory async //you must first remove all files in this directory, then you can delete this folder fs.unlink('./stuff/writeMe.txt', function(){ fs.rmdir('stuff'); })
在解释stream以前,首先咱们来想想为何要有readable stream和writable stream呢? 明明fs类里就有readFile和writeFile的方法啊,何须还要再添加stream来找麻烦呢?
要知道这两个之间的不一样,让咱们来理一下"buffer"的概念。
buffer的介绍通常是这样:
在Node.js中,Buffer类是随Node内核一块儿发布的核心库。Buffer库为Node.js带来了一种存储原始数据的方法,可让Nodejs处理二进制数据,每当须要在Nodejs中处理I/O操做中移动的数据时,就有可能使用Buffer库。
简单来讲,buffer就是一个处理二进制数据的缓冲模块,那和stream又有什么关系呢?
能够看下下面的图:
这张图里,能够把很大的一块data进行肢解,而后储存到buffer中,造成一个个的相对比较小的data chunk;
在这张图里,chunk one by one向前传递就成了stream。
因此回到咱们最初的问题,用readable stream和fs里的readFile有什么不一样?
readFile是等一个file所有读完以后才fire callback函数,进行下一步动做;而stream是经过把file里的数据分红不少不少个小的chunk, 每次callback函数感知到data chunk的时候就会触发。这样传递数据会更快。
var fs = require('fs'); var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8'); //这里的‘utf8'若是不加的话会解码成二进制(由于buffer) myReadStream.on('data', function(chunk){ console.log('new chunk received'); console.log(chunk); }); //stream也继承eventEmitter //每次感知到data chunk就会触发,不须要等到整个file都读完
//和readable stream相似 //会创造出来一个output.txt在目录下 var fs = require('fs'); var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8'); var myWriteStream = fs.createWriteStream(__dirname + '/output.txt'); myReadStream.on('data', function(chunk){ console.log('new chunk received'); myWriteStream.write(chunk); });
由于把readable stream转成writable stream在node.js里很是常见,因此有一个更elegant的办法就是用pipe:
var fs = require('fs'); var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8'); var myWriteStream = fs.createWriteStream(__dirname + '/output.txt'); //pipe只能从可读流出发,不能从可写流出发 myReadStream.pipe(myWriteStream);
若是加上web server就能够这么写:
var fs = require('fs'); var http = require('http'); var server = http.createServer(function(req, res){ console.log('request was made: '+ req.url); res.writeHead(200, {'Content-Type': 'text/plain'}); var myReadStream = fs.createReadStream(__dirname + '/input.txt', 'utf8'); myReadStream.pipe(res); //response obj is writable }); server.listen(3000); console.log('listen to port 3000');
若是要传递的数据是html格式的话:
var fs = require('fs'); var http = require('http'); var server = http.createServer(function(req, res){ console.log('request was made: '+ req.url); //html格式也能够用stream传递,用pipe把可读流转成可写流 res.writeHead(200, {'Content-Type': 'text/html'}); var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8'); myReadStream.pipe(res); //response obj is writable }); server.listen(3000); console.log('listen to port 3000');
若是传递的数据是json的话:
//不使用stream,直接在res.end()里面传递 //可是要注意的是,end()里面只接受string,不能把object直接放进去 var fs = require('fs'); var http = require('http'); var server = http.createServer(function(req, res){ console.log('request was made: '+ req.url); res.writeHead(200, {'Content-Type': 'application/json'}); var myobj = { name: 'Ryu', job: 'ninja', age: 29 }; res.end(JSON.stringify(myobj)); }); server.listen(3001); console.log('listen to port 3001');
介绍一个很简单的router的使用办法,只要用if判断req.url,而后用stream写入便可:
var http = require('http'); var fs = require('fs'); var server = http.createServer(function(req,res){ console.log('request was made: '+ req.url); if(req.url === '/home' || req.url === '/'){ res.writeHead(200, {'Content-Type' : 'text/html'}); //用fs的pipe把readable stream改为writable stream fs.createReadStream(__dirname +'/index.html').pipe(res); }else if(req.url === '/sample'){ res.writeHead(200, {'Content-Type': 'text/html'}); fs.createReadStream(__dirname + '/sample.html').pipe(res); }else if(req.url === '/api/ninjas'){ var ninjas = [{name: 'ryu', age: 29}, {name: 'yoshi',age:32 }]; res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(ninjas)); } //其实还能够再写一个404 page }); server.listen(3000); console.log('now listening to port 3000');