Heim >Web-Frontend >js-Tutorial >Eine ausführliche Einführung in JavaScript-Referenzen in Node.js
Dieser Artikel stellt hauptsächlich die relevanten Informationen zum Zitieren von in js in JavaScript vor. Freunde in Not können darauf verweisen . Weiter
Als ich in den frühen Tagen (2011-2012) Node.js lernte, hatten einige Leute Bedenken, dass sie Node.js nach der Bearbeitung neu starten mussten Code (PHP erfordert diesen Prozess nicht), daher begannen Freunde in der Community, die Verwendung des Node-Supervisor-Moduls zum Obwohl Sie den Datenverlust während des Neustartvorgangs reduzieren können, indem Sie die Zuerst habe ich mich auf das Modula.js
var express = require('express'); var b = require('./b.js'); var app = express(); app.get('/', function (req, res) { b = require('./b.js'); res.send(b.num); }); app.listen(3000);
b.js
exports.num = 1024;Zwei JS-Dateien geschrieben Beginnen Sie anschließend mit a.js, aktualisieren Sie die Seite und die 1024 in b.js werden ausgegeben, und ändern Sie dann den in die b.js-Datei exportierten Wert, beispielsweise in 2048. Aktualisieren Sie die Seite erneut und es wird immer noch die ursprüngliche 1024 angezeigt. Durch die erneute Ausführung von require wird der Code nicht aktualisiert. Nachdem require den Code während der Ausführung geladen hat, werden die vom Modul exportierten Daten in require.cache abgelegt. require.cache ist ein { }
Objekt, mit dem absoluten Pfad des Moduls als Schlüssel und den detaillierten Daten des Moduls als Wert. Also fing ich an, Folgendes auszuprobieren:
a.js
var path = require('path'); var express = require('express'); var b = require('./b.js'); var app = express(); app.get('/', function (req, res) { if (true) { // 检查文件是否修改 flush(); } res.send(b.num); }); function flush() { delete require.cache[path.join(dirname, './b.js')]; b = require('./b.js'); } app.listen(3000);Bevor Sie es erneut anfordern, bereinigen Sie den Cache des Moduls bei Anforderung und verwenden Sie es vorher Methode wurde erneut getestet. Es stellt sich heraus, dass der Code von b.js erfolgreich aktualisiert werden kann und der neu geänderte Wert ausgegeben wird. Nachdem ich diesen Punkt verstanden hatte, wollte ich dieses Prinzip nutzen, um eine Hot-Update-Version von Node-Supervisor ohne Neustart zu implementieren. Erwägen Sie beim Einkapseln des Moduls aus sentimentalen Gründen die Bereitstellung einer
Funktion ähnlich wie include in PHP, um „require“ zum Einführen eines Moduls zu ersetzen. Tatsächlich wird require immer noch intern zum Laden verwendet. Am Beispiel von b.js wird die ursprüngliche Schreibmethode in geändert. Nachdem die Datei b.js aktualisiert wurde, kann das Include automatisch intern aktualisiert werden, sodass die Außenwelt den neuesten Code erhalten kann. var b = include(‘./b')
var include = require('./include'); var express = require('express'); var b = include('./b.js'); var app = express(); app.get('/', function (req, res) { res.send(b.num); }); app.listen(3000);Bei der Kapselung von include gemäß diesem Ziel haben wir jedoch ein Problem festgestellt. Unabhängig davon, wie wir es in include.js implementieren, können wir die neue b.num nicht wie am Anfang erhalten. Beim Vergleich des ursprünglichen Codes stellen wir fest, dass das Problem im fehlenden b = xx liegt. Mit anderen Worten, es kann so geschrieben werden: web.js
var include = require('./include'); var express = require('express'); var app = express(); app.get('/', function (req, res) { var b = include('./b.js'); res.send(b.num); }); app.listen(3000);Ändern Sie es wie folgt, um sicherzustellen, dass der neueste Code jedes Mal korrekt aktualisiert werden kann, ohne die Instanz neu zu starten. . Interessierte Leser können sich mit der Implementierung dieses Include befassen, da diese Technik nicht weit verbreitet ist und nicht sehr elegant zu schreiben ist [1]. .
Der Unterschied zwischen JavaScript-Referenzen und traditionellen Referenzen
Um dieses Problem zu diskutieren, müssen wir zunächst verstehen, wie JavaScript-Referenzen in anderen Sprachen verwendet werden . Der Unterschied besteht darin, dass in C++ Referenzen externe Werte direkt ändern können:#include using namespace std; void test(int &p) // 引用传递 { p = 2048; } int main() { int a = 1024; int &p = a; // 设置引用p指向a test(p); // 调用函数 cout << "p: " << p << endl; // 2048 cout << "a: " << a << endl; // 2048 return 0; }und in JavaScript:
var obj = { name: 'Alan' }; function test1(obj) { obj = { hello: 'world' }; // 试图修改外部obj } test1(obj); console.log(obj); // { name: 'Alan' } // 并没有修改① function test2(obj) { obj.name = 'world'; // 根据该对象修改其上的属性 } test2(obj); console.log(obj); // { name: 'world' } // 修改成功②Wir finden das im Gegensatz zu C++, gemäß dem obigen Code ① wir Das kann man in JavaScript sehen Anstatt eine Referenz zu übergeben, wird eine neue Um diese ziemlich unangenehme Schlussfolgerung zu verstehen, schauen wir uns einen Code an:
var obj = { name: 'Alan' }; function test1(obj) { obj = { hello: 'world' }; // 试图修改外部obj } test1(obj); console.log(obj); // { name: 'Alan' } // 并没有修改① function test2(obj) { obj.name = 'world'; // 根据该对象修改其上的属性 } test2(obj); console.log(obj); // { name: 'world' } // 修改成功②
通过这个例子我们可以看到,data 虽然像一个引用一样指向了 obj.data,并且通过 data 可以访问到 obj.data 上的属性。但是由于 JavaScript 值传递的特性直接修改 data = xxx 并不会使得 obj.data = xxx。
打个比方最初设置 var data = obj.data 的时候,内存中的情况大概是:
| Addr | 内容 | |----------|-------- | obj.data | 内存1 | | data | 内存1 |
所以通过 data.xx 可以修改 obj.data 的内存1。
然后设置 data = xxx,由于 data 是拷贝的一个新的值,只是这个值是一个引用(指向内存1)罢了。让它等于另外一个对象就好比:
| Addr | 内容 | |----------|-------- | obj.data | 内存1 || data | 内存2 |
让 data 指向了新的一块内存2。
如果是传统的引用(如上文中提到的 C++ 的引用),那么 obj.data 本身会变成新的内存2,但 JavaScript 中均是值传递,对象在传递的过程中拷贝了一份新的引用。所以这个新拷贝的变量被改变并不影响原本的对象。
Node.js 中的 module.exports 与 exports
上述例子中的 obj.data 与 data 的关系,就是 Node.js 中的 module.exports 与 exports 之间的关系。让我们来看看 Node.js 中 require 一个文件时的实际结构:
function require(...) { var module = { exports: {} }; ((module, exports) => { // Node.js 中文件外部其实被包了一层自执行的函数 // 这中间是你模块内部的代码. function some_func() {}; exports = some_func; // 这样赋值,exports便不再指向module.exports // 而module.exports依旧是{} module.exports = some_func; // 这样设置才能修改到原本的exports })(module, module.exports); return module.exports; }
所以很自然的:
console.log(module.exports === exports); // true // 所以 exports 所操作的就是 module.exports
Node.js 中的 exports 就是拷贝的一份 module.exports 的引用。通过 exports 可以修改Node.js 当前文件导出的属性,但是不能修改当前模块本身。通过 module.exports 才可以修改到其本身。表现上来说:
exports = 1; // 无效 module.exports = 1; // 有效
这是二者表现上的区别,其他方面用起来都没有差别。所以你现在应该知道写module.exports.xx = xxx; 的人其实是多写了一个module.。
更复杂的例子
为了再练习一下,我们在来看一个比较复杂的例子:
var a = {n: 1}; var b = a; a.x = a = {n: 2}; console.log(a.x); console.log(b.x);
按照开始的结论我们可以一步步的来看这个问题:
var a = {n: 1}; // 引用a指向内存1{n:1} var b = a; // 引用b => a => { n:1 }
内部结构:
| Addr | 内容 | |---------|-------------| | a | 内存1 {n:1} | | b | 内存1 |
继续往下看:
a.x = a = {n: 2}; // (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}
a 虽然是引用,但是 JavaScript 是值传的这个引用,所以被修改不影响原本的地方。
| Addr | 内容 | |-----------|-----------------------| | 1) a | 内存2({n:2}) | | 2) 内存1.x | 内存2({n:2}) | | 3) b | 内存1({n:1, x:内存2}) |
所以最后的结果
a.x 即(内存2).x ==> {n: 2}.x ==> undefined b.x 即(内存1).x ==> 内存2 ==> {n: 2}
总结
JavaScrip t中没有引用传递,只有值传递。对象(引用类型)的传递只是拷贝一个新的引用,这个新的引用可以访问原本对象上的属性,但是这个新的引用本身是放在另外一个格子上的值,直接往这个格子赋新的值,并不会影响原本的对象。本文开头所讨论的 Node.js 热更新时碰到的也是这个问题,区别是对象本身改变了,而原本拷贝出来的引用还指向旧的内存,所以通过旧的引用调用不到新的方法。
Node.js 并没有对 JavaScript 施加黑魔法,其中的引用问题依旧是 JavaScript 的内容。如 module.exports 与 exports 这样隐藏了一些细节容易使人误会,本质还是 JavaScript 的问题。
注[1]:
老实说,模块在函数内声明有点谭浩强的感觉。
把 b = include(xxx) 写在调用内部,还可以通过设置成中间件绑定在公共地方来写。
除了写在调用内部,也可以导出一个工厂函数,每次使用时 b().num 一下调用也可以。
还可以通过中间件的形式绑定在框架的公用对象上(如:ctx.b = include(xxx))。
要实现这样的热更新必须在架构上就要严格避免旧代码被引用的可能性,否则很容易写出内存泄漏的代码。
Das obige ist der detaillierte Inhalt vonEine ausführliche Einführung in JavaScript-Referenzen in Node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!