一. 模块
1. 载入策略
原生模块已经被转成二进制执行文件,加载快。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。。
(1)文件模块
由module原生module模块实现。该原生模块启动时已经被加载。
1) require参数
原生模块
相对路径文件模块
绝对路径文件模块
非原生文件模块
2)所有代码都运行在模块作用域,不会污染全局作用域
导出方式:
exports.name = value;
module.exports = { name: value };
var exports = module.exports;
一旦为 module.exports 赋值,就会切断之前两者的相关性;
最终模块的导出成员以 module.exports 为准
3)require文件查找策略
从文件模块缓存中加载
从原生模块中加载
从文件加载
require('./module'); //先找js
// 此时文件按 JS 文件执行
require('./module.js'); //js没找到 找json
// 此时文件按 JSON 文件解析
require('./module.json'); //json没找到 找node
// 此时文件预编译好的 C++ 模块执行
require('./module.node'); //node没找到找module模块下的index.js
// 载入目录module目录中的 package.json 中main指向的文件
require('./module/index.js');
// 载入目录module 中的default.js文件
条件 | 描述 | 例子 |
---|---|---|
./ 或 ../ 开头 | 按照相对路径从当前文件所在文件夹开始寻找模块; | require(‘../file.js’); => 上级目录下找 file.js 文件 |
通过 / 开头 | 则以系统根目录开始寻找模块 | require(‘/Users/iceStone/Documents/file.js’); => 以绝对路径的方式找 |
不以“./“ 或 ”/“ 开头 | 则表示加载的是一个默认提供的核心模块(位于 Node 的系统安装目录中) | require(‘fs’); => 加载核心模块中的文件系统模块 |
或者从当前目录向上搜索 node_modules 目录中的文件 | require(‘my_module’); => 各级 node_modules 文件夹中搜索 my_module.js 文件 | |
传入的是一个目录的路径 | 自动查看该目录的 package.json 文件,然后加载 main 字段指定的入口文件 | |
package.json文件没有main字段,或者根本就没有package.json文件,则默认找目录下的 index.js 文件作为模块 | require(‘./calcuator’); => 当前目录下找 calculator 目录中的 index.js 文件 |
4) 包
require没找到文件时,会将文件名作为一个包来尝试加载,package.json文件的main字段定义了默认加载的文件,如之前的index.js。
package.json字段:
字段 | 描述 |
---|---|
name | 包名 npm上是唯一的 |
description | 简介 |
version | 版本号 |
keywords | 关键字数组,用于npm中分类搜索 |
maintainers | 维护者数组 |
contributors | 贡献者数组 |
bugs | 提交bug网址 |
licenses | 许可证 |
repositories | 源代码地址数组 |
dependencies | 包依赖,npm会自动加载依赖 |
5) 模块中的全局环境
属性方法名 | 描述 |
---|---|
__dirname | 用于获取当前文件所在目录的完整路径;在 REPL 环境无效; |
__filename | 用来获取当前文件的完整路径; 在 REPL 环境同样无效; |
module | 模块对象 |
exports | 映射到 module.exports 的别名 |
require() | |
require.cache | 模块的缓存 |
require.extensions | |
require.main | |
require.resolve() |
2. 内置模块
path:处理文件路径。
fs:操作(CRUD)文件系统。
child_process:新建子进程。
util:提供一系列实用小工具。
http:提供 HTTP 服务器功能。
url:用于解析 URL。
querystring:解析 URL 中的查询字符串。
crypto:提供加密和解密功能。
二. 事件机制
1. 事件
统一了前后端javascript编程模型
利用事件机制充分利用异步IO突破单线程编程模型的性能瓶颈
events.EventEmitter事件监听器
操作 | 描述 |
---|---|
addListener/on | 多次添加监听 |
once | 触发发一次后删除监听 |
removeListener | 根据函数删除监听 |
removeAllListeners | 删除该事件上的所有监听 |
emit | 触发事件 |
对一个时间加十个监听器就会得到一个警告,emitter.setMaxListeners(0)
可去掉这个警告
运行期间触发error事件,EventEmitter交于事件error监听处理,如果没有添加监听,抛出异常,若没有异常捕获,则会引起线程退出。
//addListener removeListener EventEmitter.listenerCount
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.addListener("testEvent", function() {
console.log("testEvent");
});
event.addListener("testEvent", function() {
console.log("testEvent");
});
var l2 = function() {
console.log("testEvent");
};
event.on("testEvent", l2);
event.emit("testEvent");
console.log(EventEmitter.listenerCount(event, "testEvent"));
event.removeListener("testEvent", l2);
console.log(EventEmitter.listenerCount(event, "testEvent"));
/*输出:
testEvent
testEvent
testEvent
3
2
*/
2.事件的继承
node中提供继承方法utils.inherit()
三. 文件系统
1. fs的同步与异步
fs模块对文件的几乎所有操作都有同步和异步两种形式
readFile() 和 readFileSync()
区别:
同步调用会阻塞代码的执行,异步则不会
异步调用会将读取任务下达到任务队列,直到任务执行完成才会回调
异常处理方面,同步必须使用 try catch 方式,异步可以通过回调函数的第一个参数
2. 文件读取
(1)基本方法
方式 | 代码 |
---|---|
异步 | fs.readFile(file[,options], callback(err, data){}) |
同步 | fs.readFileSync(file[,options]) |
文件流 | fs.createReadStream(path[,options]) |
//异步
console.time('async');
fs.readFile(path.join(__dirname, './images/1.jpg'), (err) => {
if(err) {
throw error;
}
});
console.timeEnd('async');
//同步
console.time("sync");
try {
let img = fs.readFileSync(path.join(__dirname,
'./images/1.jpg'));
}catch(error) {
throw error;
}
console.timeEnd("sync");
//文件流
const iconv = require('iconv-lite'); //解码中文
var data = '';
var streamReader = fs.createReadStream(path.join(__dirname,
'./传奇.lrc'));
streamReader.on('data', (chunk) => {
//部分文件流
console.log(iconv.decode(chunk, 'gbk'));
});
streamReader.on('end', () => {});
歌词解析读取案例
//逐行读取
const iconv = require('iconv-lite');
fs.readFile(path.join(__dirname, './传奇.lrc'), (err, data) => {
var lines = iconv.decode(data, 'gbk').split('\r\n');
var regex = /\[(\d{2})\:(\d{2})\.(\d{2})\](.+)/;
//[03:07.57]想你时你在天边
lines.forEach((line) => {
var matches = regex.exec(line);
if(matches) {
var m = parseFloat(matches[1]);
var n = parseFloat(matches[2]);
var f = parseFloat(matches[3]);
var lyric = matches[4];
setTimeout(() => {
console.log(lyric);
}, m*60*1000 + n*1000 + f);
}else {
//console.log("不是标准行" + line);
}
});
});
(2)readline实现上例
const fs = require('fs');
const path = require('path');
const iconv = require('iconv-lite');
const readline = require('readline');
var filename = path.join(__dirname, ''./传奇.lrc'');
var streamReader = fs.createReadStream(filename)
.pipe(iconv.decodeStream('gbk'));
// 利用readline读取
var rl = readline.createInterface({ input: streamReader });
var begin = new Date().getTime();
rl.on('line', (line) => {
task(line, begin);
});
var regex = /\[(\d{2})\:(\d{2})\.(\d{2})\]\s(.+)/;
function task(line, begin) {
var matches = regex.exec(line);
if (matches) {
var m = parseFloat(matches[1]);
var s = parseFloat(matches[2]);
var f = parseFloat(matches[3]);
var lyric = matches[4];
var offset = new Date().getTime() - begin;
setTimeout(() => {
console.log(lyric);
}, m * 60 * 1000 + s * 1000 + f - offset);
} else {
//console.log(line);
}
}
3.文件写入
方式 | 代码 |
---|---|
异步文件写入 | fs.writeFile(file,data[,option],callback(err)) |
同步文件写入 | fs.writeFileSync(file,data,[,option]) |
流式文件写 | fs.createWriteStream(path[,option]) |
异步追加 | fs.appendFile(file,data[,options],callback(err)) |
同步追加 | fs.appendFileSync(file,data[,options]) |
4. 其它文件操作
方式 | 代码 |
---|---|
获取文件信息 | fs.stat(path, callback(err, stats)) fs.statSync(path)返回fs.stats实例 |
移动文件 | fs.rename(oldpath, newpath) |
重命名文件或目录 | fs.rename(oldPath,newPath,callback) fs.renameSync(oldPath,newPath) |
删除文件 | fs.unlink(path,callback(err)) fs.unlinkSync(path) |
5. 目录操作
方式 | 代码 |
---|---|
创建一个目录 | fs.mkdir(path[,model],callback) fs.mkdirSync(path[,model]) |
删除一个空目录 | fs.rmdir(path,callback) fs.rmdirSync(path) |
读取一个目录 | fs.readdir(path,callback(err,files)) fs.readdirSync(path) // => 返回files |
6. 监视文件变化
fs.watchFile(filename[, options], listener(curr,prev))
options:{persistent,interval}
fs.watch(filename[,options][,listener])