Heim > Web-Frontend > js-Tutorial > Eine ausführliche Einführung in JavaScript-Referenzen in Node.js

Eine ausführliche Einführung in JavaScript-Referenzen in Node.js

黄舟
Freigeben: 2017-04-24 09:14:50
Original
1101 Leute haben es durchsucht

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

Starten des Projekts zu empfehlen, das nach der Bearbeitung des Codes automatisch neu gestartet werden kann. Im Vergleich zu PHP ist es jedoch immer noch nicht praktisch, da nach dem Neustart von Node.js der vorherige Kontext verloren geht.

Obwohl Sie den Datenverlust während des Neustartvorgangs reduzieren können, indem Sie die

Sitzungsdaten in der Datenbank oder im Cache speichern, gibt es in der Produktion keine Möglichkeit, die Neustartlücke zu aktualisieren des Codes Anfragen verarbeiten (PHP kann das, und Node.js hatte zu diesem Zeitpunkt keine Cluster). Aufgrund dieses Problems und der Tatsache, dass ich von PHP auf Node.js umgestiegen bin, begann ich darüber nachzudenken, ob es eine Möglichkeit gibt, den Node.js-Code ohne Neustart im laufenden Betrieb zu aktualisieren.

Zuerst habe ich mich auf das Modul

require konzentriert. Die Idee ist einfach, denn jedes in Node.js eingeführte Modul wird über die Methode require geladen. Also begann ich darüber nachzudenken, ob require nach der Aktualisierung des Codes erneut erforderlich sein kann. Versuchen Sie Folgendes:

a.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);
Nach dem Login kopieren

b.js

exports.num = 1024;
Nach dem Login kopieren
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);
Nach dem Login kopieren
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')

Während des eigentlichen Entwicklungsprozesses traten jedoch schnell Probleme auf. Der Code, den wir hoffen, könnte wie folgt aussehen:

web.js

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);
Nach dem Login kopieren
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);
Nach dem Login kopieren
Ä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;
 }
Nach dem Login kopieren
und in JavaScript:

var obj = { name: &#39;Alan&#39; };
function test1(obj) {
 obj = { hello: &#39;world&#39; }; // 试图修改外部obj
 }
test1(obj);
 console.log(obj); // { name: &#39;Alan&#39; } // 并没有修改①
function test2(obj) {
 obj.name = &#39;world&#39;; // 根据该对象修改其上的属性
 }
test2(obj);
 console.log(obj); // { name: &#39;world&#39; } // 修改成功②
Nach dem Login kopieren
Nach dem Login kopieren
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

-Variable kopiert, also ein Wert übergeben. Gemäß ② ist ersichtlich, dass die kopierte Variable eine „Referenz“ ist, die auf die Objekteigenschaften zugreifen kann (anders als herkömmliche C++-Referenzen sind die unten genannten JavaScript-Referenzen alle solche speziellen Referenzen). Hier muss eine komplizierte Schlussfolgerung gezogen werden: Javascript wird vollständig als Wert übergeben und während des Übertragungsprozesses wird eine neue Referenz auf das Objekt kopiert.

Um diese ziemlich unangenehme Schlussfolgerung zu verstehen, schauen wir uns einen Code an:

var obj = { name: &#39;Alan&#39; };
function test1(obj) {
 obj = { hello: &#39;world&#39; }; // 试图修改外部obj
 }
test1(obj);
 console.log(obj); // { name: &#39;Alan&#39; } // 并没有修改①
function test2(obj) {
 obj.name = &#39;world&#39;; // 根据该对象修改其上的属性
 }
test2(obj);
 console.log(obj); // { name: &#39;world&#39; } // 修改成功②
Nach dem Login kopieren
Nach dem Login kopieren

通过这个例子我们可以看到,data 虽然像一个引用一样指向了 obj.data,并且通过 data 可以访问到 obj.data 上的属性。但是由于 JavaScript 值传递的特性直接修改 data = xxx 并不会使得 obj.data = xxx。

打个比方最初设置 var data = obj.data 的时候,内存中的情况大概是:

|   Addr   |  内容  | 
|----------|-------- 
| obj.data |  内存1 |
| data | 内存1 |
Nach dem Login kopieren

所以通过 data.xx 可以修改 obj.data 的内存1。

然后设置 data = xxx,由于 data 是拷贝的一个新的值,只是这个值是一个引用(指向内存1)罢了。让它等于另外一个对象就好比:

|   Addr   |  内容  |
 |----------|-------- 
| obj.data |  内存1 || data | 内存2 |
Nach dem Login kopieren

让 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;
 }
Nach dem Login kopieren

所以很自然的:

console.log(module.exports === exports); // true
// 所以 exports 所操作的就是 module.exports
Nach dem Login kopieren

Node.js 中的 exports 就是拷贝的一份 module.exports 的引用。通过 exports 可以修改Node.js 当前文件导出的属性,但是不能修改当前模块本身。通过 module.exports 才可以修改到其本身。表现上来说:

exports = 1; // 无效
module.exports = 1; // 有效
Nach dem Login kopieren

这是二者表现上的区别,其他方面用起来都没有差别。所以你现在应该知道写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);
Nach dem Login kopieren

按照开始的结论我们可以一步步的来看这个问题:

var a = {n: 1};  // 引用a指向内存1{n:1}
var b = a; // 引用b => a => { n:1 }
Nach dem Login kopieren

内部结构:

|   Addr  |     内容     | |---------|-------------|
| a | 内存1 {n:1} | | b | 内存1 |
Nach dem Login kopieren

继续往下看:

a.x = a = {n: 2}; // (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}
Nach dem Login kopieren

a 虽然是引用,但是 JavaScript 是值传的这个引用,所以被修改不影响原本的地方。

| Addr | 内容 | |-----------|-----------------------|
| 1) a | 内存2({n:2}) | | 2) 内存1.x | 内存2({n:2}) |
| 3) b | 内存1({n:1, x:内存2}) |
Nach dem Login kopieren

所以最后的结果

a.x 即(内存2).x ==> {n: 2}.x ==> undefined
b.x 即(内存1).x ==> 内存2 ==> {n: 2}
Nach dem Login kopieren

总结

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!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage