1. 程式人生 > 其它 >Node模組-官方流程分析(分析Node原始碼)

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

連結:http://nodejs.cn/api/

使用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