前言,本书书名有点夸张,任何一门编程语言都无法在短期内学会,必须坚持不懈进行学习,才能够学有所成。但这本书是挺适合入门的,通过这本书能够了解到node.js的一些基本知识,了解node.js能够实现什么场景下的业务。同时推荐另一本node.js入门书籍,名字就叫做《node.js入门》(The Node Beginner Book)。基本入门之后,那么必须要拜读下朴灵大神的著作《深入浅出Node.js》,这是进阶学习node.js的必读之书!
模块
require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象
exports对象是当前模块的导出对象,用于导出模块公有方法和属性。
通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。
模块路径解析规则
require函数支持斜杠(/)或盘符(C:)开头的绝对路径,也支持./开头的相对路径。
require函数支持第三种形式的路径,写法类似于foo/bar,并依次按照以下规则解析路径,直到找到模块位置:
/home/user/node_modules/foo/bar
/home/node_modules/foo/bar
/node_modules/foo/bar
包
JS模块的基本单位是单个JS文件,但复杂些的模块往往由多个子模块组成。为了便于管理和使用,我们可以把由多个子模块组成的大模块称做包,并把所有子模块放在同一个目录里。
在组成一个包的所有子模块中,需要有一个入口模块,入口模块的导出对象被作为包的导出对象。
当入口模块的文件名为index.js时,加载模块可以使用模块所在目录的路径代替模块文件路径:
以上两条语句等价。因此采用第一种写法,感觉整个目录被当做单个 模块使用,更有整体感。
若想自定义入口模块的文件名和存放位置,可以在包目录下包含一个 package.json 文件,在其中配置入口模块的路径:
这样便可以使用require(‘home/lib/user/cat’)加载模块。
小文件拷贝,直接使用fs的读写方法:readFileSync()和writeFileSync()
大文件拷贝,考虑到内存有限,因此应采取流的方法进行读写,读一点,写一点,使用fs的可读流和可写流:createReadStream()和createWriteStream()
Buffer与字符串有一个重要区别。字符串是只读的,并且对字符串的任何修改得到的都是一个新字符串,原字符串保持不变。至于Buffer,更像是可以做指针操作的C语言数组。
文件系统
fs模块提供的API基本上可以分为以下三类:
文件属性读写:常用的有fs.stat、fs.chmod、fs.chown等
文件内容读写:fs.readFile、fs.readdir、fs.writeFile、fs.close等
底层文件操作:fs.open、fs.read、fs.write、fs.close等
nodeJS最精华的异步IO模型在fs模块里有着充分的体现,例如通过回调函数传递结果:
此外,fs模块的所有异步API都有对应的同步版本,同步API除了方法名的末尾多了一个Sync之外,异常对象与执行结果的传递方式也有相应变化。
Path
nodeJS提供了path内置模块来简化路径相关操作,并提高代码可读性。
常用API:
path.normalize
将传入的路径转换为标准路径,具体讲,就是除了解析路径中的.和..外,还能去除多余的斜杠。
如果有程序需要使用路径作为某些数据的索引,但又允许用户随意输入路径时,就需要使用该方法保证路径的唯一性。
注意:标准化之后的路径里的斜杠在Windows系统下是\,而在Linux系统下是/。如果想保证任何系统下都使用/作为路径分隔符的话,需要用.replace(/\/g, ‘/‘)再替换一下标准路径。
path.join
将传入的多个路径拼接为标准路径。该方法可避免手工拼接路径字符串的繁琐,并且能在不同系统下正确使用相应的路径分隔符。
path.extname
当我们需要根据不同文件扩展名做不同操作时,该方法就显得很好用。
遍历目录
递归算法
遍历目录时一般使用递归算法,否则就难以编写出简洁的代码。递归算法通过不断缩小问题的规模来解决问题。
注意:使用递归算法编写的代码虽然简洁,但由于每递归一次就产生一次函数调用,在需要优先考虑性能时,需要把递归算法转换为循环算法,以减少函数调用次数。
遍历算法
目录是一个树状结构,在遍历时一般使用深度优先+先序遍历算法。
深度优先,意味着到达一个节点后,首先接着遍历子节点而不是邻居节点。先序遍历,意味着首次到达了某节点就算遍历完成,而不是最后一次返回某节点才算数。因此使用这种遍历方式时,下边这棵树的遍历顺序是A > B > D > E > C > F。
A
/ \
B C
/ \ \
D E F
同步遍历
异步遍历
如果读取目录或读取文件状态时使用的是异步API,目录遍历函数实现起来会有些复杂,但原理相同。
文件编码
BOM用于标记一个文本文件使用Unicode编码,其本身是一个Unicode字符(‘\uFEFF’),位于文本文件头部。
可通过BOM标记来判断文件的编码格式,但在读取文件时,如果不去掉BOM,则在某些情况下会有问题,如合并几个JS文件,若中间有BOM标记,则会导致报错。
BOM的移除:
网络操作
HTTP
http模块提供两种使用方式:
作为服务端使用时,创建一个HTTP服务器,监听HTTP客户端请求并返回响应;
作为客户端使用时,发起一个HTTP客户端请求,获取服务端响应。
HTTP请求本质上是一个数据流,由请求头和请求体组成。
HTTP响应本质上也是一个数据流,由响应头和响应体组成。
request对象可以当作一个只读数据流来访问请求数据;
response对象可以当作一个只写流来写入响应数据。
URL
parse()方法将一个URL字符串解析为URL对象;
format()方法允许将一个URL对象转换为URL字符串;
resolve()方法用于拼接URL。
Query String
querystring模块用于实现URL参数字符串与参数对象的互相转换。
querystring.parse()将URL参数字符串转换为URL参数对象;
querystring.stringify()将URL参数对象转换为URL参数字符串.
Zlib
zlib模块提供了数据压缩和解压功能。
zlib.gzip()压缩数据;
zlib.gunzip()解压数据;
Net
net模块可用于创建Socket服务器或Socket客户端。
进程管理
NodeJS可以创建子进程并与其协同工作,把多个程序组合在一起共同完成某项工作,并在其中充当胶水和调度器的作用。
process
process不是内置模块,是一个全局对象,可以感知和控制NodeJS自身进程的方方面面。
Child Process
child_process模块可以创建和控制子进程。
Cluster
cluster模块是对child_process模块的进一步封装,专用于解决单进程NodeJS web服务器无法充分利用多核CPU的问题。
应用场景:
获取命令行参数
通过process.argv获取命令行参数,但node执行程序路径和主模块文件路径固定占据了argv[0]和argv[1]两个位置,而第一个命令行参数从argv[2]开始。
如何退出程序
如何控制输入输出
标准输入流(stdin)、一个标准输出流(stdout)、一个标准错误流(stderr):process.stdin、process.stdout和process.stderr,第一个是只读流,后两个是只写流。
如何降权
在Linux系统下,我们知道需要使用root权限才能监听1024以下端口。但是一旦完成端口监听后,继续让程序运行在root权限下存在安全隐患,因此最好能把权限降下来。
Tips:
- 如果是通过sudo获取root权限的,运行程序的用户的UID和GID保存在环境变量SUDO_UID和SUDO_GID里边。如果是通过chmod +s方式获取root权限的,运行程序的用户的UID和GID可直接通过process.getuid和process.getgid方法获取。
- process.setuid和process.setgid方法只接受number类型的参数。
- 降权时必须先降GID再降UID,否则顺序反过来的话就没权限更改程序的GID了。
如何创建子进程
spawn(exec, args, options)方法,该方法支持三个参数。第一个参数是执行文件路径,可以是执行文件的相对或绝对路径,也可以是根据PATH环境变量能找到的执行文件名。第二个参数中,数组中的每个成员都按顺序对应一个命令行参数。第三个参数可选,用于配置子进程的执行环境与行为。
进程间如何通讯
在Linux系统下,进程之间可以通过信号互相通信。
kill方法本质上是用来给进程发送信号的,并不是关闭进程。进程收到信号后具体要做啥,完全取决于信号的种类和进程自身的代码。
如何守护子进程
守护进程一般用于监控工作进程的运行状态,在工作进程不正常退出时重启工程进程,保障工程进程不间断运行。
监听‘exit’事件,当退出状态码不等于0时,重新启用进程。
异步编程
NodeJS最大的卖点——事件机制和异步IO。而异步编程是NodeJS最大的特点,没有掌握异步编程就不能说是真正学会了NodeJS。
回调
异步编程的直接体现就是回调。
JS是单线程运行,这决定了JS在执行完一段代码之前无法执行包括回调函数在内的别的代码。也就是说,在平行线程完成工作了,通知JS主线程执行回调函数了,回调函数也要等到JS主线程有空闲时才能开始执行。
代码设计模式
1、函数返回值
同步方式下:
而在异步方式下,由于函数执行结果不是通过返回值,而是通过回调函数传递:
多个回调嵌套,代码结构会比较混乱,阅读性差。
2、遍历数组
3、异常处理
JS自身提供的异常捕获和处理机制——try..catch..,只能用于同步执行的代码。
4、域
NodeJS提供了domain模块,可以简化异步代码的异常处理。
域的概念:一个域就是一个JS运行环境,在一个运行环境中,如果一个异常没有被捕获,将作为一个全局异常被抛出。NodeJS通过process对象提供了捕获全局异常的方法。
域的示例:
通过create()方法创建子域对象,并通过run()方法进入需要在子域中运行的代码的入口点。
陷阱
无论是通过process对象的uncaughtException事件捕获到全局异常,还是通过子域对象的error事件捕获到了子域异常,在NodeJS官方文档中都强烈建议处理完异常后立即重启程序,而不是让程序继续运行。
总结
本书讲得相对浅显易懂,能够让新手快速入门node.js,基本了解node.js,但要想学好,并且更好地应用于实践中,还需要继续学习专研,再次推荐下《深入浅出Node.js》!