node基礎(2.1)-----手寫一個require模組

好想吃涼皮發表於2018-04-06

動手寫require的思路

node基礎(2.1)-----手寫一個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

相關文章