目錄
parent.js" >parent.js
son.js" >son.js
test.js" >test.js
望" >
問" >
為什麼狗日的模組名稱大小寫都錯了,還能載入? " >為什麼狗日的模組名稱大小寫都錯了,還能載入?
首頁 web前端 js教程 關於node.js和macOS之間的故事

關於node.js和macOS之間的故事

Dec 18, 2017 am 11:22 AM
javascript macos node.js

本文由一個小故事來和大家分享關於node.js和macOS之間的故事,希望能幫助大家。

喬治G在他的電腦上做了一個小測試,但結果和預期的大不相同。

那麼我們先來看看這個小測試都寫了什麼:

總共三個文件,程式碼總計不超過15行

<span style="font-size: 14px;">parent.js</span>

<span style="font-size: 14px;">class Parent {}<br><br>module.exports = Parent<br></span>
登入後複製

<span style="font-size: 14px;">son.js</span>

<span style="font-size: 14px;">//加载时把模块文件名首字母大写了(不正确的)<br/>const Parent = require(&#39;./Parent&#39;)<br/><br/>class Son extends Parent {}<br/><br/>module.exports = Son<br/></span>
登入後複製

<span style="font-size: 14px;">test.js</span>

<span style="font-size: 14px;">//加载时把模块名首字母大写(不正确的)<br/>const ParentIncorrect = require(&#39;./Parent&#39;)<br/>//通过正确的模块文件名加载(正确)<br/>const Parent = require(&#39;./parent&#39;)<br/><br/>const Son = require(&#39;./son&#39;)<br/><br/>const ss = new Son()<br/><br/>//测试结果<br/>console.log(ss instanceof Parent) // false<br/>console.log(ss instanceof ParentIncorrect) // true<br/></span>
登入後複製

喬治G同學有以下問題:

  1. <span style="font-size: 14px;">son.js</span><span style="font-size: 14px;">test.js</span> 裡都有錯誤的檔名(大小寫問題)引用,為什麼不報錯?

  2. 測試結果,為什麼 <span style="font-size: 14px;">ss instanceof ParentIncorrect === true</span> ?不報錯我忍了,為什麼還認賊作父,說自己是那個透過不正確名字載入出來的模組的instance?

如果同學你對上述問題已經了然於胸,恭喜你,文能提筆安天,武能上馬定乾坤;上炕認識娘們,下炕認識鞋!

但如果你也不是很清楚為什麼?那麼好了,我有的說,你有的看。

其實斷症(裝逼範兒的debug)之法和中醫看病也有相似指出,望、聞、問、切四招可以按需選取一二來尋求答案。

程式碼不多,看了一會兒,即使沒有我的註釋,相信仔細的同學也都發現真正的文件名和程式碼中引入時有出入的,那麼這裡肯定是有問題的,問題記住,我們繼續

<span style="font-size: 14px;"></span>

#這個就算了,程式碼我也聞不出個什麼鬼來

#來吧,軟體工程裡很重要的一環,就是溝通,不見得是和遇到bug的同事,可能是自己,可能是QA,當然也可能是PM或是你的老闆。你沒問出自己想知道的問題;他沒說清楚自己要回答的;都完蛋。 。 。 。

那我想知道什麼呢?以下兩件事作為debug的入口比較合理:

  1. 作業系統

  2. ##運行環境+ 版本<span style="font-size: 14px;"></span>

  3. 你怎麼測試的,命令列還是其他什麼手段<span style="font-size: 14px;"></span>

答曰:macOS; <span style="font-size: 14px;"></span>node.js > 8.0<span style="font-size: 14px;"></span># ;命令列<span style="font-size: 14px;"></span>##node test.js<span style="font-size: 14px;"> </span>

#切

<span style="font-size: 14px;"></span>

