一、原理
module、exports、require都不存在在全局上,那么为什么模块可以使用这些变量。
分析require的原理
//require中的伪代码
function require(moudlePath) {
//1.根据传递的模块路径,得到完整的绝对路径
var moudleId = require.resolve(moudlePath)
//2.判断缓存
if(cache[moduleId]) {
return cache[moudleId]
}
//3.真正运行模块代码的辅助函数
function _require(exports, require, module, _filename, _dirname) {
//目标模块的代码在这里执行
}
//4.准备并运行辅助函数
var module = {
exports: {}
};
var exports = module.exports;
var _filename = moudleId; //得到模块文件的绝对路径
var _dirname = ...; //得到模块所在目录的绝对路径
_require.call(exports, exports, _require, module, _filename, _dirname)
//5.缓存module.exports
cache[moduleId] = module.exports;
//6. 返回module.exports;
return module.exports
}
1.通过resolve()将传入的模块路径moudlePath转化成绝对路径,保证路径的唯一性;
2.通过cache缓存表查看是否有moudleId对应的属性值,若有就返回属性值,避免多次执行。
//cache就是一个对象
cache {
moudleId: XXX
}
3.目标模块的代码就是在_require()中执行的,这也就是模块间不会造成全局变量污染的真正原因,_require()有五个参数,exports, require, module, _filname, _dirname
4.module是一个对象,exports是module对象中一个属性,属性值也是一个对象,_filename是moduleId代表的绝对路径,_dirname是模块所在目录的绝对路径。执行_require()利用call将_require()的this指向了exports。所以,这时的this === exports === module.exports。
5.将module.exports和对应的moduleId添加到缓存表cache中
6.返回module.exports,所以require('./')导入的是目标模块的module.exports
二、面试题
// a.js
exports.a = 1;
module.exports.b = 2;
module.exports = function(){}
module.exports.c = 3;
exports.d = 4;
this.e = 5;
console(this === exports);
console(this === module.exports);
console(exports === module.exports);
// index.js
var a = arguments[1]("./a.js")
console.log(typeof a);
console.log(a.a, a.b, a.c, a.d, a.e);
console.log(arguments.length);
首先看index.js
var a = arguments[1]("./a.js")
arguments[1]对应的是5个参数中的第二个参数require,所以其实就是var a = require("./a.js")
导入模块后,就将模块路径转成绝对路径,并检查缓存,然后在_require()执行目标模块a.js的代码
a.js运行过程:
最初 this === exports === module.exports
1.exports.a = 1;
exports {
a: 1
}
2.module.exports.b = 2;
exports { //module.exports == exports
a: 1,
b: 2
}
3.module.exports = function() {}
这时三者关系改变,module.exports脱离组织
this == exports module.exports = function(){}
4.module.exports.c = 3
本质上来说function(){}也是一个对象
module.exports {
c: 3
}
5.exports.d = 4
exports {
a: 1,
b: 2,
d: 4
}
6.this.e = 5
exports {
a: 1,
b: 2,
d: 4,
e: 5
}
7.console(this === exports)
true
8.console.log(this === module.exports)
false
9.console.log(exports === module.exports)
false
a.js执行完成
1.console.log(typeof(a))
a接收的是require("./a.js")返回的结果,所a的类型就module.exports的类型,所以typeof(a) 为function
2.console.log(a.a, a.b, a.c, a.d, a.e)
module.exports=function() {c=3}
也就是
module.exports: {
c: 3
}
所以对应结果为undefined,undefined,3,undefined,undefined
3.console.log(arguments.length)
arguments有5个参数 exports,require,module,_filename,_dirname
arguments.length == 5
最终结果:
true
false
false
function
undefined undefined 3 undefined undefined
5