Hat die Verwendung der neuen Schlüsselwörter "let" und "const" unabhängig von funktionalen Unterschieden allgemeine oder spezifische Auswirkungen auf die Leistung im Vergleich zu "var"?
Nach dem Ausführen des Programms:
function timeit(f, N, S) {
var start, timeTaken;
var stats = {min: 1e50, max: 0, N: 0, sum: 0, sqsum: 0};
var i;
for (i = 0; i < S; ++i) {
start = Date.now();
f(N);
timeTaken = Date.now() - start;
stats.min = Math.min(timeTaken, stats.min);
stats.max = Math.max(timeTaken, stats.max);
stats.sum += timeTaken;
stats.sqsum += timeTaken * timeTaken;
stats.N++
}
var mean = stats.sum / stats.N;
var sqmean = stats.sqsum / stats.N;
return {min: stats.min, max: stats.max, mean: mean, spread: Math.sqrt(sqmean - mean * mean)};
}
var variable1 = 10;
var variable2 = 10;
var variable3 = 10;
var variable4 = 10;
var variable5 = 10;
var variable6 = 10;
var variable7 = 10;
var variable8 = 10;
var variable9 = 10;
var variable10 = 10;
function varAccess(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += variable1;
sum += variable2;
sum += variable3;
sum += variable4;
sum += variable5;
sum += variable6;
sum += variable7;
sum += variable8;
sum += variable9;
sum += variable10;
}
return sum;
}
const constant1 = 10;
const constant2 = 10;
const constant3 = 10;
const constant4 = 10;
const constant5 = 10;
const constant6 = 10;
const constant7 = 10;
const constant8 = 10;
const constant9 = 10;
const constant10 = 10;
function constAccess(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += constant1;
sum += constant2;
sum += constant3;
sum += constant4;
sum += constant5;
sum += constant6;
sum += constant7;
sum += constant8;
sum += constant9;
sum += constant10;
}
return sum;
}
function control(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
}
return sum;
}
console.log("ctl = " + JSON.stringify(timeit(control, 10000000, 50)));
console.log("con = " + JSON.stringify(timeit(constAccess, 10000000, 50)));
console.log("var = " + JSON.stringify(timeit(varAccess, 10000000, 50)));
.. Meine Ergebnisse waren die folgenden:
ctl = {"min":101,"max":117,"mean":108.34,"spread":4.145407097016924}
con = {"min":107,"max":572,"mean":435.7,"spread":169.4998820058587}
var = {"min":103,"max":608,"mean":439.82,"spread":176.44417700791374}
Die hier erwähnte Diskussion scheint jedoch auf ein echtes Potenzial für Leistungsunterschiede unter bestimmten Szenarien hinzuweisen: https://esdiscuss.org/topic/performance-concern-with-let-const
javascript
performance
constants
v8
let
sean2078
quelle
quelle
let
Verwendung im Blockbereich leistungsfähiger sein als dievar
, die keinen Blockbereich, sondern nur Funktionsbereich hat.let
würden Sie dies tun und dann Müll sammeln, während diesvar
, was funktionsbezogen ist, nicht unbedingt auf die gleiche Weise funktionieren würde. Wieder denke ich, es ist so spezifisch für die Nutzung, dass beidelet
undconst
kann performanter sein, aber nicht immer sein würde.var
und demonstrieren solllet
: Er wird überhaupt nicht verwendetlet
.Antworten:
TL; DR
Theoretisch eine nicht optimierte Version dieser Schleife:
for (let i = 0; i < 500; ++i) { doSomethingWith(i); }
ist möglicherweise langsamer als eine nicht optimierte Version derselben Schleife mit
var
:for (var i = 0; i < 500; ++i) { doSomethingWith(i); }
weil für jede Schleifeniteration mit eine andere
i
Variable erstellt wirdlet
, während es nur einei
mit gibtvar
.Dagegen spricht die Tatsache, dass das
var
so hochgezogen wird, dass es außerhalb der Schleife deklariert wird, während daslet
nur innerhalb der Schleife deklariert wird, was einen Optimierungsvorteil bieten kann.In der Praxis führen moderne JavaScript-Engines hier im Jahr 2018 eine ausreichende Selbstbeobachtung der Schleife durch, um zu wissen, wann sie diesen Unterschied beseitigen können. (Schon vorher hat Ihre Schleife wahrscheinlich genug Arbeit geleistet, dass der zusätzliche
let
Overhead ohnehin ausgewaschen wurde. Aber jetzt müssen Sie sich nicht einmal mehr darum kümmern.)Passen Sie auf synthetische Benchmarks auf, da diese extrem leicht falsch sind, und lösen Sie JavaScript-Engine-Optimierer auf eine Weise aus, die echter Code nicht tut (sowohl auf gute als auch auf schlechte Weise). Wenn Sie jedoch einen synthetischen Benchmark wünschen, ist hier einer:
Code-Snippet anzeigen
const now = typeof performance === "object" && performance.now ? performance.now.bind(performance) : Date.now.bind(Date); const btn = document.getElementById("btn"); btn.addEventListener("click", function() { btn.disabled = true; runTest(); }); const maxTests = 100; const loopLimit = 50000000; const expectedX = 1249999975000000; function runTest(index = 1, results = {usingVar: 0, usingLet: 0}) { console.log(`Running Test #${index} of ${maxTests}`); setTimeout(() => { const varTime = usingVar(); const letTime = usingLet(); results.usingVar += varTime; results.usingLet += letTime; console.log(`Test ${index}: var = ${varTime}ms, let = ${letTime}ms`); ++index; if (index <= maxTests) { setTimeout(() => runTest(index, results), 0); } else { console.log(`Average time with var: ${(results.usingVar / maxTests).toFixed(2)}ms`); console.log(`Average time with let: ${(results.usingLet / maxTests).toFixed(2)}ms`); btn.disabled = false; } }, 0); } function usingVar() { const start = now(); let x = 0; for (var i = 0; i < loopLimit; i++) { x += i; } if (x !== expectedX) { throw new Error("Error in test"); } return now() - start; } function usingLet() { const start = now(); let x = 0; for (let i = 0; i < loopLimit; i++) { x += i; } if (x !== expectedX) { throw new Error("Error in test"); } return now() - start; }
<input id="btn" type="button" value="Start">
Es heißt, dass es keinen signifikanten Unterschied in diesem synthetischen Test auf V8 / Chrome oder SpiderMonkey / Firefox gibt. (Wiederholte Tests in beiden Browsern haben den einen oder den anderen Gewinn und in beiden Fällen innerhalb einer Fehlergrenze.) Aber auch hier handelt es sich um einen synthetischen Benchmark, nicht um Ihren Code. Sorgen Sie sich um die Leistung Ihres Codes, wenn und wenn Ihr Code ein Leistungsproblem aufweist.
Aus
let
Stilgründen bevorzuge ich den Scoping-Vorteil und den Closure-in-Loops-Vorteil, wenn ich die Loop-Variable in einem Closure verwende.Einzelheiten
Der wichtige Unterschied zwischen
var
undlet
in einerfor
Schleife besteht darin, dassi
für jede Iteration ein anderer erstellt wird. Es befasst sich mit dem klassischen Problem "Closures in Loop":Code-Snippet anzeigen
function usingVar() { for (var i = 0; i < 3; ++i) { setTimeout(function() { console.log("var's i: " + i); }, 0); } } function usingLet() { for (let i = 0; i < 3; ++i) { setTimeout(function() { console.log("let's i: " + i); }, 0); } } usingVar(); setTimeout(usingLet, 20);
Das Erstellen des neuen EnvironmentRecord für jeden Schleifenkörper ( Spezifikationslink ) ist Arbeit, und Arbeit braucht Zeit, weshalb die
let
Version theoretisch langsamer als dievar
Version ist.Der Unterschied ist jedoch nur wichtig, wenn Sie eine Funktion (Schließung) innerhalb der verwendeten Schleife erstellen
i
, wie ich es in diesem Beispiel für ein ausführbares Snippet oben getan habe. Andernfalls kann die Unterscheidung nicht beobachtet und wegoptimiert werden.Hier im Jahr 2018 sieht es so aus, als würde V8 (und SpiderMonkey in Firefox) eine ausreichende Selbstbeobachtung durchführen, sodass in einer Schleife, die nicht die
let
Semantik der Variablen pro Iteration verwendet , keine Leistungskosten anfallen. Siehe diesen Test .In einigen Fällen bietet sich
const
möglicherweise eine Optimierungsmöglichkeit,var
die insbesondere bei globalen Variablen nicht möglich wäre.Das Problem mit einer globalen Variablen ist, dass sie global ist. Jeder Code kann überall darauf zugreifen. Wenn Sie also eine Variable deklarieren, mit
var
der Sie niemals Änderungen vornehmen möchten (und die Sie niemals in Ihrem Code ändern), kann die Engine nicht davon ausgehen, dass sie sich aufgrund von später geladenem oder ähnlichem Code niemals ändern wird.Mit
const
teilen Sie der Engine jedoch ausdrücklich mit, dass sich der Wert nicht ändern kann¹. Sie können also jede gewünschte Optimierung durchführen, einschließlich der Ausgabe eines Literals anstelle eines variablen Verweises auf Code, der es verwendet, da Sie wissen, dass die Werte nicht geändert werden können.¹ Denken Sie daran, dass bei Objekten der Wert eine Referenz auf das Objekt ist, nicht auf das Objekt selbst. Mit
const o = {}
können Sie also den Status des Objekts (o.answer = 42
) ändern , aber Sie können nichto
auf ein neues Objekt verweisen (da dies das Ändern der darin enthaltenen Objektreferenz erfordern würde).Bei Verwendung
let
oderconst
invar
ähnlichen Situationen ist es unwahrscheinlich, dass sie eine andere Leistung aufweisen. Diese Funktion sollte genau die gleiche Leistung haben, egal ob Sievar
oderlet
zum Beispiel:function foo() { var i = 0; while (Math.random() < 0.5) { ++i; } return i; }
Es ist natürlich alles unwahrscheinlich und etwas, worüber man sich nur Sorgen machen muss, wenn ein echtes Problem zu lösen ist.
quelle
let
im Loop-Beispiel. Der Leistungsunterschied ist im Fall von 99,999% einfach keine Sorge wert."LET" IST IN LOOP-ERKLÄRUNGEN BESSER
Mit einem einfachen Test (5 Mal) im Navigator wie folgt:
// WITH VAR console.time("var-time") for(var i = 0; i < 500000; i++){} console.timeEnd("var-time")
Die durchschnittliche Ausführungszeit beträgt mehr als 2,5 ms
// WITH LET console.time("let-time") for(let i = 0; i < 500000; i++){} console.timeEnd("let-time")
Die durchschnittliche Ausführungszeit beträgt mehr als 1,5 ms
Ich fand, dass die Schleifenzeit mit let besser ist.
quelle
var=138.8ms
undlet=4ms
. Das ist kein Tippfehler,let
istvar=2.6ms
undlet=1.0ms
. Einlassknoten ist also etwas mehr als doppelt so schnell.Die Antwort von TJ Crowder ist so hervorragend.
Hier ist eine Ergänzung von: "Wann würde ich das Beste für mein Geld bekommen, wenn ich vorhandene var-Deklarationen zu const bearbeite?"
Ich habe festgestellt, dass der größte Leistungsschub mit "exportierten" Funktionen zu tun hat.
Wenn also Datei A, B, R und Z eine "Dienstprogramm" -Funktion in Datei U aufrufen, die üblicherweise über Ihre App verwendet wird, kann die Umschaltung dieser Dienstprogrammfunktion auf "const" und der Verweis der übergeordneten Datei auf eine Konstante auftreten etwas verbesserte Leistung. Es schien mir nicht messbar schneller zu sein, aber der Gesamtspeicherverbrauch wurde für meine grob monolithische Frankenstein-ed-App um etwa 1-3% reduziert. Wenn Sie viel Geld in der Cloud oder auf Ihrem Baremetal-Server ausgeben, kann dies ein guter Grund sein, 30 Minuten damit zu verbringen, einige dieser var-Deklarationen zu durchsuchen und auf const zu aktualisieren.
Mir ist klar, dass Sie, wenn Sie lesen, wie const, var und unter der Decke arbeiten lassen, wahrscheinlich bereits das Obige abgeschlossen haben ... aber für den Fall, dass Sie darüber "geschaut" haben: D.
Soweit ich mich an das Benchmarking auf Knoten v8.12.0 erinnere, als ich das Update durchführte, ging meine App von einem Leerlaufverbrauch von ~ 240 MB RAM auf ~ 233 MB RAM über.
quelle
Die Antwort von TJ Crowder ist sehr gut, aber:
Der Effekt der Leistungslücke zwischen var und let ist im realen Gesamtprogramm und nicht in einer einzelnen Basisschleife zu sehen.
Wenn Sie let verwenden, wo Sie es nicht müssen, ist Ihr Code weniger lesbar.
quelle
let
langsamer alsvar
? Besonders angesichts des Konsenses in den Kommentaren zu der obigen Antwort, der zeigt, dass es schneller ist?