Heim > Web-Frontend > js-Tutorial > JavaScript spricht auch über Speicheroptimierung_Javascript-Fähigkeiten

JavaScript spricht auch über Speicheroptimierung_Javascript-Fähigkeiten

WBOY
Freigeben: 2016-05-16 16:45:59
Original
1038 Leute haben es durchsucht

Im Vergleich zu C/C konnten wir durch die von uns verwendete Speicherverarbeitung von JavaScript dem Schreiben der Geschäftslogik während der Entwicklung mehr Aufmerksamkeit schenken. Angesichts der kontinuierlichen Komplexität des Geschäfts und der Entwicklung von Single-Page-Anwendungen, mobilen HTML5-Anwendungen, Node.js-Programmen usw. sind jedoch Phänomene wie Verzögerungen und Speicherüberlauf, die durch Speicherprobleme in JavaScript verursacht werden, keine Unbekannten mehr.

In diesem Artikel werden die Speichernutzung und -optimierung auf der Sprachebene von JavaScript erläutert. Von den Aspekten, mit denen jeder vertraut ist oder von denen er ein wenig gehört hat, bis hin zu den Bereichen, die den meisten Menschen nicht auffallen, werden wir sie einzeln analysieren.

1. Speicherverwaltung auf Sprachebene

1.1 Geltungsbereich

Der Bereich ist ein sehr wichtiger Betriebsmechanismus in der JavaScript-Programmierung. Bei der synchronen JavaScript-Programmierung erregt er nicht die volle Aufmerksamkeit von Anfängern, aber bei der asynchronen Programmierung sind gute Fähigkeiten zur Bereichskontrolle der Schlüssel für die JavaScript-Entwicklung. Darüber hinaus spielt der Bereich eine entscheidende Rolle bei der JavaScript-Speicherverwaltung.

In JavaScript können die Bereiche durch Funktionsaufrufe, mit Anweisungen und globalem Bereich gebildet werden.

Nehmen Sie den folgenden Code als Beispiel:

Kopieren Sie den Code Der Code lautet wie folgt:

var foo = function() {
var local = {};
};
foo();
console.log(local); //=> undefiniert

var bar = function() {
local = {};
};
bar();
console.log(local); {}

Hier definieren wir die Funktionen foo() und bar(). Ihre Absicht ist es, eine Variable mit dem Namen local zu definieren. Aber das Endergebnis war völlig anders.

In der Funktion foo() verwenden wir die var-Anweisung, um eine lokale Variable zu deklarieren und zu definieren. Da innerhalb des Funktionskörpers ein Bereich gebildet wird, wird diese Variable im Bereich definiert. Darüber hinaus führt der Hauptteil der Funktion foo() keine Verarbeitung zur Bereichserweiterung durch, sodass nach der Ausführung der Funktion auch die lokale Variable zerstört wird. Auf die Variable kann im äußeren Bereich nicht zugegriffen werden.

In der Funktion bar() wird die lokale Variable nicht mit der var-Anweisung deklariert, sondern local wird direkt als globale Variable definiert. Daher kann der äußere Bereich auf diese Variable zugreifen.

Code kopieren Der Code lautet wie folgt:

local = {};
/ / hier Die Definition entspricht
global.local = {};


1.2 Scope Chain

In der JavaScript-Programmierung werden Sie dies tun Wenn Sie auf ein Szenario mit mehreren Funktionsverschachtelungsebenen stoßen, handelt es sich auf jeden Fall um die Darstellung einer typischen Bereichskette.

Wie im folgenden Code gezeigt:

Kopieren Sie den CodeDer Code lautet wie folgt:

