global
浏览器的全局对象是
window,而Node的全局对象是global。在Node中可以直接访问global,默认声明的属性时不放在global上的,因为Node的每个模块最外层包了一个匿名函数,也就是说this === module.exports
global 中的属性叫全局属性 + module,exports require __dirname __filename 叫全局对象(在文件内部也是可以直接使用)
// 这样可以获取global上的内容
(function(){
console.log(Object.keys(this))
})()
process
- process.argv
代表用户通过node执行文件传递的参数 默认前两个参数 没有实际意义
// node index.js --port = 3000 --config = 'xxx'
console.log(process.argv.slice(2))
// 取到传递参数的方法
let config = process.argv.slice(2).reduce((memo,current,index,array)=>{
if(current.includes('--')){
memo[current.slice(2)] = array[index+1];
}
return memo;
},{});
可以配合commander 来解析process.argv 并且生成类似cli的命令帮助等。
const program = require('commander');
const chalk = require('chalk');
// 解析用户的参数 默认提供--help
program // 配置命令 我输入命令后 要执行一些内容
.command('create')
.alias('c')
.description('create project')
.action(()=>{
console.log('create project')
})
program //配置属性 给代码传递参数
.option('-p, --port <val>','set port')
.version('1.0.0')
program.on('--help',()=>{
console.log('\r\nExamples')
console.log(' node 1.js --help');
console.log(' node 1.js create '+chalk.green('project'))
}).parse(process.argv);
// chalk 粉笔
console.log(program.port);
- process.chdir
方法变更 Node.js 进程的当前工作目录,如果变更目录失败会抛出异常
- process.cwd()
返回 Node.js 进程的当前工作目录。在哪里执行这个文件 ,目录就是哪里 代表的就是执行的文件的目录
- process.nextTick(Node中的微任务)
定义出一个动作,并且让这个动作在下一个事件轮询的时间点上执行
- process.env
环境变量,可以根据这个变量的不同执行不同的结果,window和mac设置这个变量的方式不同,可以使用cross-env来统一设置。
CommonJS规范及实现
实现步骤
- req方法传递文件路径,Module.resolveFileName解析为绝对路径
- 判断缓存,存在直接使用,不存在创建新的模块,然后加入缓存
- 模块加载module.load,返回module.exports
- 根据不同的文件后缀名,采取不同的方法
- json文件直接返回,js文件使用闭包包裹,然后使用vm.runInThisContext生成函数执行
const path = require("path");
const fs = require("fs");
const vm = require("vm");
function Module(id) {
this.id = id;
this.exports = {}; // 模块的结果
}
Module.wrapper = [
"(function(module,exports,require,__filename,__dirname){",
"})"
];
Module.extensions = {
".js"(module) {
let script = fs.readFileSync(module.id, "utf8");
let fnStr = Module.wrapper[0] + script + Module.wrapper[1];
let fn = vm.runInThisContext(fnStr); // 让字符串变成js代码
// 第一个参数是改变this指向 module module.exports
fn.call(
module.exports,
module,
module.exports,
req,
module.id,
path.dirname(module.id)
);
// exports 已经让用户更改了
}, // js需要将exports 传入给用户 用户自己赋值
".json"(module) {
// 解析后 node默认会赋值
let script = fs.readFileSync(module.id, "utf8");
module.exports = JSON.parse(script);
}
};
// 给你一个相对路径 解析成绝对路径
Module.resolveFileName = function(filename) {
// 1) 把相对路径转化成绝对路口 默认会先判断一下是否是绝对路径
let absPath = path.resolve(__dirname, filename);
let flag = fs.existsSync(absPath); // 判断文件是否存在 异步方法被废弃
let current = absPath; // 默认是当前路径
if (!flag) {
let keys = Object.keys(Module.extensions);
for (let i = 0; i < keys.length; i++) {
current = absPath + keys[i]; // 当前找到的文件路径
let flag = fs.existsSync(current);
if (flag) {
break;
} else {
current = null;
}
}
}
if (!current) {
// 如果没有 说明加了后缀文件还是不存在
throw new Error("文件不存在");
}
return current; // 返回文件的路径
};
Module.prototype.load = function() {
// 模块加载就是读取文件的内容
let ext = path.extname(this.id);
Module.extensions[ext](this); // 根据不同的后缀 调用不同的处理方法
};
Module.cache = {};
function req(filename) {
// 自己实现了一个require方法
let current = Module.resolveFileName(filename);
if (Module.cache[current]) {
return Module.cache[current].exports;
}
let module = new Module(current); // 产生一个module
Module.cache[current] = module;
module.load();
return module.exports; // 默认导出module.exports对象
}