溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

NodeJS中的模塊是單例嗎

發(fā)布時間:2021-11-17 15:18:42 來源:億速云 閱讀:202 作者:iii 欄目:web開發(fā)

本篇內(nèi)容介紹了“NodeJS中的模塊是單例嗎”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

NodeJS的模塊默認情況下是單例性質(zhì)的,不過其并不能保證如我們編程時設(shè)想的那樣一定是單例,根據(jù)NodeJS的官方文檔中描述,某個模塊導入是否為單例受以下兩個因素的影響:

  • Node  模塊的緩存機制是大小寫敏感的,譬如如果你require('/foo')與require('/FOO')會返回兩個不同的對象,盡管你的foo與FOO是完全相同的文件。

  • 模塊是基于其被解析得到的文件名進行緩存的,鑒于不同的模塊會依賴于其被調(diào)用的路徑進行緩存鑒別,因此并不能保證你使用require('foo')會永遠返回相同的對象,可能會根據(jù)不同的文件路徑得到不同的對象。

創(chuàng)建新的NodeJS模塊

根據(jù)NodeJS文檔所述,文件和模塊是一一對應(yīng)的關(guān)系。這個也是解釋上文提及的模塊緩存機制的基礎(chǔ),我們首先創(chuàng)建一個簡單的模塊:

// counter.js   let value = 0  module.exports = {   increment: () => value++,   get: () => value, }

在counter.js中我們創(chuàng)建了某個私有變量,并且只能通過公共的increment與get方法進行操作。在應(yīng)用中我們可以如下方法使用該模塊:

// app.js const counter = require(‘./counter.js’)  counter.increment() counter.increment()  console.log(counter.get()) // prints 2 console.log(counter.value) // prints undefined as value is private

Module Caching

NodeJS會在***次導入某個模塊之后將該模塊進行緩存,在官方文檔中有如下描述:

Every call to require(‘foo’) will get exactly the same object returned, if it  would resolve to the same file.

我們也可以通過如下簡單的例子來驗證這句話:

// app-singleton.js  const counter1 = require(‘./counter.js’) const counter2 = require(‘./counter.js’)  counter1.increment() counter1.increment() counter2.increment()  console.log(counter1.get()) // prints 3 console.log(counter2.get()) // also prints 3

可以看出盡管我們兩次導入了該模塊,但是還是指向了同一個對象。不過并不是每次我們導入同一個模塊時,都會得到相同的對象。在NodeJS中,模塊對象有個內(nèi)置的方法:Module._resolveFilename(),其負責尋找require中合適的模塊,在找到正確的文件之后,會根據(jù)其文件名作為緩存的鍵名。官方的搜索算法偽代碼為:

require(X) from module at path Y 1. If X is a core module,    a. return the core module    b. STOP 2. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X)       1. If X is a file, load X as JavaScript text.  STOP       2. If X.js is a file, load X.js as JavaScript text.  STOP       3...       4... b. LOAD_AS_DIRECTORY(Y + X)       1. If X/package.json is a file,          a. Parse X/package.json, and look for "main" field.          b. let M = X + (json main field)          c. LOAD_AS_FILE(M)       2. If X/index.js is a file, load X/index.js as JS text.  STOP       3...       4... 3. LOAD_NODE_MODULES(X, dirname(Y)) 4. THROW "not found"

簡單來說,加載的邏輯或者說優(yōu)先級為:

  • 優(yōu)先判斷是不是核心模塊

  • 如果不是核心模塊則搜索node_modules

  • 否則在相對路徑中進行搜索

解析之后的文件名可以根據(jù)module對象或得到:

// counter-debug.js  console.log(module.filename) // prints absolute path to counter.js console.log(__filename) // prints same as above // i get: "/Users/laz/repos/medium/modules/counter-debug.js"  let value = 0  module.exports = {   increment: () => value++,   get: () => value,

在上述的例子中我們可以看出,解析得到的文件名即使被加載模塊的絕對路徑。而根據(jù)文件與模塊一一映射的原則,我們可以得出下面兩個會破壞模塊導入單例性的特例。

Case Sensitivity

在大小寫敏感的文件系統(tǒng)中或者操作系統(tǒng)中,不同的解析之后的文件可能會指向相同的文件,但是其緩存鍵名會不一致,即不同的導入會生成不同的對象。

// app-no-singleton-1.js const counter1 = require('./counter.js') const counter2 = require('./COUNTER.js')  counter1.increment() console.log(counter1.get()) // prints 1 console.log(counter2.get()) // prints 0, not same object as counter1  /*  We have two different resolved filenames: - “Users/laz/repos/medium/modules/counter.js” - “Users/laz/repos/medium/modules/COUNTER.js” */

在上面的例子中,我們分別用counter、COUNTER這僅僅是大小寫不同的方式導入相同的某個文件,如果是在某個大小寫敏感的系統(tǒng)中,譬如UBUNTU中會直接拋出異常:

NodeJS中的模塊是單例嗎

解析為不同的文件名

當我們使用require(x)并且x不屬于核心模塊時,其會自動搜索node_modules文件夾。而在npm3之前,項目會以嵌套的方式安裝依賴。因此當我們的項目依賴module-a與module-b,并且module-a與module-b也相互依賴時,其會生成如下文件路徑格式:

// npm2 installed dependencies in nested way app.js package.json node_modules/ |---module-a/index.js |---module-b/index.js     |---node_modules         |---module-a/index.js

這樣的話,我們對于同一個模塊就有兩個副本,那當我們在應(yīng)用中導入module-a時,豈會載入如下文件:

 // app.js const moduleA = require(‘module-a’) loads: “/node_modules/module-a/index.js”

而從module-b中載入module-a時,其載入的是如下文件:

 // /node_modules/module-b/index.js const moduleA = require(‘module-a’) loads “/node_modules/module-b/node_modules/module-a/index.js”

不過在npm3之后,其以扁平化方式進行文件加載,其文件目錄結(jié)構(gòu)如下所示:

 // npm3 flattens secondary dependencies by installing in same folder app.js package.json node_modules/ |---module-a/index.js |---module-b/index.js

不過此時就存在另一個場景,即我們應(yīng)用本身依賴module-a@v1.1與module-b,而module-b又依賴于module-a@v1.2,在這種情況下還是會采用類似于npm3之前的嵌套式目錄結(jié)構(gòu)。這樣的話對于module-a一樣會產(chǎn)生不同的對象,不過此時本身就是不同的文件了,因此相互之間不會產(chǎn)生沖突。

“NodeJS中的模塊是單例嗎”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI