이 기사는 node.js와 macOS 사이의 이야기를 여러분과 공유하기 위해 짧은 이야기를 기반으로 작성되었습니다. 모든 사람에게 도움이 되기를 바랍니다.
George G는 컴퓨터로 약간의 테스트를 했는데 결과는 예상과 많이 달랐습니다.
먼저 이 작은 테스트에 작성된 내용을 살펴보겠습니다.
총 3개의 파일, 전체 코드는 15줄을 초과하지 않습니다
<span style="font-size: 14px;">class Parent {}<br><br>module.exports = Parent<br></span>
<span style="font-size: 14px;">//加载时把模块文件名首字母大写了(不正确的)<br/>const Parent = require('./Parent')<br/><br/>class Son extends Parent {}<br/><br/>module.exports = Son<br/></span>
<span style="font-size: 14px;">//加载时把模块名首字母大写(不正确的)<br/>const ParentIncorrect = require('./Parent')<br/>//通过正确的模块文件名加载(正确)<br/>const Parent = require('./parent')<br/><br/>const Son = require('./son')<br/><br/>const ss = new Son()<br/><br/>//测试结果<br/>console.log(ss instanceof Parent) // false<br/>console.log(ss instanceof ParentIncorrect) // true<br/></span>
학생 George G가 다음 질문을 가지고 있습니다:
<code><span style="font-size: 14px;">son.js</span>
和 <span style="font-size: 14px;">test.js</span>
里都有错误的文件名(大小写问题)引用,为什么不报错?
测试结果,为什么 <span style="font-size: 14px;">ss instanceof ParentIncorrect === true</span>
?不报错我忍了,为什么还认贼作父,说自己是那个通过不正确名字加载出来的模块的instance?
如果同学你对上述问题已经了然于胸,恭喜你,文能提笔安天,武能上马定乾坤;上炕认识娘们,下炕认识鞋!
但如果你也不是很清楚为什么?那么好了,我有的说,你有的看。
其实断症(装逼范儿的debug)之法和中医看病也有相似指出,望、闻、问、切四招可以按需选取一二来寻求答案。
代码不多,看了一会,即便没有我的注释,相信仔细的同学也都发现真正的文件名和代码中引入时有出入的,那么这里肯定是有问题的,问题记住,我们继续
这个就算了,代码我也闻不出个什么鬼来
来吧,软件工程里很重要的一环,就是沟通,不见得是和遇到bug的同事,可能是自己,可能是QA,当然也可能是PM或者你的老板。你没问出自己想知道的问题;他没说清楚自己要回答的;都完蛋。。。。
那么我想知道什么呢?下面两件事作为debug的入口比较合理:
操作系统
运行环境 + 版本
你怎么测试的,命令行还是其他什么手段
答曰:macOS; <span style="font-size: 14px;">node.js > 8.0</span>
;命令行 <span style="font-size: 14px;">node test.js</span>
激动人心的深刻到来了,我要动手了。(为了完整的描述 <span style="font-size: 14px;">debug</span>
过程,我会假装这下面的所有事情我事先都是不知道的)
准备电脑,完毕
准备运行环境 <span style="font-size: 14px;">node.js > 9.3.0</span>
, 完毕
复刻代码,完毕
运行,日了狗,果然没报错,而且运行结果就是乔治G说的那样。
为了证明我没瞎,我又尝试在 <span style="font-size: 14px;">test.js</span>
里 <span style="font-size: 14px;">require</span>
了一个压根不存在的文件 <span style="font-size: 14px;">require('./nidayede')</span>
,运行代码。
还好这次报错了 <span style="font-size: 14px;">Error: Cannot find module './nidayede'</span>
son.js 및
<p>test. js<span style="font-size: 14px;"></span></p>
잘못된 파일 이름 참조(대소문자 문제)가 있는데 왜 오류가 보고되지 않나요? 🎜🎜🎜🎜테스트 결과, 왜 🎜🎜ss 인스턴스of ParentIncorlect === true🎜
🎜인가요? 오류를 보고하지 않는 것은 용납했지만 왜 여전히 내가 잘못된 이름으로 로드된 모듈의 인스턴스라고 생각했습니까? 🎜🎜🎜🎜동급생 여러분이 위의 문제를 이미 명확하게 이해했다면 축하합니다. 펜을 써서 하늘을 진정시킬 수 있고, 무술가가 말을 타고 세상은 강에 있으면 여자를 알 수 있고, 강에 있으면 신발도 알 수 있습니다! 🎜🎜🎜🎜그런데 왜 그런지 모르겠다면? 좋습니다. 그러면 저 중 일부는 이렇게 말할 것이고 여러분 중 일부는 이를 살펴보게 될 것입니다. 🎜🎜🎜🎜사실 진단 방법(가식적으로 디버깅)은 한의학의 방법과 비슷합니다. 보고, 냄새 맡고, 묻고, 촉진하는 네 가지 방법 중 하나 또는 두 가지를 선택하여 답을 찾을 수 있습니다. 필요에 따라. 🎜🎜🎜node.js > 8.0🎜
🎜; 🎜🎜debug🎜
🎜 과정을 완벽하게 설명하기 위해 아래 내용은 미리 모르는 척 할게요) 🎜🎜🎜🎜컴퓨터 준비, 완료🎜🎜🎜🎜 실행 환경 준비 🎜🎜node.js > 9.3.0🎜
🎜 , 완료 🎜🎜🎜🎜코드 복사 완료 🎜🎜🎜🎜 실행 중입니다. 놀랍습니다. 오류가 보고되지 않았으며 실행 중입니다. 결과는 George G 입니다. 🎜🎜🎜🎜내가 시각 장애인이 아니라는 것을 증명하기 위해 🎜🎜test.js🎜
🎜🎜🎜require🎜
에 전혀 존재하지 않는 파일을 추가하려고 했습니다. 🎜 code>🎜require('./nidayede')🎜🎜 , 코드를 실행하세요. 🎜🎜🎜🎜다행히 이번에 오류가 보고되었습니다. 🎜🎜오류: './nidayede' 모듈을 찾을 수 없습니다🎜
🎜, 미친 건 아닙니다. 그것은 정말 기쁨입니다. 🎜🎜🎜🎜첫 번째 질문이 나옵니다🎜🎜운영체제와 관련이 있나요? <code><span style="font-size: 14px;">windows</span>
试试,果然,到了 <code><span style="font-size: 14px;">windows</span> 上,大小写问题就是个问题了, <span style="font-size: 14px;">Error: Cannot find module './Parent'</span>
。
那么 <span style="font-size: 14px;">macOS</span>
到底在干什么?连个大小写都分不出来么?于是赶紧 <span style="font-size: 14px;">google</span>
(别问我为什么不baidu)
原来人家牛逼的 <span style="font-size: 14px;">OS X</span>
默认用了 <span style="font-size: 14px;">case-insensitive</span>
的文件系统( 详细文档 )。
but why?这么反人类的设计到底是为了什么?
更多解释, 来,走你
所以,这就是你不报错的理由?(对 <span style="font-size: 14px;">node.js</span>
指责道),但这就是全部真相了。
但事情没完
依稀有听过 <span style="font-size: 14px;">node.js</span>
里有什么缓存,是那个东西引起的么?于是抱着试试看的心情,我把 <span style="font-size: 14px;">const ParentIncorrect = require('./Parent')</span>
和 <span style="font-size: 14px;">const Parent = require('./parent')</span>
换了下位置,心想,这样最先按照正确的名字加载,会不会就对了呢?
果然, 还是不对 。靠猜和装逼是不能够真正解决问题的
那比比 <span style="font-size: 14px;">ParentIncorrect</span>
和 <span style="font-size: 14px;">Parent</span>
呢?于是我写了 <span style="font-size: 14px;">console.log(ParentIncorrect === Parent)</span>
,结果为 <span style="font-size: 14px;">false</span>
。所以他俩还真的不是同一个东西,那么说明问题可能在引入的部分喽?
于是一个装逼看 <span style="font-size: 14px;">node.js</span>
源码的想法诞生了(其实不看,问题最终也能想明白)。 日了狗,怀着忐忑的心情,终于 <span style="font-size: 14px;">clone</span>
了一把 <span style="font-size: 14px;">node.js</span>
源码(花了好久,真tm慢)
来,我们一起进入神秘的 <span style="font-size: 14px;">node.js</span>
源码世界。既然我们的问题是有关 <span style="font-size: 14px;">require</span>
的,那就从她开始吧,不过找到 <span style="font-size: 14px;">require</span>
定义的过程需要点耐心,这里不详述,只说查找的顺序吧
<span style="font-size: 14px;">src/node_main.cc => src/node.cc => lib/internal/bootstrap_node.js => lib/module.js</span>
找到咯,就是这个 <span style="font-size: 14px;">lib/module.js</span>
windows에서 다시 시도해 보겠습니다. 물론입니다.
<p>windows<span style="font-size: 14px;"></span></p>
에서 케이스 문제가 문제입니다. 🎜 오류: 불가능 모듈 './Parent'🎜
🎜를 찾으세요. 🎜🎜🎜🎜그럼 🎜🎜macOS🎜
🎜는 무엇을 하고 있나요? 대문자와 소문자의 차이를 구분할 수 없나요? 너무 빨리 🎜🎜google🎜
🎜 (baidu가 아닌 이유는 묻지 마세요)🎜🎜🎜 🎜🎜🎜멋진 🎜🎜OS X🎜
🎜은 기본적으로 🎜🎜대소문자를 구분하지 않는🎜
🎜 파일 시스템을 사용하는 것으로 나타났습니다(자세한 문서). 🎜🎜🎜🎜근데 왜? 그러한 반인간적인 디자인의 목적은 무엇입니까? 🎜🎜🎜 🎜🎜🎜자세한 설명은 오고 가세요🎜🎜🎜🎜그래서 이것이 바로 여러분이 하지 않는 이유입니다. 오류 보고 이유는? (🎜🎜node.js🎜
🎜을 비난) 하지만 그게 전부 진실입니다. 🎜🎜🎜🎜아직 끝나지 않았습니다 🎜🎜🎜node.js🎜
🎜에 캐시가 있는 걸까요? 그래서 시도해 보고 싶은 마음에 🎜🎜const ParentIncorlect = require('./Parent')🎜
🎜 및 🎜🎜const Parent = require('./parent')를 입력했습니다. )🎜
🎜 위치를 바꿔서 먼저 올바른 이름으로 로드하는 것이 맞을까? 🎜🎜🎜🎜물론이죠. 아직도 정확하지 않습니다. 추측하고 가장하는 것만으로는 문제를 해결할 수 없습니다🎜🎜🎜🎜Bibi 🎜🎜ParentIncorlect🎜
🎜와 🎜🎜Parent🎜
🎜는 어떻습니까? 그래서 🎜🎜console.log(ParentIncordirect === Parent)🎜
🎜 라고 썼는데, 결과는 🎜🎜false🎜
🎜 였습니다. 그러면 실제로는 같은 것이 아니므로 도입 부분에 문제가 있는 것이 아닐까요? 🎜🎜🎜🎜그래서 🎜🎜node.js🎜
🎜의 소스코드를 살펴보자는 아이디어가 탄생했습니다(사실 보지 않아도 문제는 결국 알 수 있습니다). 긴 하루를 보낸 후, 떨리는 마음으로 마침내 🎜🎜clone🎜
🎜 소수의 🎜🎜node.js🎜
🎜 소스 코드를 얻었습니다(오랜 시간이 걸렸습니다). 시간이 너무 느려) 🎜🎜🎜🎜자, 신비한 🎜🎜node.js🎜
🎜 소스코드 세계로 함께 들어가보자. 우리의 질문은 🎜🎜require🎜
🎜에 관한 것이므로 그녀부터 시작하겠습니다. 하지만 🎜🎜require🎜
🎜의 정의를 찾는 과정에는 약간의 인내심이 필요합니다. 자세한 내용은 검색 순서에 대해서만 이야기하겠습니다🎜src/node_main.cc => src/node.cc => lib/internal/bootstrap_node.js => 🎜 code> 🎜🎜🎜 찾았습니다. 바로 이것이 🎜🎜lib/module.js🎜
🎜입니다. 요점을 살펴보겠습니다. 🎜🎜🎜🎜lib/module.js => 🎜<span style="max-width:90%">Module.prototype.require = function(path) {<br/> assert(path, 'missing path');<br/> assert(typeof path === 'string', 'path must be a string');<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('Module._load REQUEST %s parent: %s', 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('load native module %s', 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 = '.';<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>
了,要不我们先停一停?
好了,分析问题的关键在于不忘初心,虽然到目前为止我们前进的比较顺利,也很爽对不对?。但我们的此行的目的并不是爽,好像是有个什么疑惑哦!于是,我们再次梳理下问题:
<code><span style="font-size: 14px;">son.js</span> 里用首字母大写(不正确)的模块名引用了 <span style="font-size: 14px;">parent.js</span>
<span style="font-size: 14px;">test.js</span>
里,引用了两次 <span style="font-size: 14px;">parent.js</span>
,一次用完全一致的模块名;一次用首字母大写的模块名。结果发现 <span style="font-size: 14px;">son instanceof require('./parent') === 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/>[ '/Users/admin/codes/test/index.js',<br/> '/Users/admin/codes/test/Parent.js',<br/> '/Users/admin/codes/test/parent.js',<br/> '/Users/admin/codes/test/son.js' ]<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>
当真了,一看文件名不一样,就当成不同模块了),所以最后问题的关键就在于 <code><span style="font-size: 14px;">son.js</span> 里到底引用时用了哪个名字(上面我们用了首字母大写的 <span style="font-size: 14px;">require('./Parent.js')</span>
),这才导致了 <span style="font-size: 14px;">test.js</span>
认贼作父的梗。
如果我们改改 <code><span style="font-size: 14px;">son.js</span> ,把引用换成 <span style="font-size: 14px;">require('./parEND.js')</span>
,再次执行下 <span style="font-size: 14px;">test.js</span>
看看结果如何呢?
<span style="font-size: 14px;">false<br/>false<br/>[ '/Users/haozuo/codes/test/index.js',<br/> '/Users/haozuo/codes/test/Parent.js',<br/> '/Users/haozuo/codes/test/parent.js',<br/> '/Users/haozuo/codes/test/son.js',<br/> '/Users/haozuo/codes/test/parENT.js' ]<br/></span>
没有认贼作父了对不对?再看 <span style="font-size: 14px;">Module._cache</span>
里,原来是 <span style="font-size: 14px;">parENT.js</span>
也被当成一个单独的模块了。
따라서 모듈 파일 이름에 이론적으로 <code><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>
的 <code><span style="font-size: 14px;">n</span> 次方个缓存来
是不是很惨!?还好 <span style="font-size: 14px;">macOS</span>
macOS 대소문자를 구분하지 않는 파일 시스템에서
<p>n<span style="font-size: 14px;"></span></p>
문자가 있다고 가정하면 <p>node.js<span style="font-size: 14px;"></span></p>
최대의 <p>2<a href="http://www.php.cn/js-tutorial-382351.html" target="_self" style="font-size: 14px; text-decoration: underline;"></a></p>
를 n
전력으로 만드는 것은 비참하지 않나요? 캐시를 가지고 ! ? 다행히
<p>macOS<a href="http://www.php.cn/js-tutorial-379690.html" target="_self" style="font-size: 14px; text-decoration: underline;"></a></p>
는 여전히 대소문자 구분으로 변경될 수 있으며, 그리드 디스크에 시스템을 다시 설치하거나 새 파티션을 생성하면 됩니다. 🎜문제가 어렵지는 않지만 문제를 탐구하려는 결단력과 아이디어는 여전히 중요합니다. 🎜🎜🎜🎜관련 권장사항: 🎜🎜🎜🎜🎜node.js를 사용하여 하위 프로세스를 만드는 방법을 가르쳐주세요🎜🎜🎜🎜🎜🎜PHP 및 Node.js🎜🎜🎜🎜🎜🎜node.js 게시-구독 모드 방법 🎜🎜🎜위 내용은 node.js와 macOS 사이의 이야기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!