令人興奮的深刻到來了,我要動手了。 (為了完整的描述

<span style="font-size: 14px;"></span>debug<span style="font-size: 14px;"></span> 過程,我會假裝這下面的所有事情我事先都是不知道的)<span style="font-size: 14px;"></span>

準備電腦,完成

<span style="font-size: 14px;"></span>

準備運行環境

<span style="font-size: 14px;"></span>#node.js > 9.3.0<span style="font-size: 14px;">## ,完畢</span><span style="font-size: 14px;"></span>復刻程式碼,完畢

<span style="font-size: 14px;"></span>#運行,日了狗,果然沒報錯,而且運行結果就是喬治G說的那樣。

<span style="font-size: 14px;"></span>為了證明我沒瞎,我又嘗試在

​​

#test.js<span style="font-size: 14px;"> 裡</span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span> <span style="font-size: 14px;">require</span> 了一個壓根不存在的檔案

#require('./nidayede')<span style="font-size: 14px;"></span> ,執行程式碼。 <span style="font-size: 14px;"></span>還好這次報錯了

Error: Cannot find module './nidayede'<span style="font-size: 14px;"></span>

,所以我沒瘋。這點真令人高興。 ############於是有了第一個問題#######

為什麼狗日的模組名稱大小寫都錯了,還能載入?

會不會跟作業系統有關係?來我們再找台<span style="font-size: 14px;">windows</span> 試試,果然,到了<span style="font-size: 14px;">windows</span>## 上,大小寫問題就是個問題了, <span style="font-size: 14px;"></span>Error: Cannot find module './Parent'<span style="font-size: 14px;"></span> 。 <span style="font-size: 14px;"></span>

那麼 <span style="font-size: 14px;"></span>macOS<span style="font-size: 14px;"></span># 到底在做什麼?連個大小寫都分不出來麼?於是趕緊<span style="font-size: 14px;"></span>google<span style="font-size: 14px;"></span>(別問我為什麼不baidu)<span style="font-size: 14px;"></span>

關於node.js和macOS之間的故事

##原來人家牛逼的

OS X<span style="font-size: 14px;"></span> 預設用了<span style="font-size: 14px;"></span>case-insensitive<span style="font-size: 14px;"></span> 的文件系統( 詳細文件)。 <span style="font-size: 14px;"></span>

but why?這麼反人類的設計到底是為了什麼?

<span style="font-size: 14px;"></span>

關於node.js和macOS之間的故事

更多解釋, 來,走你

所以,這就是你不報錯的理由? (對

<span style="font-size: 14px;"></span>node.js<span style="font-size: 14px;"></span> 指責道),但這就是全部真相了。 <span style="font-size: 14px;"></span>

但事情沒完

<span style="font-size: 14px;"></span>

那認賊作父又是個什麼鬼?

<span style="font-size: 14px;"></span>

依稀有聽過

<span style="font-size: 14px;"></span>node.js<span style="font-size: 14px;"></span> 裡面有什麼緩存,是那個東西引起的麼?於是抱著試試看的心情,我把<span style="font-size: 14px;"></span>const ParentIncorrect = require('./Parent')<span style="font-size: 14px;"></span> 和<span style="font-size: 14px;"></span>const Parent = require('./parent')<span style="font-size: 14px;"></span> 換了下位置,心想,這樣最先按照正確的名字加載,會不會就對了呢? <span style="font-size: 14px;"></span>

果然,

還是不對 。靠猜和裝逼是不能夠真正解決問題的

那比比

<span style="font-size: 14px;"></span>ParentIncorrect<span style="font-size: 14px;"></span> 和<span style="font-size: 14px;"></span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span><span style="font-size: 14px;"></span>

<span style="font-size: 14px;"></span><span style="font-size: 14px;"></span>Parent<span style="font-size: 14px;"></span> 呢?於是我寫了<span style="font-size: 14px;"></span>console.log(ParentIncorrect === Parent)<span style="font-size: 14px;"></span> ,結果是<span style="font-size: 14px;"></span>false<span style="font-size: 14px;"></span> 。所以他兩個真的不是同一個東西,那麼說明問題可能在引入的部分嘍?

