官网:https://nodejs.org/en/
Node 不是一种新的语言
Node 是一个可以用来解析和执行 JavaScript 语言的一个环境或者说是一个平台
Node 环境或者说平台 给 JavaScript 语言,提高了 一些 例如文件操作、网络操作等接口API
Node 的特点:事件驱动、 异步IO模型 、 跨平台、单线程
官方解释:
Node.js 是一个构建与 Chrome 的 V8 JavaScript 引擎之上的一个 JavaScript 运行时环境
Node 可以用来解析和执行 JavaScript 代码
Node 无法解析 DOM 和 BOM 等对象
Node 只关心数据部分,例如 读写文件,提供 Web 服务
Node.js 使用 事件驱动和非阻塞IO模型,使它非常的轻量和高效
event driven:事件驱动
non blocking I/O model:非阻塞IO模型
Node.js 的包管理系统 npm 是世界上最大的开源库生态系统
Node 的作者:Ryan Dahl
2004年还在纽约读数学系博士
2006年退学(世界那么大,我想去看看),生活所迫,选择了码农
接项目、工作、旅行,两年之后,成为了高性能Web服务器的专家
2009年5月,Ryan Dahl 在 github 上发布了最初的 Node 版本
2010年底,Ryan Dahl 加入 Joyent 公司全职负责 Node 的发展
2011年7月,Node 在微软的支持下发布了 Windows 版
2012年1月底,Ryan Dahl 将掌门人身份转交给了 Isaac Z.Schlueter,自己转向一些研究项目
2014年12月,多为重量级Node开发者不满 Joyent 对 Node 的管理,自立门户创建了 `io.js`
2015年9月,Node 与 io.js 合并,Node 的版本从 0.12.7 直接升级到了 4.0.0
合并后的 io.js 和 Node 在 Joyent 公司的维护下并行了两个版本:
一个是4.x.x 还是原来的 Node,这个版本是稳定版
一个是5.x.x,目前已经更新到了 6.4.0,其实就是 io,最新特性版,不建议生产环境使用
Node 打破了过去 JavaScript 只能在浏览器中运行的局面
前后端编程环境统一,大大降低了前后端语言切换的代价
全栈开发工程师
后端开发工程师
前端开发工程师
移动端开发工程师
前端 后端
高性能的网站服务器
实时多人游戏后台服务器
简单易用的命名行应用程序
gulp、less
高大上的桌面应用程序
使用 Web 技术 作为解决方案
底层的物联网开发
移动开发
Ionic 安卓 IOS
使用 Web 技术作为解决方案
nodeJS遵守的规范
一个单独的文件就是一个模块
每一个模块都是一个单独的作用域
每个文件对外接口是 `module.exports` 对象
还有一个别名 `exports` 指向了 `module.exports` 接口对象
`require` 方法用于加载模块,得到模块的 `module.exports` 接口对象
Node 根据 `CommonJS` 规范实现了自己的一套模块系统。
在 Node 中,模块分为两类,一类是 Node 提供的模块,称为核心模块。另一类是用户编写的模块,称为文件模块。
核心模块在 Node 源代码的编译过程中,编译进了二进制执行文件,
在使用的时候,`require` 参数中直接写核心模块名即可(相当于Java jdk提供的基础类,比如Date,String等,可以直接使用提供的方法)。
例如:`var fs = require('fs')` ,表示加载用于文件操作的 `fs` 核心模块(类似于Java中的引包)。
在Node中,文件模块则是在运行时动态加载,完全以同步的方式加载,然后执行。
浏览器中的模块必须通过网络去加载,所以无论是 SeaJS 还是 RequireJS 都是预先加载所有的模块。
在 Node 中,因为 JavaScript 模块运行在服务器端,加载模块代码的时候可以直接通过本地就可以加载了,
所以,Node 中加载模块都是同步的方式进行加载的,加载到哪里就执行到哪里。
重点: var fs = require('fs')加载模块,相当于Java中的import引依赖
优先从缓存加载
路径分析和文件定位
模块标识符分析
核心模块
路径形式的文件模块
自定义模块
无论是核心模块还是文件模块都是通过 requrie 方法进行加载
1、 优先从缓存中加载(node对于已经加载过的模块会缓存起来,下次如果继续使用,直接从缓存中拿),无论是核心模块还是文件模块
2.1 、如果模块标识是核心模块,那么 node 去读取自己的核心模块
2.2 、如果模块标识是以 . 开头的模块,那么这个模块就是一个相对路径文件模块。
2.3、 既不是路径形式的文件模块,也不是核心模块,是一个包
3.1 、node 先在当前目录下找有没有一个叫做 node_modules 的目录, 如果找到,则找里面有没有对应的包名
3.2、 如果找到该包名对应的目录,找里面的 package.json 文件
3.3 、如果找到 package.json 文件,那么找里面的 main 属性,如果找到 main 属性,直接加载该属性对应的 模块文件
3.4 、如果没有找到 package.json 文件 或者 package.json 文件中没有 main 属性 或者 main 指定的入口文件模块不存在,查找失败
4、 node 自动进入上一级目录 重复上面的步骤
node 中的包模块查找机制非常类似于 JavaScript 中的 作用域链 和 原型链
5、 如果直到 当前文件模块所属的盘符根路径还找不到,最后报错。
总结: 先去缓存中找,缓存没有如果是核心模块就去读自己的核心模块,如果不是按照路径找,路径没有不是文件的话就是包,就去找这个包
文件定位
文件扩展名分析:按照 `.js` `.json` `.node` 的次序补足扩展名
目录分析和包
模块编译
`.js` 文件,通过 fs 模块同步读取文件后编译执行
`.node` 文件
`.json` 文件,通过 fs 模块同步读取文件后使用 `JSON.parse()` 解析返回结果
其它扩展名,被当作 `.js` 文件载入
兼容模块规范
在代码中加入这些判断
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
// 向 Window 暴露接口对象
root._ = _;
}
兼容AMD模块规范
// 判断在全局是否有 define 这个方法,同步 define 有一个属性叫做 amd
// 两个判定条件都成功,就意味着当前浏览器环境引入了 require.js 文件
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
兼容CMD规范
if (typeof define === 'function' && define.cmd) {
define(function (require, exports, module) {
module.exports = _;
});
}
node对象
REPL (Read Eval Print Loop:交互式解释器) 运行环境 : 可以执行一些js代码
ECMAScript
nodeJS对象
全局对象
global : 相当于浏览器中的window,不建议使用容易造成冲突
Console : 日志打印和浏览器中的console功能相同,(火狐浏览器给JS加的,用于调试,不在规范中)
process : 进程对象, 获取系统相关的信息
Buffer(class): 全局构造函数,用来操作二进制数据
全局函数
setInterval 和 clearInterval
setTimeout 和 clearTimeout
setImmediate 和 clearImmediate
模块内成员
变量:
__dirnam :获取当前文件绝对路径(不包括文件名)
__filename: 获取当前文件的绝对路径(包括文件名)
方法:
require ('路径') : 获取模块的方法
exports:
exports= module.exports
exports是引用 module.exports的值,而模块导出的时候,真正导出的执行是 module.exports,而不是exports
module.exports和exports的区别就是var a={}; var b=a; 中a和的区别。exports指向的只是一个引用。改变exports的指向后所添加的exports.***都是无效的。因为require返回的只是module.exports. 所以不能在使用了exports.***之后,改变module.exports的指向。因为exports.***添加的属性和方法并不存在与module.exports所指向的新对象中。
个人建议,可以全部使用module.exports来应对所有的情况,并尽量减少犯错的机会。
module
Node内部提供一个Module构造函数,所有模块都是Module的实例 • 每个模块内部,都有一个module对象,代表当前模块。
– module.id 带有绝对路径的模块文件名
– module.filename 模块的文件名,带有绝对路径
– module.loaded 表示模块是否已经完成加载
– module.parent 返回一个对象,表示调用该模块的模块。
– module.children 返回一个数组,表示该模块要用到的其 他模块。
– module.exports 模块对外输出的值(暴露模块方法和属性)
module.exports属性表示当前模块对外输出的接口, 其它文件加载该模块,实际上就是读取 module.exports属性 • 点儿导出单个函数、对象或者值的时候非常有用
为了方便,Node为每个模块提供一个exports变量, 指向module.exports。 相当于在每个模块头部,有这样一行命令: var exports = module.exports;
结果就是: – 在对外输出模块接口时,可以向exports对象添加方法
及可以直接使用exports代替module.exports
注意:不能直接给exports赋值,因为这样等于切断了 exports和module.exports的联系
require()加载模块
• 在Node.js中,require命令用于加载模块文件
• 基本功能: 1、读取并执行一个JavaScript文件 2、 然后返回该模块的exports对象 3、 如果没有发现指定模块,会报错
require模块加载规则:
• 参数字符串以 “/”开头 (绝对路径)
• 参数字符换以“./”开头 (相对路径)
• 参数字符串没有路径,表示加载核心模块, 或者一个位于各级node_modules目录已安装的模块
• 参数字符串可以省略后缀名 : .js、.json、.node
– .js会当做JavaScript脚本文件解析
– .json会以JSON格式解析
– .node会以编译后的二进制文件解析
注意: node项目目录结构没有要求, 相对路径是相对于node命令所在的目录
核心模块与文件模块
• 核心模块 :require(‘核心模块名’)
• 文件模块 : require(‘路径+模块名’)路径包括相对路径与绝对路径 ,绝对路径的加载速度快
总结
– 加载模块时将运行模块文件中的每一行代码
– 相同模块多次引用不会引起模块内代码多次执行
核心模块:
http : 提供http服务器功能
url :解析url
fs :与文件系统交互
querystring :解析url查询字符串
util :提供一系列实用小工具
path : 处理文件路径
核心模块的源码都在node的lib子目录中。为了提高运行效率,她们安装的时候都会被编译成二进制文件
模块的加载机制:
如果require绝对路径的文件,查找时不会去遍历每一个node_modules目录,其速度最快。 其余流程如下:
1. 从module path数组中取出第一个目录作为查找基准。
2. 直接从目录中查找该文件,如果存在,则结束查找。如果不存在,则进行下一条查找。
3. 尝试添加.js、.json、.node后缀后查找,如果存在文件,则结束查找。如果不存在, 则进行下一条。
4. 尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得 main参数指定的文件。
5. 尝试查找该文件,如果存在,则结束查找。如果不存在,则进行第3条查找。
6. 如果继续失败,则取出module path数组中的下一个目录作为基准查找,循环第1至5个 步骤。
7. 如果继续失败,循环第1至6个步骤,直到module path中的最后一个值。
8. 如果仍然失败,则抛出异常。
总结
所有代码都运行在模块作用域,不会污染全局作用域
模块可以多次加载,但是只会在第一次加载的时候运行 一次,然后运行结果就被缓存了,以后再加载,就直接 读取缓存结果
模块的加载顺序,按照代码的出现的顺序是同步加载的
require是同步加载模块的
`path` 是一个核心模块,在使用的时候必须加载。
basename(p[,ext]) 注意第二个参数用法 获取文件名
dirname(p) 获取文件目录
extname(p) 获取文件扩展名
isAbsolute(path) 判断是否是绝对路径
join([path1][,path2][,...]) 拼接路径字符串
sep 获取操作系统的文件路径分隔符
`fs` 是一个核心模块,在使用的时候必须加载。
同步和异步文件系统调用
异步:
fs.readFile('./README.md', function (err, data) {
if (err) {
throw err
}
console.log(data.toString())
})
err 就表示操作的过程中可能出错的对象
data 就是最终的执行结果
IO 需要时间,例如有 1MB 的文件 和 100MB 的文件
异步IO 不会阻塞后续代码的继续执行
一般一个异步IO往往都需要一个回调函数来接收处理结果
而 回调函数中一般第一个参数都是 err 对象,表示可能出错的对象
第二个参数才是真正的数据
异步捕获不了异常,处理异步代码中的异常
异步IO,如果出错,node 不会帮你自动抛出异常
对于异步IO,try-catch 是无法捕获异常的
如果想要捕获异常,只能通过回调函数中的参数 err 来判断进行捕获
所以 err 一定要进行判断,否则代码不健壮
同步:
try {
var data = fs.readFileSync('./aaa').toString().
console.log(data)
} catch (e) {
return console.log('读取文件失败了')
}
同步IO,执行同步IO,当前代码阻塞,等待IO数据的返回
使用同步IO符合代码编程人员的思维习惯,但是性能不高,必须干完一件事儿,才能继续下一件事儿
对于同步IO,只能使用 try-catch 来捕获异常
fs模块对文件的几乎所有操作都有同步和异步两种形式:
例如:`readFileSync()` 和 `readFile()`
8.2 文件基本操作
8.2.1 文件写入
8.2.2 向文件中追加内容
8.2.3 文件读取
8.2.4 其它文件操作任务
8.2. 文件监视
8.3 目录操作
8.4 流