摘要:先天就缺乏一項功能模塊通過標簽引入代碼的方式顯得雜亂無章,語言自身毫無組織和約束能力。與文件模塊區(qū)別地方在于它從內存中加載緩存執(zhí)行結果的位置核心模塊在對象上,文件模塊在對象上未完待續(xù)
javascript先天就缺乏一項功能:模塊
javasciprt 通過script標簽引入代碼的方式顯得雜亂無章,語言自身毫無組織和約束能力。人們不得不用命名空間等方式人為地約束代碼,以求達到安全和易用的目的。
為了讓javascript能在服務端有市場,社區(qū)為javascript制定了相應的規(guī)范——CommonJS
CommonJS規(guī)范 CommonJs的出發(fā)點javascript有以下缺點:
==沒有模塊系統(tǒng)==
==標準庫較少==-比如沒有文件系統(tǒng)、I/O流等標準的API
==沒有標準接口==-幾乎沒有定義過web服務器或者數(shù)據(jù)庫之類的標準統(tǒng)一接口
==缺乏包管理系統(tǒng)==-javascript沒有自動加載和安裝依賴的能力
CommonJS就是來彌補這些缺陷的
CommonJS大部分規(guī)范依舊是草案,但是為javascript開發(fā)大型應用程序指明了一條道路
Node受到CommonJS的影響,CommonJS因Node表現(xiàn)優(yōu)異而走入各個公司項目里,相互影響和促進
CommonJS的模塊規(guī)范分為模塊引用、模塊定義、模塊標識
==模塊引用== var math = require("math");
es6已更新一套支持模塊引用的語法:import。在調用require時,可以把它放在某個判斷條件下,但import不行;在打包編譯時,如果require里的文件模塊不存在,即便邏輯上不會進入其所在的判斷條件,依舊會報錯。可以使用try{}catch(e){}來處理
==模塊定義==
提供了exports對象用于導出當前模塊的方法或變量
export 是es6的規(guī)范,與import一同用,不要弄混了
一個模塊里還有module對象,代表模塊自身;exports是module的屬性
在Node中,一個文件就是一個模塊,將方法掛載在exports對象上作為屬性即可定義導出的方式
// math.js
exports.add = function(a,b){
return a+b;
}
exports.sum = function(a,b){
return a+b;
}
// index.js
var math = reqire("math");
exports.add = function(val){
return math.add(val,1);
}
==模塊標識==
指的是傳給require()方法的參數(shù),必須是符合小駝峰命名的字符串,或者以.、..開頭的相對路徑,或者絕對路徑
每個模塊具有獨立的空間,互不干擾,用戶不必考慮變量污染
Node的模塊實現(xiàn)Node沒有完全按照CommonJS規(guī)范實現(xiàn),做了一定的取舍并加入自身需要的特性。
Node引入模塊需經歷如下3個步驟
==路徑分析==
==文件定位==
==編譯執(zhí)行==
Node中模塊分為兩塊:一類是Node提供的模塊,稱為==核心模塊==;另一類是用戶編寫的模塊,稱為==文件模塊==
核心模塊在編譯過程中,成為二進制執(zhí)行文件;Node進程啟動時,部分核心模塊就直接加載進內存中
開發(fā)者引入這部分核心模塊時,省略了文件定位、編譯執(zhí)行,且在路徑分析中優(yōu)先判斷;故,加載速度最快
文件模塊是在運行時動態(tài)加載,需要路徑分析、文件定位、編譯執(zhí)行過程,速度比核心模塊慢
先路徑分析文件定位、最后再運行,所以前面require一個不存在的文件時,即便有判斷條件,依然會報錯
優(yōu)先從緩存加載Node對引入過的模塊都會進行緩存,以減少二次引入時的開銷。==Node緩存的是編譯和執(zhí)行之后的對象==
所以,所有模塊exports/export出的的東西,在內存中有且僅有一份,不受exports/export后的表達式/new之類的語句所影響
require()方法/import語句對于相同模塊的二次加載都一律采用緩存優(yōu)先的方式,這是第一優(yōu)先級的。核心模塊的緩存檢查優(yōu)于文件模塊的緩存檢查
路徑分析和文件定位因標識符的不同形式,模塊的查找和定位有不同程度的差異
模塊標識符分析
標識符分為以下幾類:
==核心模塊==,如http、fs、path
.或..開始的==相對路徑文件模塊==
以/開始的==絕對路徑文件模塊==
==非路徑形式的文件模塊==
核心模塊
優(yōu)先級僅次于緩存加載,加載速度最快
不能將自定義模塊的標識符與核心模塊標識符定義得一致,除非你換用路徑的方式
路徑形式的文件模塊
. .. /開始的標識符,均視為文件模塊
require將路徑轉為真實路徑,以此做索引,編譯執(zhí)行后的結果放入緩存
加載速度比核心模塊慢
自定義模塊
特殊的文件模塊,查找費時,加載速度最慢
“模塊路徑”——Node定位文件模塊時定制的查找策略。模塊路徑是一個路徑組成的數(shù)組:
[
?當前文件目錄下的xx目錄,
?父目錄下的xx目錄,
?父目錄下的父目錄下的xx目錄,
?沿路徑向上逐級遞歸,直到根目錄下的xx目錄
]
類似于js的原型鏈或作用域鏈的查找方式。也正因如此,一旦路徑越深,查找耗時就越多,所以加載速度最慢
文件定位
有緩存的存在和前面的路徑分析,文件定位相對比較簡單。這里注意一些細節(jié):
文件擴展名分析
有時標識符沒有擴展名,CommonJS規(guī)范不允許不包含文件擴展名,但Node會按.js、.json、.node的順序依次嘗試
嘗試時,會調用fs模塊同步阻塞時判斷文件是否存在。由于Node單線程,所以這里會引起性能問題——建議.node/.json文件加上擴展名
目錄分析和包
有時沒查找到文件,而是一個目錄
Node會先找當前目錄下的package.json,解析出main屬性指定的文件名進行定位;如果文件名沒有擴展名,則會進入擴展名分析的步驟
解析方式就是JSON.parse()
如果main提供的文件名有誤,或者沒有package.json,則查index(.js、.json、.json)
還沒定位成功,則按照自定義模塊的模塊路徑 策略,去父目錄查詢;如果模塊路徑數(shù)組遍歷完畢都沒找到,則拋出查找失敗的異常
模塊編譯(這里指的都是文件模塊,非核心模塊) 當定位到具體文件后,Node會新建一個模塊對象,將文件載入并編譯。載入方法根據(jù)不同文件擴展名而區(qū)分
.js文件:fs模塊同步讀取后編譯執(zhí)行
.node文件:這是c/c++編寫的擴展文件,通過dlopen()方法加載最后編譯生成的文件
.json文件:fs模塊同步讀取后用JSON.parse()解析返回結果
其余擴展名:當.js文件載入
編譯成功的模塊會緩存在Module._cache上,以文件路徑作為索引
javascript模塊的編譯
編譯過程中,Node對獲取的javascript的文件內容進行了頭尾包裝,頭部添加(function(==exports, require, module, __filename, __dirname==){ ,尾部添加 })
__filename 完整的文件路徑;__dirname 文件目錄
包裝后的代碼vm原生模塊的runInThisContext()執(zhí)行,返回一個具體的function對象
最后,將當前模塊對象的exports、require()、module、文件路徑和目錄作為參數(shù)傳入給function對象
執(zhí)行后exports返回給調用方,其上的任何方法與屬性均可被外部調用,但是模塊中的其余變量或屬性不可。==以此達到模塊間的作用域隔離==
這就是Node對CommonJS模塊規(guī)范的實現(xiàn)
exports的誤用 編寫代碼時,理論上只要這樣寫就行:
exports = function(){//My Class};
但是這樣寫是有問題的,我們來看看編譯過程:
頭尾包裝:
(function(exports, require, module, __filename, __dirname){
exports = function(){//My Class};
})
看,你把形參改了。。。但是exports最后是要返回給調用方然后被外部調用的,==改形參根本不能真正改變exports對象的內容==
所以,要不老老實實地寫:exports.add = ...;或者寫module.exports = ...
c/c++模塊的編譯
事實上,.node模塊不需要編譯,因為它是c/c++模塊之后編譯生成。它只需要加載和執(zhí)行
執(zhí)行中exports對象與.node模塊產生臉型,返回給調用者
優(yōu)勢:執(zhí)行效率高;劣勢:編寫門檻高
JSON文件的編譯
fs模塊同步讀取JSON文件
JSON.parse()方法得到對象
賦給exports
如果.json文件作為配置文件(如package.json),則不必調用fs模塊去讀取和解析,直接reqire()引入即可。同理它也是享受緩存的便利,二次引入時沒有性能影響
核心模塊分為c/c++編寫的和javascript編寫的兩部分
js核心模塊的編譯過程編譯程序會將js模塊文件編譯成c/c++代碼
轉存為c/c++代碼
將所有內置的js代碼轉換成c++的數(shù)組,此時js代碼不可直接執(zhí)行,啟動Node進程后,js代碼直接加載進內存,將來查找比文件模塊要快得多
編譯js核心模塊
經歷頭尾包裝、執(zhí)行、導出exports對象。與文件模塊區(qū)別地方在于:它從內存中加載;緩存執(zhí)行結果的位置
核心模塊在NativeModule._cache對象上,文件模塊在Module._cache對象上
(未完待續(xù)~)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7211.html
摘要:垃圾回收內存管理實踐先通過一個來看看在中進行垃圾回收的過程是怎樣的內存泄漏識別在環(huán)境里提供了方法用來查看當前進程內存使用情況,單位為字節(jié)中保存的進程占用的內存部分,包括代碼本身棧堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技術棧 | https:...
摘要:它是在的基礎上改進的一種方案,通過對文件描述符上的事件狀態(tài)進行判斷。檢索新的事件執(zhí)行與相關的回調幾乎所有情況下,除了關閉的回調函數(shù),它們由計時器和排定的之外,其余情況將在此處阻塞。執(zhí)行事件的,例如或者。 前言 學習Node就繞不開異步IO, 異步IO又與事件循環(huán)息息相關, 而關于這一塊一直沒有仔細去了解整理過, 剛好最近在做項目的時候, 有了一些思考就記錄了下來, 希望能盡量將這一塊的...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應用添加全局功能的一種強大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內優(yōu)雅的實現(xiàn)文件分片斷點續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應用添加全局功能的一種強大而且簡單的方式。插....
摘要:概述本文主要介紹了我對的一些核心特性的理解,包括架構特點機制核心模塊與簡單應用。在此期間,主線程繼續(xù)執(zhí)行其他任務。延續(xù)了瀏覽器端單線程,只用一個主線程執(zhí)行,不斷循環(huán)遍歷事件隊列,執(zhí)行事件。 原文地址在我的博客,轉載請注明來源,謝謝! node是在前端領域經常看到的詞。node對于前端的重要性已經不言而喻,掌握node也是作為合格的前端工程師一項基本功了。知道node、知道后端的一些東西...
摘要:前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。對該漏洞的綜合評級為高危。目前,相關利用方式已經在互聯(lián)網(wǎng)上公開,近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡...
閱讀 3197·2021-11-25 09:43
閱讀 3406·2021-11-11 16:54
閱讀 822·2021-11-02 14:42
閱讀 3740·2021-09-30 09:58
閱讀 3663·2021-09-29 09:44
閱讀 1278·2019-08-30 15:56
閱讀 2096·2019-08-30 15:54
閱讀 2985·2019-08-30 15:43