<span style="font-size: 14px;"></span>於是一個裝逼看<span style="font-size: 14px;"></span>node.js<span style="font-size: 14px;"></span> 原始碼的想法誕生了(其實不看,問題最終也能想明白)。 日了狗,懷著忐忑的心情,終於<span style="font-size: 14px;"></span>clone<span style="font-size: 14px;"></span># 了一把<span style="font-size: 14px;">##node.js</span> # 原始碼(花了好久,真tm慢)

來,我們一起進入神秘的<span style="font-size: 14px;"></span>node.js

原始碼世界。既然我們的問題是有關<span style="font-size: 14px;">require</span> 的,那就從她開始吧,不過找到

##require

<span style="font-size: 14px;"></span> 定義的過程需要點耐心,這裡不詳述,只說查找的順序吧

############src/node_main.cc => src/node. cc => lib/internal/bootstrap_node.js => lib/module.js###### ########找到咯,就是這個##########lib/ module.js######### ,進入正題:#############lib/module.js => require#######
<span style="font-size: 14px;">Module.prototype.require = function(path) {<br/>  assert(path, &#39;missing path&#39;);<br/>  assert(typeof path === &#39;string&#39;, &#39;path must be a string&#39;);<br/>  return Module._load(path, this, /* isMain */ false);<br/>};<br/></span>
登入後複製

好像没什么卵用,对不对?她就调用了另一个方法 <span style="font-size: 14px;">_load</span> ,永不放弃,继续

lib/module.js => _load

<span style="font-size: 14px;">Module._load = function(request, parent, isMain) {<br/>  //debug代码,么卵用,跳过<br/>  if (parent) {<br/>    debug(&#39;Module._load REQUEST %s parent: %s&#39;, request, parent.id);<br/>  }<br/><br/>  if (isMain && experimentalModules) {<br/>    //...<br/>    //...<br/>    //这段是给ES module用的,不看了啊<br/>  }<br/><br/>  //获取模块的完整路径<br/>  var filename = Module._resolveFilename(request, parent, isMain);<br/><br/>  //缓存在这里啊?好激动有没有?!?终于见到她老人家了<br/>  //原来这是这样的,简单的一批,毫无神秘感啊有木有<br/>  var cachedModule = Module._cache[filename];<br/>  if (cachedModule) {<br/>    updateChildren(parent, cachedModule, true);<br/>    return cachedModule.exports;<br/>  }<br/><br/>  //加载native但非内部module的,不看<br/>  if (NativeModule.nonInternalExists(filename)) {<br/>    debug(&#39;load native module %s&#39;, request);<br/>    return NativeModule.require(filename);<br/>  }<br/><br/>  //构造全新Module实例了<br/>  var module = new Module(filename, parent);<br/><br/>  if (isMain) {<br/>    process.mainModule = module;<br/>    module.id = &#39;.&#39;;<br/>  }<br/><br/>  //先把实例引用加缓存里<br/>  Module._cache[filename] = module;<br/><br/>  //尝试加载模块了<br/>  tryModuleLoad(module, filename);<br/><br/>  return module.exports;<br/>};<br/></span>
登入後複製

似乎到这里差不多了,不过我们再深入看看 <span style="font-size: 14px;">tryModuleLoad</span>

lib/module.js => tryModuleLoad

<span style="font-size: 14px;">function tryModuleLoad(module, filename) {<br/>  var threw = true;<br/>  try {<br/>    //加载模块<br/>    module.load(filename);<br/>    threw = false;<br/>  } finally {<br/>    //要是加载失败,从缓存里删除<br/>    if (threw) {<br/>      delete Module._cache[filename];<br/>    }<br/>  }<br/>}<br/></span>
登入後複製

接下来就是真正的 <span style="font-size: 14px;">load</span> 了,要不我们先停一停?

好了,分析问题的关键在于不忘初心,虽然到目前为止我们前进的比较顺利,也很爽对不对?。但我们的此行的目的并不是爽,好像是有个什么疑惑哦!于是,我们再次梳理下问题:

  1. <span style="font-size: 14px;">son.js</span> 里用首字母大写(不正确)的模块名引用了 <span style="font-size: 14px;">parent.js</span>

  2. <span style="font-size: 14px;">test.js</span> 里,引用了两次 <span style="font-size: 14px;">parent.js</span> ,一次用完全一致的模块名;一次用首字母大写的模块名。结果发现 <span style="font-size: 14px;">son instanceof require(&#39;./parent&#39;) === false</span>

既然没报错的问题前面已经解决了,那么,现在看起来就是加载模块这个部分可能出问题了,那么问题到底是什么?我们怎么验证呢?

这个时候我看到了这么一句话 <span style="font-size: 14px;">var cachedModule = Module._cache[filename];</span> ,文件名是作为缓存的 <span style="font-size: 14px;">key</span> ,来吧,是时候看看 <span style="font-size: 14px;">Module._cache</span> 里存的模块 <span style="font-size: 14px;">key</span> 都是什么牛鬼蛇神了,打出来看看吧,于是我在 <span style="font-size: 14px;">test.js</span> 里最后面加了一句 <span style="font-size: 14px;">console.log(Object.keys(require.cache))</span> ,我们看看打出了什么结果

<span style="font-size: 14px;">false<br/>true<br/>[ &#39;/Users/admin/codes/test/index.js&#39;,<br/>  &#39;/Users/admin/codes/test/Parent.js&#39;,<br/>  &#39;/Users/admin/codes/test/parent.js&#39;,<br/>  &#39;/Users/admin/codes/test/son.js&#39; ]<br/></span>
登入後複製

真相已经呼之欲出了, <span style="font-size: 14px;">Module._cache</span> 里真的出现了两个 <span style="font-size: 14px;">[p|P]arent</span><span style="font-size: 14px;">macOS</span> 默认不区分大小写,所以她找到的其实是同一个文件;但 <span style="font-size: 14px;">node.js</span> 当真了,一看文件名不一样,就当成不同模块了),所以最后问题的关键就在于 <span style="font-size: 14px;">son.js</span> 里到底引用时用了哪个名字(上面我们用了首字母大写的 <span style="font-size: 14px;">require(&#39;./Parent.js&#39;)</span> ),这才导致了 <span style="font-size: 14px;">test.js</span> 认贼作父的梗。

如果我们改改 <span style="font-size: 14px;">son.js</span> ,把引用换成 <span style="font-size: 14px;">require(&#39;./parEND.js&#39;)</span> ,再次执行下 <span style="font-size: 14px;">test.js</span> 看看结果如何呢?

<span style="font-size: 14px;">false<br/>false<br/>[ &#39;/Users/haozuo/codes/test/index.js&#39;,<br/>  &#39;/Users/haozuo/codes/test/Parent.js&#39;,<br/>  &#39;/Users/haozuo/codes/test/parent.js&#39;,<br/>  &#39;/Users/haozuo/codes/test/son.js&#39;,<br/>  &#39;/Users/haozuo/codes/test/parENT.js&#39; ]<br/></span>
登入後複製

没有认贼作父了对不对?再看 <span style="font-size: 14px;">Module._cache</span> 里,原来是 <span style="font-size: 14px;">parENT.js</span> 也被当成一个单独的模块了。

所以,假設你的模組檔名有<span style="font-size: 14px;">n</span># 字符,理論上,在<span style="font-size: 14px;"># macOS</span> 大小寫不敏感的檔案系統裡,你能讓<span style="font-size: 14px;">#node.js</span> 將其弄出最大<span style="font-size: 14px;">2</span><span style="font-size: 14px;">n</span> 次方個快取來

#是不是很慘! ?還好 <span style="font-size: 14px;">macOS</span> 還是可以改成大小寫敏感的,格盤重裝系統;新分割區都行。

問題雖然不難,但探究問題的決心和想法還是重要的。

相關推薦:

教大家如何利用node.js建立子程式

#PHP與Node.js

#node.js 發布訂閱模式的方法

以上是關於node.js和macOS之間的故事的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

apache怎麼刪除多於的服務器名 apache怎麼刪除多於的服務器名 Apr 13, 2025 pm 01:09 PM

要從 Apache 中刪除多餘的 ServerName 指令,可以採取以下步驟:識別並刪除多餘的 ServerName 指令。重新啟動 Apache 使更改生效。檢查配置文件驗證更改。測試服務器確保問題已解決。

vscode需要什麼電腦配置 vscode需要什麼電腦配置 Apr 15, 2025 pm 09:48 PM

VS Code 系統要求:操作系統:Windows 10 及以上、macOS 10.12 及以上、Linux 發行版處理器:最低 1.6 GHz,推薦 2.0 GHz 及以上內存:最低 512 MB,推薦 4 GB 及以上存儲空間:最低 250 MB,推薦 1 GB 及以上其他要求:穩定網絡連接,Xorg/Wayland(Linux)

apache服務器怎麼重啟 apache服務器怎麼重啟 Apr 13, 2025 pm 01:12 PM

要重啟 Apache 服務器,請按照以下步驟操作:Linux/macOS:運行 sudo systemctl restart apache2。 Windows:運行 net stop Apache2.4 然後 net start Apache2.4。運行 netstat -a | findstr 80 檢查服務器狀態。

macos終端怎麼打開 macos終端怎麼打開 Apr 12, 2025 pm 05:39 PM

在 macOS 終端中打開文件:打開終端導航到文件目錄:cd ~/Desktop使用 open 命令:open test.txt其他選項:使用 -a 選項指定特定應用程序使用 -R 選項僅在 Finder 中顯示文件

vscode終端使用教程 vscode終端使用教程 Apr 15, 2025 pm 10:09 PM

vscode 內置終端是一個開發工具,允許在編輯器內運行命令和腳本,以簡化開發流程。如何使用 vscode 終端:通過快捷鍵 (Ctrl/Cmd ) 打開終端。輸入命令或運行腳本。使用熱鍵 (如 Ctrl L 清除終端)。更改工作目錄 (如 cd 命令)。高級功能包括調試模式、代碼片段自動補全和交互式命令歷史。

git怎麼查看倉庫地址 git怎麼查看倉庫地址 Apr 17, 2025 pm 01:54 PM

要查看 Git 倉庫地址,請執行以下步驟:1. 打開命令行並導航到倉庫目錄;2. 運行 "git remote -v" 命令;3. 查看輸出中的倉庫名稱及其相應的地址。

macos怎麼錄屏 macos怎麼錄屏 Apr 12, 2025 pm 05:33 PM

macOS 內置“屏幕錄製”應用程序,可用於錄製屏幕視頻。步驟: 1. 啟動應用程序;2. 選擇錄製範圍(整個屏幕或特定應用程序);3. 啟用/禁用麥克風;4. 點擊“錄製”按鈕;5. 點擊“停止”按鈕完成。錄製文件保存為 .mov 格式在“影片”文件夾中。

vscode 無法安裝擴展 vscode 無法安裝擴展 Apr 15, 2025 pm 07:18 PM

VS Code擴展安裝失敗的原因可能包括:網絡不穩定、權限不足、系統兼容性問題、VS Code版本過舊、殺毒軟件或防火牆干擾。通過檢查網絡連接、權限、日誌文件、更新VS Code、禁用安全軟件以及重啟VS Code或計算機,可以逐步排查和解決問題。

See all articles