(七)nodejs循序漸進-模塊系統(進階篇)

模塊系統

為了讓Node.js的文件可以相互調用,Node.js提供了一個簡單的模塊系統。

模塊是Node.js 應用程序的基本組成部分,文件和模塊是一一對應的。換言之,一個 Node.js 文件就是一個模塊,這個文件可能是JavaScript 代碼、JSON 或者編譯過的C/C++ 擴展。

創建模塊

Node.js 提供了 exports 和 require 兩個對象,其中 exports 是模塊公開的接口,require 用于從外部獲取一個模塊的接口,即所獲取模塊的 exports 對象。

接下來我們就來創建 hello.js 文件模塊,代碼如下:

exports.world = function() {console.log('Hello World');
}

我們創建一個?main.js?文件,代碼如下:

var hello = require('./hello');
hello.world();

以上實例中,代碼 require('./hello') 引入了當前目錄下的 hello.js 文件(./ 為當前目錄,node.js 默認后綴為 js)。

在以上示例中,hello.js 通過 exports 對象把 world 作為模塊的訪問接口,在 main.js 中通過 require('./hello') 加載這個模塊,然后就可以直接訪 問 hello.js 中 exports 對象的成員函數了。

有時候我們只是想把一個對象封裝到模塊中,格式如下:

module.exports = function() {// ...
}
例如:
???????//hello.js 
function Hello() { var name; this.setName = function(thyName) { name = thyName; }; this.sayHello = function() { console.log('Hello ' + name); }; 
}; 
module.exports = Hello;

這樣就可以直接獲得這個對象了:

//main.js 
var Hello = require('./hello'); 
hello = new Hello(); 
hello.setName('BYVoid'); 
hello.sayHello(); 

模塊接口的唯一變化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用該模塊時,其接口對象就是要輸出的 Hello 對象本身,而不是原先的 exports。


服務端的模塊放在哪里

也許你已經注意到,我們已經在代碼中使用了模塊了。像這樣:

var http = require("http");...http.createServer(...);

Node.js 中自帶了一個叫做?http?的模塊,我們在我們的代碼中請求它并把返回值賦給一個本地變量。

這把我們的本地變量變成了一個擁有所有 http 模塊所提供的公共方法的對象。

Node.js 的 require 方法中的文件查找策略如下:

由于 Node.js 中存在 4 類模塊(原生模塊和3種文件模塊),盡管 require 方法極其簡單,但是內部的加載卻是十分復雜的,其加載優先級也各自不同。如下圖所示:

從文件模塊緩存中加載

盡管原生模塊與文件模塊的優先級不同,但是都會優先從文件模塊的緩存中加載已經存在的模塊。

從原生模塊加載

原生模塊的優先級僅次于文件模塊緩存的優先級。require 方法在解析文件名之后,優先檢查模塊是否在原生模塊列表中。以http模塊為例,盡管在目錄下存在一個 http/http.js/http.node/http.json 文件,require("http") 都不會從這些文件中加載,而是從原生模塊中加載。

原生模塊也有一個緩存區,同樣也是優先從緩存區加載。如果緩存區沒有被加載過,則調用原生模塊的加載方式進行加載和執行。

從文件加載

當文件模塊緩存中不存在,而且不是原生模塊的時候,Node.js 會解析 require 方法傳入的參數,并從文件系統中加載實際的文件,加載過程中的包裝和編譯細節在前一節中已經介紹過,這里我們將詳細描述查找文件模塊的過程,其中,也有一些細節值得知曉。

require方法接受以下幾種參數的傳遞:

  • http、fs、path等,原生模塊。
  • ./mod或../mod,相對路徑的文件模塊。
  • /pathtomodule/mod,絕對路徑的文件模塊。
  • mod,非原生模塊的文件模塊。

在路徑 Y 下執行 require(X) 語句執行順序:

1. 如果 X 是內置模塊a. 返回內置模塊b. 停止執行
2. 如果 X 以 '/' 開頭a. 設置 Y 為文件根路徑
3. 如果 X 以 './' 或 '/' or '../' 開頭a. LOAD_AS_FILE(Y + X)b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. 拋出異常 "not found"LOAD_AS_FILE(X)
1. 如果 X 是一個文件, 將 X 作為 JavaScript 文本載入并停止執行。
2. 如果 X.js 是一個文件, 將 X.js 作為 JavaScript 文本載入并停止執行。
3. 如果 X.json 是一個文件, 解析 X.json 為 JavaScript 對象并停止執行。
4. 如果 X.node 是一個文件, 將 X.node 作為二進制插件載入并停止執行。LOAD_INDEX(X)
1. 如果 X/index.js 是一個文件,  將 X/index.js 作為 JavaScript 文本載入并停止執行。
2. 如果 X/index.json 是一個文件, 解析 X/index.json 為 JavaScript 對象并停止執行。
3. 如果 X/index.node 是一個文件,  將 X/index.node 作為二進制插件載入并停止執行。LOAD_AS_DIRECTORY(X)
1. 如果 X/package.json 是一個文件,a. 解析 X/package.json, 并查找 "main" 字段。b. let M = X + (json main 字段)c. LOAD_AS_FILE(M)d. LOAD_INDEX(M)
2. LOAD_INDEX(X)LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:a. LOAD_AS_FILE(DIR/X)b. LOAD_AS_DIRECTORY(DIR/X)NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,a. if PARTS[I] = "node_modules" CONTINUEb. DIR = path join(PARTS[0 .. I] + "node_modules")c. DIRS = DIRS + DIRd. let I = I - 1
5. return DIRS

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444544.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(八)nodejs循序漸進-事件驅動(進階篇)

事件驅動程序 Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉然后進行處理,然后去服務下一個web請求。 當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。 這個模型…

leetcode304. 二維區域和檢索 - 矩陣不可變

給定一個二維矩陣,計算其子矩形范圍內元素的總和,該子矩陣的左上角為 (row1, col1) ,右下角為 (row2, col2)。 上圖子矩陣左上角 (row1, col1) (2, 1) ,右下角(row2, col2) (4, 3),該子矩形內元素的總和為 8。 示例…

(九)nodejs循序漸進-Express框架(進階篇)

Express 框架 Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你創建各種 Web 應用,和豐富的 HTTP 工具。 使用 Express 可以快速地搭建一個完整功能的網站。 Express 框架核心特性: 可以設置中間件來響應 HTTP 請求。 定…

leetcode326. 3的冪 如此6的操作你想到了嗎

給定一個整數,寫一個函數來判斷它是否是 3 的冪次方。 示例 1: 輸入: 27 輸出: true 示例 2: 輸入: 0 輸出: false 示例 3: 輸入: 9 輸出: true 示例 4: 輸入: 45 輸出: false 進階: 你能不使用循環或者遞歸來完成本題嗎? 注意最后一句…

(十)nodejs循序漸進-高性能游戲服務器框架pomelo之介紹和安裝篇

目錄 Pomelo 安裝Pomelo 創建demoserver項目 pomelo命令 項目結構說明 pomelo框架 架構 服務器實現 客戶端請求與響應、廣播的抽象介紹 Pomelo pomelo是一個快速、可擴展、Node.js分布式游戲服務器框架,對游戲服務器開發感興趣的同學可以關注關注。 之前…

leetcode344. 反轉字符串 史上最簡單力扣題

編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。 不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 你可以假設數組中的所有字符都是 ASCII 碼表中的可打印字符。…

(十一)nodejs循序漸進-高性能游戲服務器框架pomelo之啟動流程和組件

