Node模組-官方流程分析(分析Node原始碼)
技術標籤:javascriptnode.js前端
前言
PS:在這裡小編分析Node模組系統原始碼的時候,使用的Node版本是:6.17.1;
- 主要目的是低版本的Node原始碼很適合初學者研究並且學習,Node版本太高,有的技術點就會發生變化,不利於初學者研究~
一、知識點補充
小編在給大家分析Node原始碼之前,必須先補充幾個有關Node原始碼的知識點:
1.Node模組原理分析:要想使用模組必須先通過require()方法匯入模組,所以可以推斷出require()方法的作用其實就是讀取檔案,所以要想了解Node是如何實現模組的,必須先了解如何執行讀取到的程式碼;
2.執行從檔案中讀取的程式碼:我們都知道通過fs模組可以讀取檔案,但是讀取的資料要麼是二進位制,要麼是字串,無論是二進位制還是字串都無法直接執行;但是我們知道如果是字串,在JS中還是有辦法讓它執行的,使用 eval 或者 new Function,使用方法如下:
使用eval()方法執行字串:
let str = "console.log('www.it666.com');";
eval(str); // www.it666.com
let name = "lnj";
let str = "console.log(name);";
eval(str); // lnj
//結論:存在依賴關係,字串可以訪問外界資料(即訪問了外界的變數 name),不安全
使用 new Function() 方法執行字串:
let str = "console.log('www.it666.com');"; let fn = new Function(str); // console.log(fn); // 輸出是一個函式 fn(); // www.it666.com let name = "lnj"; let str = "console.log(name);"; let fn = new Function(str); fn(); // lnj //結論:存在依賴關係,字串可以訪問外界資料(訪問了外界變數name),不安全
得出結論:eval()和 new Function()兩種方法執行字串程式碼不安全,所以不建議使用;
- 其他方法:Node中的一個模組:vm
使用vm模組中的方法執行字串:
let vm = require("vm"); let str = "console.log('www.it666.com');"; vm.runInThisContext(str); // www.it666.com let name = "lnj"; let str = "console.log(name);"; vm.runInThisContext(str); // 執行結果下面截圖所示 //結論:runInThisContext:提供了一個安全的環境給我們執行字串中的程式碼 // runInThisContext提供的環境不能訪問本地的變數
上述程式碼執行結果如下:
二、Node原始碼分析
1.小編提示
小編提示:下面是小編在Node v6.17.1版本中提取出來的原始碼,並且給大家加上了註釋,便於理解~,Node是面試題必問的一個點,搞懂Node模組系統的原理,是必備的,下面小編給大家整理的真的已經非常非常清晰了,小編真心希望,想透徹搞懂Node原理的小夥伴們務必認真看~
2. Node原始碼上臺表演
//1.內部實現了一個require方法
function require(path) {
return self.require(path);
}
//2.通過Module物件的靜態_load方法載入模組檔案
Module.prototype.require = function(path) {
return Module._load(path, this, /* isMain */ false);
};
//3.通過Module物件的靜態_resolveFilename方法,得到絕對路徑並新增字尾名(相對路徑==>絕對路徑)
var filename = Module._resolveFilename(request, parent, isMain);
//4.根據路徑判斷是否有快取,如果沒有就建立一個新的Module模組物件並快取起來
var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
}
var module = new Module(filename, parent);
Module._cache[filename] = module;
function Module(id, parent) {
this.id = id;
this.exports = {};
}
//5.利用tryModuleLoad方法載入模組
tryModuleLoad(module, filename);
//5.1取出當前載入模組的字尾
var extension = path.extname(filename) || '.js';
//5.2根據不同字尾查詢不同方法並執行對應的方法,載入模組
Module._extensions[extension](this, filename);
//5.3如果是json模組,就轉換成物件
var content = fs.readFileSync(filename, 'utf8');
module.exports = JSON.parse(internalModule.stripBOM(content));
//5.4如果是js模組,就包裹成一個函式
var wrapper = Module.wrap(content);
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
//5.5執行包裹函式之後的程式碼,拿到執行結果(將String型別的函式轉化為了 函式)
var compiledWrapper = vm.runInThisContext(wrapper);
// 5.6利用call(高版本Node)或apply(低版本Node)執行函式,修改module.exports的值(修改函式中的this指向)
var args = [this.exports, require, this, filename, dirname];
var result = compiledWrapper.apply(this.exports, args);
// 5.7返回module.exports
return module.exports;
三.手寫Node模組系統
PS:小編是這樣一個人,怎麼樣的一個人呢?小編每次在百度搜索一些技術點看的時候,如果看到有很長很長的一篇部落格,小編看到呢,就會感到頭疼,心裡突然就會變得很煩躁,因為眼前的部落格太多,太長了,小編就首先會一直把部落格拉到最底部,看看還有多少內容~,不知道小夥伴們有沒有和小編一樣的feel呢?
所以呢,小編就把 “手寫Node模組系統”這個知識點放到下一個部落格中了
連結:https://blog.csdn.net/weixin_54443444/article/details/113703808