第三章 Node编程基础
一、模块
Node的模块系统避免了对全局作用域的污染,从而也就避免了命名冲突
Node.js以模块为单位,划分各个功能,而每一个模块是一个js文件,Node.js实现了在每一个模块中定义的全局变量和函数只能在这个模块内部使用,只有通过exports导出的对象才能传递到外部,这样就解决了私有作用域的问题。
Node.js作为JavaScript在服务端的实现,它遵循一套叫common.js的规范。每个模块都有一个全局对象module,同时module对象中有一个对象exports,使用exports对象实现模块导出(exports是module.exports对象的别名)。
require可以加载文件模块(.js、.code、.json)和nodejs核心模块,最终获取到的是module.exports对象。第一次加载的时候执行代码,第二次从缓存中获取module.exports对象。
1、创建模块
// 私有变量仅作用在num的逻辑内部,程序不能直接访问它,这里仅导出num函数
var base = 10;
exports.num = function(a){
return a*base;
}
// 只返回一个Currency构造函数,而不是包含两个函数的对象。
var Currency = function(num){
this.num = num;
}
Currency.prototype.add = function(a){
return a*10;
}
Currency.prototype.divide = function(a){
return a/10;
}
module.exports = Currency
Node觉得不能用任何其他对象、函数或变量给exports赋值,而用module.exports
可以对外提供单个变量、函数或者对象。如果你创建了一个既有exports又有module.exports
的模块,那它会返回module.exports,而exports会被忽略。
2、引入模块
var current = require('./module.js')
console.log(current.num(10))
4、用node_modules重用模块
Node中有一个独特的模块引入机制,可以不必知 道模块在文件系统中的具体位置。这个机制就是使用node_modules目录。
5、模块使用的注意事项
- 如果模块是目录,在模块目录中定义模块的文件必须被命名为index.js,除非你在这个 目录下一个叫package.json的文件里特别指明。要指定一个取代index.js的文件,package.json文件 里必须有一个用JavaScript对象表示法(JSON)数据定义的对象,其中有一个名为main的键,指 明模块目录内主文件的路径。
- Node能把模块作为对象缓存起来。如果程序中的两个文件引入了相 同的模块,第一个文件会把模块返回的数据存到程序的内存中,这样第二个文件就不用再去访问 和计算模块的源文件了。
二、异步编程技术
服务端异步编程:事件发生会触发响应逻辑。
在Node的世界里流行两种响应逻辑管理方式:回调和事件监听。
一个Node HTTP服务器实例就是一个事件发射器(回调),一个可以继承、能够添加事件发射及处理 能力的类(EventEmitter),Node的很多核心功能都继承自EventEmitter。
1、回调
定义:一个函数,它被当做参数传给异步函数,它描述了异步操作完成之后要做什么。
Node中的大多数内置模块在使用回调时都会带两个参数:第一个是用来放可能会发生的 错误的(er或err),第二个是放结果的(data)。
var http = require('http')
const fs = require('fs');
http.createServer(function (req, res) {
if (req.url == '/') {
fs.readFile('./titles.json',function(err,data){
if(err){
console.error(err)
res.end('Server Error')
}else{
var titles = JSON.parse(data.toString());
fs.readFile('./index.html',function(err,data){
if(err){
console.log(err)
res.end('Server Error')
}else{
var tmpl = data.toString();
var html = tmpl.replace('%',titles.join('</li><li>'));
res.writeHead(200,{'Content-Type':'text/html'})
res.end(html)
}
})
}
})
}
}).listen(3000, '127.0.0.1');
2、用事件发射器处理重复性事件
事件发射器会触发事件,并且在那些事件被触发时能处理它们。一些重要的Node API组件,比如HTTP服务器、TCP服务器和流,都被做成了事件发射器。
// socket是个事件发射器,可以用on方法添加监听器响应data事件。只要socket上有新数据过来,就会发出这些data事件。
var net = require('net');
var server = net.createServer(function(socket){
socket.on('data',function(data){
socket.write(data)
})
})
server.listen(8888)
3、异步逻辑的顺序化
三、Node.js的交互式运行环境(REPL)
REPL(Read Eval Print Loop:交互式解释器)的可交互运行环境,开发者可在该运行环境中输入任何表达式,然后按下回车键,REPL运行环境将显示该表达式的运行结果。
四、Node.js的全局作用域
全局的命名空间被称为global,它是一个对象。在global上挂载了很多属性、方法和类。
1、Node.js提供的全局函数和变量
- setTimeout 和 clearTimeout
- setInterval函数与clearInterval函数
- unref方法与ref方法
Node.js为这两个定时器对象各添加了unref和ref方法。unref方法用于取消setTimeout方法与setInterval方法中callback回调函数的执行;而ref作用正好相反,用于恢复调用callback回调函数。
const timer = setInterval(callback, ms, [arg], [...]) // setTimeout、setInterval都定时器返回一个定时器对象
timer.unref() // 取消callback函数的执行
timer.ref() // 恢复callback函数的执行
- 与require模块相关的全局方法和属性
//引入模块时使用require方法
const foo = require('../foo.js) // 接收一个字符串形式的路径名作为参数
传给require的参数是一个模糊的文件名,Node.js并不会直接报错,而是有一套查找规则,总结如下:
先加载文件,优先级为:.js > .json >.node没有文件加载文件夹:
*先看有没有package.json,有的话,加载package.json里main属性指定的文件。
*没有package.json,加载该目录下的index.js文件
- 与文件系统模块相关的全局变量
__filename用于获取当前模块文件的完整绝对路径文件名,__dirname用于获取当前模块文件所在目录的完整绝对路径。