Funktion foo( ) {
var val = 'hello';

Funktion bar() {
Funktion baz() {
global.val = 'world;'
}
baz ();
console.log(val); //=> 🎜>

Basierend auf der vorherigen Beschreibung des Bereichs denken Sie vielleicht, dass das vom Code hier angezeigte Ergebnis „Welt“ ist, aber das tatsächliche Ergebnis ist „Hallo“. Viele Anfänger werden hier verwirrt sein, also schauen wir uns an, wie dieser Code funktioniert.

Da in JavaScript die Suche nach Variablenbezeichnern vom aktuellen Bereich aus beginnt und nach außen bis zum globalen Bereich sucht. Daher kann der Zugriff auf Variablen im JavaScript-Code nur nach außen erfolgen, nicht umgekehrt.

Die Ausführung der baz()-Funktion definiert eine globale Variable val im globalen Bereich. In der Funktion bar () erfolgt beim Zugriff auf den Bezeichner val das Suchprinzip von innen nach außen: Wenn es nicht im Bereich der Funktion bar gefunden wird, wechselt es zur oberen Ebene, dh zum Bereich von foo ()-Funktion. Suche im Bereich.

Der entscheidende Punkt, der alle verwirrt, ist jedoch hier: Dieser Identifikatorzugriff findet eine passende Variable im Bereich der foo()-Funktion und sucht nicht weiter nach außen, also in der baz()-Funktion Das definierte Global Der Variablenwert hat keinen Einfluss auf diesen Variablenzugriff.

JavaScript spricht auch über Speicheroptimierung_Javascript-Fähigkeiten
1.3 Schließung

Wir wissen, dass die Suche nach Bezeichnern in JavaScript dem Inside-Out-Prinzip folgt. Angesichts der Komplexität der Geschäftslogik reicht eine einzelne Liefersequenz jedoch bei weitem nicht aus, um den zunehmenden neuen Anforderungen gerecht zu werden.

Schauen wir uns zunächst den folgenden Code an:

Kopieren Sie den Code


Der Code lautet wie folgt:

function foo() {
var local = 'Hello';
return function() {
return local;
};
}
var bar = foo();
console.log(bar()); //=> Hallo

Die hier gezeigte Technologie, die es dem äußeren Zielfernrohr ermöglicht, auf das innere Zielfernrohr zuzugreifen, ist der Verschluss (Closure). Durch den Einsatz von Funktionen höherer Ordnung wurde der Umfang der Funktion foo() „erweitert“.

Die Funktion foo() gibt eine anonyme Funktion zurück, die im Bereich der Funktion foo() existiert, sodass Sie auf die lokale Variable im Bereich der Funktion foo() zugreifen und deren Referenz speichern können. Da diese Funktion die lokale Variable direkt zurückgibt, kann die Funktion bar() direkt im äußeren Bereich ausgeführt werden, um die lokale Variable abzurufen.

Closure ist eine erweiterte Funktion von JavaScript. Wir können damit komplexere Effekte erzielen, um unterschiedliche Anforderungen zu erfüllen. Es ist jedoch zu beachten, dass die Variablen im Gültigkeitsbereich nach der Ausführung der Funktion nicht unbedingt zerstört werden, bis alle Verweise auf die internen Variablen freigegeben werden, da die Funktion mit internen Variablenreferenzen aus der Funktion entfernt wird. Daher kann die Anwendung von Verschlüssen leicht dazu führen, dass der Speicher nicht freigegeben werden kann.

2. JavaScript-Speicherrecyclingmechanismus

Hier werde ich die von Google eingeführte V8-Engine, die von Chrome und Node.js verwendet wird, als Beispiel nehmen, um den Speicherrecyclingmechanismus von JavaScript kurz vorzustellen. Für detailliertere Inhalte können Sie das Buch „Speaking in“ meines guten Freundes Pu Ling kaufen Einfache und tiefe Sprache „Node.js“ zum Erlernen, in der das Kapitel „Speichersteuerung“ eine recht ausführliche Einführung enthält.

In V8 wird allen JavaScript-Objekten Speicher über den „Heap“ zugewiesen.

JavaScript spricht auch über Speicheroptimierung_Javascript-Fähigkeiten

Wenn wir eine Variable im Code deklarieren und einen Wert zuweisen, weist V8 der Variablen einen Teil des Heap-Speichers zu. Wenn der zugewiesene Speicher nicht ausreicht, um diese Variable zu speichern, beantragt V8 weiterhin Speicher, bis die Heap-Größe die obere Speichergrenze von V8 erreicht. Standardmäßig beträgt die Obergrenze der Heap-Speichergröße von V8 1464 MB in 64-Bit-Systemen und 732 MB in 32-Bit-Systemen, was etwa 1,4 GB und 0,7 GB entspricht.

Darüber hinaus verwaltet V8 JavaScript-Objekte im Heap-Speicher nach Generation: neue Generation und alte Generation. Die neue Generation bezieht sich auf JavaScript-Objekte mit einem kurzen Lebenszyklus, z. B. temporäre Variablen, Zeichenfolgen usw., während sich die alte Generation auf Objekte bezieht, die mehrere Garbage Collections überstanden haben und einen langen Lebenszyklus haben, z. B. Hauptcontroller und Serverobjekte , usw.

Garbage-Collection-Algorithmen waren schon immer ein wichtiger Teil der Entwicklung von Programmiersprachen, und die in V8 verwendeten Garbage-Collection-Algorithmen umfassen hauptsächlich Folgendes:

1.Scavange-Algorithmus: Speicherplatzverwaltung durch Kopieren, hauptsächlich für den Speicherplatz der neuen Generation verwendet
2.Mark-Sweep-Algorithmus und Mark-Compact-Algorithmus: Organisieren und Organisieren des Heap-Speichers durch Markieren Recycling ist Wird hauptsächlich zur Inspektion und zum Recycling von Gegenständen der alten Generation verwendet.


PS: Eine detailliertere V8-Garbage-Collection-Implementierung kann durch das Lesen relevanter Bücher, Dokumente und Quellcodes erlernt werden.

Sehen wir uns an, welche Objekte die JavaScript-Engine unter welchen Umständen recycelt.

2.1 Geltungsbereich und Referenz

Anfänger glauben oft fälschlicherweise, dass nach Abschluss der Ausführung der Funktion das in der Funktion deklarierte Objekt zerstört wird. Tatsächlich ist dieses Verständnis jedoch nicht streng und umfassend und kann leicht zu Verwirrung führen.

Referenz ist ein sehr wichtiger Mechanismus in der JavaScript-Programmierung, aber das Seltsame ist, dass die meisten Entwickler ihm keine Beachtung schenken oder ihn nicht einmal verstehen. Referenz bezieht sich auf die abstrakte Beziehung des „Codezugriffs auf Objekte“. Sie ähnelt in gewisser Weise C/C-Zeigern, ist aber nicht dasselbe. Referenzen sind auch der wichtigste Mechanismus für die Speicherbereinigung durch die JavaScript-Engine.

Nehmen Sie den folgenden Code als Beispiel:

Kopieren Sie den Code Der Code lautet wie folgt:

// .....
var val = 'hello world';
function foo() {
return function() {
return val;
};
}
global.bar = foo();
// ......

Können Sie nach dem Lesen dieses Codes erkennen, welche Objekte nach der Ausführung dieses Teils des Codes noch am Leben sind?

Nach den einschlägigen Grundsätzen umfassen die Objekte, die in diesem Code nicht recycelt und freigegeben wurden, val und bar(). Was ist der Grund, warum sie nicht recycelt werden können?

Wie führt die JavaScript-Engine die Speicherbereinigung durch? Der zuvor erwähnte Garbage-Collection-Algorithmus wird nur beim Recycling verwendet. Woher weiß er also, welche Objekte recycelt werden können und welche Objekte weiterhin überleben müssen? Die Antwort ist ein Verweis auf ein JavaScript-Objekt.

Selbst wenn Sie im JavaScript-Code einfach einen Variablennamen als separate Zeile schreiben, ohne einen Vorgang auszuführen, geht die JavaScript-Engine davon aus, dass es sich um ein Zugriffsverhalten auf das Objekt handelt und es einen Verweis auf das Objekt gibt. Um sicherzustellen, dass das Verhalten der Garbage Collection den Betrieb der Programmlogik nicht beeinträchtigt, darf die JavaScript-Engine die verwendeten Objekte nicht recyceln, da es sonst zu Chaos kommt. Das Kriterium für die Beurteilung, ob ein Objekt verwendet wird, ist also, ob noch ein Verweis auf das Objekt vorhanden ist. Tatsächlich ist dies jedoch ein Kompromiss, da JavaScript-Referenzen übertragen werden können. Einige Referenzen werden dann möglicherweise in den globalen Bereich übertragen, es besteht jedoch keine Notwendigkeit, sie in der Geschäftslogik zu ändern. Sobald darauf zugegriffen wird, sollten sie recycelt werden. aber die JavaScript-Engine wird immer noch fest davon überzeugt sein, dass das Programm es noch benötigt.

Die richtige Verwendung von Variablen und Referenzen ist der Schlüssel zur Optimierung von JavaScript auf Sprachebene.

3. Optimieren Sie Ihr JavaScript

Kommen wir endlich zum Punkt. Vielen Dank für Ihre Geduld beim Lesen. Ich glaube, dass Sie den Speicherverwaltungsmechanismus von JavaScript bereits gut verstanden haben .

3.1 Funktionen sinnvoll nutzen

Wenn Sie die Angewohnheit haben, hervorragende JavaScript-Projekte zu lesen, werden Sie feststellen, dass viele Experten bei der Entwicklung von Front-End-JavaScript-Code häufig eine anonyme Funktion verwenden, um die äußerste Ebene des Codes zu umschließen.

Code kopieren Der Code lautet wie folgt:

(function() {
/ / Hauptgeschäftscode
})();

Einige sind sogar noch fortgeschrittener:
Code kopieren Der Code lautet wie folgt:

;(function(win, doc, $, undefiniert) {
// Hauptgeschäftscode
})(window, document, jQuery);

Sogar modulare Front-End-Ladelösungen wie RequireJS, SeaJS, OzJS usw. nehmen eine ähnliche Form an:
Code kopieren Code wie folgt:

// RequireJS
define(['jquery'], function($) {
// Hauptgeschäftscode
});

// SeaJS
define('module', ['dep', 'underscore'], function($, _) {
// Hauptgeschäftscode
});

Wenn Sie sagen, dass der Code vieler Node.js-Open-Source-Projekte nicht auf diese Weise gehandhabt wird, dann liegen Sie falsch. Bevor der Code tatsächlich ausgeführt wird, packt Node.js jede .js-Datei in die folgende Form:
Code kopieren Der Code ist wie folgt folgt:

(function(exports, require, module, __dirname, __filename) {
// Hauptgeschäftscode
});

Was sind die Vorteile davon? Wir alle wissen, dass die Bereiche in JavaScript, wie am Anfang des Artikels erwähnt, Funktionsaufrufe mit Anweisungen und einen globalen Bereich umfassen. Und wir wissen auch, dass im globalen Bereich definierte Objekte wahrscheinlich bis zum Beenden des Prozesses überleben. Wenn es sich um ein großes Objekt handelt, ist dies problematisch. Einige Leute führen beispielsweise gerne das Rendern von Vorlagen in JavaScript durch:

Kopieren Sie den Code Der Code lautet wie folgt:
 
  $db = mysqli_connect(server, user, password, 'myapp');
  $topics = mysqli_query($db, "SELECT * FROM topic;");
?>



 
  你是猴子请来的逗比么?


 
    < ;/ul>
     
     

这种代码在新手的作品中经常能看得到,这里存在什么问题呢?如果在从数据库中获取到的数据的量是非常大的话, 前端完成模板渲染以后, data变量便被闲置在一边.可因为这个变量是被定义在全局作用域中的, 所以JavaScript引擎不会将其回收销毁.如此该变量就会一直存在于老生代堆内存中,直到页面被关闭.

可是如果我们作出一些很简单的修改,在逻辑代码外包装一层函数,这样效果就大不同了.当UI渲染完成之后,代码对data的引用也就随之解除,而在最外层函数执行完毕时, JavaScript引擎就开始对其中的对象进行检查, data也就可以随之被回收.

3.2作用域中, 默认情况下JavaScript 引擎就不会将其回收销毁。直到页面被关闭.

那么我们就一直遵循一个原则:绝对不要使用全局变量.虽然全局变量在开发中确实很省事,但是全局变量所导致的问题远比其所带来的方便更严重.

使变量不易被回收;
1.多人协作时容易产生混淆;

2.在作用域链中容易被干扰.

3.配合上面的包装函数,我们也可以通过包装函数来处理『全局变量』.

3.3了,那么就可以手工解除变量引用,以使其被回收.




复制代码


代码如下:

var data = { /* some big data */ } ;

复制代码


代码如下:

function getData(callback) {
var data = 'some big data';

callback(null, data);
}

getData(function (ähm, Daten) {
console.log(data);

Die Callback-Funktion ist eine Continuation Passing Style (CPS)-Technologie. Dieser Programmierstil verlagert den Geschäftsschwerpunkt der Funktion vom Rückgabewert auf die Callback-Funktion. Und es hat viele Vorteile gegenüber Verschlüssen:

1. Wenn es sich bei den übergebenen Parametern um Grundtypen (z. B. Zeichenfolgen, Werte) handelt, handelt es sich bei den in der Rückruffunktion übergebenen formalen Parametern um kopierte Werte, und der Geschäftscode kann nach der Verwendung einfacher recycelt werden.
2 .Durch Rückrufe können wir es zusätzlich zum Abschließen synchroner Anforderungen auch in der asynchronen Programmierung verwenden, was derzeit ein sehr beliebter Schreibstil ist.
3 Die Rückruffunktion selbst ist normalerweise eine temporäre anonyme Funktion ausgeführt wird, wird die Referenz der Callback-Funktion selbst freigegeben und selbst recycelt.

3.5 Gutes Verschlussmanagement

Wenn unsere Geschäftsanforderungen (z. B. Schleifenereignisbindung, private Eigenschaften, Rückrufe mit Parametern usw.) Abschlüsse verwenden müssen, achten Sie bitte auf die Details.

Schleifenbindungsereignisse können als erforderlicher Kurs für den Einstieg in JavaScript-Abschlüsse bezeichnet werden. Wir gehen von einem Szenario aus: Es gibt sechs Schaltflächen, die sechs Ereignistypen entsprechen. Wenn der Benutzer auf die Schaltfläche klickt, wird das entsprechende Ereignis ausgegeben am angegebenen Ort.


Code kopieren Der Code lautet wie folgt:

var btns = document.querySelectorAll ('. btn'); // 6 Elemente
var output = document.querySelector('#output');
var events = [1, 2, 3, 4, 5, 6];

// Fall 1
for (var i = 0; i < btns.length; i ) {
btns[i].onclick = function(evt) {
output.innerText = ' Clicked ' events [i];
};
}

// Case 2
for (var i = 0; i < btns.length; i ) {
btns [i] .onclick = (function(index) {
return function(evt) {
output.innerText = 'Clicked ' events[index];
};
})(i);
}

// Fall 3
for (var i = 0; i < btns.length; i ) {
btns[i].onclick = (function(event) {
return function(evt) {
      output.innerText = 'Clicked ' event;
  };
 })(events[i]);
}

Die erste Lösung hier ist offensichtlich ein typischer Loop-Binding-Ereignisfehler. Ich werde hier nicht auf Details eingehen, Sie können sich auf die Antwort beziehen, die ich einem Internetnutzer gegeben habe, und der Unterschied zwischen der zweiten und dritten Lösung in den eingehenden Parametern.

Der in der zweiten Lösung übergebene Parameter ist der aktuelle Schleifenindex, während letzterer direkt das entsprechende Ereignisobjekt übergibt. Letzteres eignet sich tatsächlich besser für umfangreiche Datenanwendungen, da in der funktionalen JavaScript-Programmierung die beim Aufruf der Funktion übergebenen Parameter grundlegende Typobjekte sind, sodass die im Funktionskörper erhaltenen formalen Parameter kopierte Werte sind. Daher wird dieser Wert als lokale Variable im Bereich des Funktionskörpers definiert. Nach Abschluss der Ereignisbindung kann die Ereignisvariable manuell dereferenziert werden, um die Speichernutzung im äußeren Bereich zu reduzieren. Und wenn ein Element gelöscht wird, werden auch die entsprechende Ereignisüberwachungsfunktion, das Ereignisobjekt und die Abschlussfunktion zerstört und recycelt.

3.6 Speicher ist kein Cache

Caching spielt eine wichtige Rolle in der Geschäftsentwicklung und kann die Belastung von Zeit- und Platzressourcen reduzieren. Es ist jedoch zu beachten, dass der Speicher nicht einfach als Cache verwendet werden kann. Speicher ist eine wertvolle Ressource für jede Programmentwicklung. Wenn es sich nicht um eine sehr wichtige Ressource handelt, platzieren Sie ihn bitte nicht direkt im Speicher und entwickeln Sie keinen Ablaufmechanismus, um den abgelaufenen Cache automatisch zu zerstören.

4. Überprüfen Sie die JavaScript-Speichernutzung

In der täglichen Entwicklung können wir auch einige Tools verwenden, um die Speichernutzung in JavaScript zu analysieren und Fehler zu beheben.

4.1 Blink-/Webkit-Browser

In Blink-/Webkit-Browsern (Chrome, Safari, Opera usw.) können wir das Profiles-Tool der Developer Tools verwenden, um den Speicher unseres Programms zu überprüfen.


4.2 Speicherüberprüfung in Node.js

In Node.js können wir die Module node-heapdump und node-memwatch zur Speicherüberprüfung verwenden.

Code kopieren Der Code lautet wie folgt:

var heapdump = require('heapdump') ;
var fs = require('fs');
var path = require('path');
fs.writeFileSync(path.join(__dirname, 'app.pid'), process.pid );
// ...

Copier le code Le code est le suivant :

Copier le code Le code est le suivant :
$ kill -USR2 (application chat .pid)

De cette façon, il y aura un fichier instantané nommé au format heapdump-..heapsnapshot dans le répertoire de fichiers. Nous pouvons utiliser l'outil Profils dans les outils de développement du navigateur pour l'ouvrir. et vérifiez-le.

5.Résumé

La fin de l'article est bientôt arrivée. Ce partage vous montre principalement les points suivants :

1. JavaScript est étroitement lié à l'utilisation de la mémoire au niveau du langage ;
2. Mécanisme de gestion et de recyclage de la mémoire en JavaScript
3 Comment utiliser la mémoire plus efficacement pour que le JavaScript produit puisse avoir plus de vitalité ; pour l'expansion ;
4. Comment effectuer une vérification de la mémoire en cas de problèmes de mémoire.

J'espère qu'en étudiant cet article, vous pourrez produire un meilleur code JavaScript, qui rassurera votre mère et votre patron.

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