動手寫require的思路
思路出來就開始寫程式碼了,先寫個架子let path = require('path')
let fs = require('fs')
function Module(filename){ //建構函式
this.filename = filename;
this.exports = {}
}
//副檔名存在它的建構函式上,作為私有屬性
Module._extentions = ['.js','.json','.node'] //如果沒有字尾
Module._cache = {} //專門快取路徑
Module._resolvePathname = function(filename){ //獲取絕對路徑的
let p = path.resolve(__dirname,filename)
console.log(p)
}
function req(filename){ //filename是檔名, 檔名可能沒有字尾
//所以我們要弄一個絕對路徑,快取是根據路徑來的
filename = Module._resolvePathname(filename)
}
let result = req('a') //req過來的是一個模組,所以需要一個建構函式
複製程式碼
這個程式碼的執行結果是e:\meterWang\ZF\require\a ;我們還要給它加上字尾。有沒有字尾用path.extname(p) 那麼我們就要把檔案的字尾加上,並判斷這個檔案存不存在
Module._resolvePathname = function(filename){ //獲取絕對路徑的
let p = path.resolve(__dirname,filename)
if(!path.extname(p)){
for(var i=0;i<Module._extentions.length;i++){
let newPath = p+Module._extentions[i];
try{ //accessSync如果檔案不存在會報錯
fs.accessSync(newPath)
return newPath
}catch(e){
}
}
}
return p
}
複製程式碼
路徑拿到了,那要看看在快取裡有沒有。Module._catch 那麼方法來了
function req(filename){ //filename是檔名, 檔名可能沒有字尾
//所以我們要弄一個絕對路徑,快取是根據路徑來的
filename = Module._resolvePathname(filename);
let cacheModule = Module._cache[filename];
if(cacheModule){ //快取裡有直接把快取的exports屬性。
return cacheModule.exports
}
//沒有就建立一個模組
let module = new Module(filename);
module.load(filename) //載入這個模組
}
複製程式碼
我們載入這個模組用到了load方法,並且是例項上的,那麼就要在Module的原型上加:
Module.prototype.load =function(filename){
//模組有可能是json,js
let ext = path.extname(filename).slice(1);
Module.__extentions[ext](this);
}
複製程式碼
Module.__extentionsext;這裡的這段程式碼就是在__extentions這個陣列上加了一個屬性,屬性值是一個方法,this代表的就是當前例項,其實就是相當於下面這個程式碼:
Module.wrapper = [
"(function(exports,require,module,__dirname,__fikename){","\n})"
]
Module.wrap = function(script){
return Module.wrapper[0]+script+Module.wrapper[1]
}
Module.__extentions['js'] = function(module){
let script = fs.readFileSync(module.filename);
let fnStr = Module.wrap(script)
//第一個引數就是this指向,第二個引數就是module.exports
//vm.runInThisContext(fnStr)==>eval()執行沒有意義,所以要給它call一下 vm.runInThisContext(fnStr).call(module.exports,module.exports,req,module)
}
複製程式碼
我們以前說過,node價值模組其實是在外層加了一個(function(exports,require,module,__dirname,__fikename){/**程式碼**/})這個殼,所以我們自己寫的時候也要加上。
現在我們的require模組就寫完了,完整的程式碼如下
let path = require('path');
let fs = require('fs');
let vm= require('vm');
function Module(filename){
this.filename = filename;
this.exports = {};
this.loaded = false;
}
Module._cache = {}; //快取
Module.__extentions = ['.js','.json','.node'] //字尾
Module._resolvePath = function(filename){
let p = path.resolve(__dirname,filename);
if(!path.extname(p)){//字尾沒有的化就給它加上
for(var i=0;i<Module.__extentions.length;i++){
let newPath = p + Module.__extentions[i]
try{ //accessSync如果檔案不存在會報錯
fs.accessSync(newPath)
return newPath
}catch(e){
}
}
}
return p
}
Module.wrapper = [
"(function(exports,require,module,__dirname,__fikename){","\n})"
]
Module.wrap = function(script){
return Module.wrapper[0]+script+Module.wrapper[1]
}
Module.__extentions['js'] = function(module){
let script = fs.readFileSync(module.filename);
let fnStr = Module.wrap(script)
//第一個引數就是this指向,第二個引數就是module.exports
vm.runInThisContext(fnStr).call(module.exports,module.exports,req,module)
}
Module.prototype.load =function(filename){
//模組有可能是json,js
let ext = path.extname(filename).slice(1);
Module.__extentions[ext](this);
}
function req(filename){
filename = Module._resolvePath(filename)
//路徑有了就可以載入了
let cacheModule = Module._cache[filename]
if(cacheModule){
return cacheModule.exports
}
let module = new Module(filename)
module.load(filename);
Module._cache[filename] = module; //加入快取
module.loaded = true; // 表示當前模組是否載入完
return module.exports;
}
let result = req('a');
console.log(result)
複製程式碼
a.js檔案內容如下
console.log('載入');
module.exports = 'zufe'
複製程式碼
vscode執行結果如下:
載入 zufe