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 wird let
, während es nur eine i
mit gibt var
.
Dagegen spricht die Tatsache, dass das var
so hochgezogen wird, dass es außerhalb der Schleife deklariert wird, während das let
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:
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
und let
in einer for
Schleife besteht darin, dass i
für jede Iteration ein anderer erstellt wird. Es befasst sich mit dem klassischen Problem "Closures in Loop":
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 die var
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 nicht o
auf ein neues Objekt verweisen (da dies das Ändern der darin enthaltenen Objektreferenz erfordern würde).
Bei Verwendung let
oder const
in var
ähnlichen Situationen ist es unwahrscheinlich, dass sie eine andere Leistung aufweisen. Diese Funktion sollte genau die gleiche Leistung haben, egal ob Sie var
oder let
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.
let
Verwendung im Blockbereich leistungsfähiger sein als dievar
, die keinen Blockbereich, sondern nur Funktionsbereich hat.