游戲啟動過程 啟動入口 在使用pomelo進行游戲開發時,工程目錄下的app.js是整個游戲服務器的啟動運行入口。app.js中創建項目,進行默認配置并啟動服務器的代碼如下: var pomelo require(pomelo); var app pomelo.createApp(); app.set(na…

(十二)nodejs循序漸進-高性能游戲服務器框架pomelo之創建一個游戲聊天服務器

上個章節我們簡單介紹了下pomelo的安裝和目錄結構,有讀者可能覺得有點吃不消,為什么不再深入講一講目錄結構和里邊的庫,這里我就不費口舌了,大家可以去官網參考文檔說明,本文只告訴大家如何利用這個框架來開發自己的東…

看這玩意復習你還會掛科?《軟件工程篇》

軟件工程:是指導軟件開發和維護的一門工程學科 三要素方法/工具/開發過程 價值:促進項目成功 現代產品開發三原則:功用性、可行性、稱許性 軟件過程是軟件工程的核心組成部分。 迭代 :反復求精 增量:逐塊建造 需…

C++:02---命名空間

一、概念: ①類似于倉庫,空間內存儲代碼,需要用到時調用②也為防止名字沖突提供了更加可控的機制二、命名空間的定義 定義的基本格式如下:namespace 命名空間名 { //一系列聲明與定義 };三、命名空間的注意事項 命名空間定義時最后的分號可有可無只要出現在全局作用域中的…

看這玩意復習你還會掛科?《軟件工程2篇》

第一章: 軟件工程定義: 1968年10月,Fritz Bauer 首次提出了“軟件工程”的概念,并將“軟件工程”定義為:為了經濟地獲得能夠在實際機器上有效運行的可靠軟件,而建立并使用的一系列工程化原則。 1993年IE…

C++:05---命名空間

一、概念: ①類似于倉庫,空間內存儲代碼,需要用到時調用②也為防止名字沖突提供了更加可控的機制二、命名空間的定義 定義的基本格式如下:namespace 命名空間名 { //一系列聲明與定義 };三、命名空間的注意事項 命名空間定義時最后的分號可有可無只要出現在全局作用域中的…

C++:04---內聯函數

1.概念: 內聯類似于宏定義,當程序執行到內聯函數時,相當于復制了一份函數代碼。犧牲代碼空間,贏得了時間 內聯說明只是向編譯器發出一個請求,編譯器可以選擇忽略這個請求 2.關鍵字:inline 聲明時寫了inline,定義時可省略。建議聲明和定義都加上inlineinline int add(int…

leetcode86. 分隔鏈表

給定一個鏈表和一個特定值 x,對鏈表進行分隔,使得所有小于 x 的節點都在大于或等于 x 的節點之前。 你應當保留兩個分區中每個節點的初始相對位置。 示例: 輸入: head 1->4->3->2->5->2, x 3 輸出: 1->2->2->4->3->5…

(十三)nodejs循序漸進-高性能游戲服務器框架pomelo之擴展聊天服務器為機器人自動聊天

聊天服務器擴展 大家在上一篇文章里相信已經學會了pomelo框架的基本用法了,那么我們在上一篇文章的代碼基礎上繼續擴展,豐富系統,另外也熟悉下他的更多的用法,這一節我將擴展它:增加一個機器人自動聊天的功能。 目的…

C++:09---類靜態成員、類常量成員

一、類靜態成員(static) 先介紹一下什么是靜態變量、靜態函數 靜態局部變量:存在域(全局數據區),作用域(塊作用域)靜態全局變量:存在域(全局數據區),作用域(整個文件)靜態函數:存在域(全局數據區),作用域(整個文件)static int a=10;//全局靜態變量 static vo…

C++:08---成員變量初始化方式

成員變量初始化有三種方式: 在構造函數體內賦值初始化在自定義的公有函數體中賦值初始化(一般用于成員變量的初始化)在構造函數的成員初始化列表初始化一、構造函數體內初始化 說明:在構造函數體內的初始化方式,本質是是為成員變量賦值,而不是真正意義上的初始化,這點要…

leetcode1290. 二進制鏈表轉整數 刷新認知,最簡單算法題

給你一個單鏈表的引用結點 head。鏈表中每個結點的值不是 0 就是 1。已知此鏈表是一個整數數字的二進制表示形式。 請你返回該鏈表所表示數字的 十進制值 。 示例 1: 輸入:head [1,0,1] 輸出:5 解釋:二進制數 (101) 轉化為十進…

Redis:02---安裝Redis(Linux+Windows+Docker)

Linux安裝:一、安裝方式1(下載源碼編譯安裝)第一步:從下面的網址中下載Redis最新穩定版本的源代碼sudo wget http://download.redis.io/redis-stable.tar.gz第二步:下載完之后解壓,建立一個軟鏈接指向于red…

C++:10---再議拷貝構造函數

一、概念 使用一個已經存在的對象,去構造(初始化)另一個對象二、格式 參數加上const&,因為拷貝構造函數在幾種情況下都會被隱式地使用,因此拷貝構造函數不應該是explict的const:防止函數內部修改值&:防止無限循環拷貝類名(類名 const& 參數名) { 函數體 }三、…