对 const、 let 和 var 的 JavaScript 性能影响?

不管功能上的差异,使用新的关键字“ let”和“ const”相对于“ var”对性能有任何普遍的或特定的影响吗?

运行程序后:

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)));

我的结果如下:

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}

然而,这里所指出的讨论似乎表明,在某些情况下,性能差异的真正潜力: https://esdiscuss.org/topic/performance-concern-with-let-const

58483 次浏览

DR

理论上 ,这个循环的一个未经优化的版本:

for (let i = 0; i < 500; ++i) {
doSomethingWith(i);
}

可能比使用 var的同一个循环的未优化版本慢:

for (var i = 0; i < 500; ++i) {
doSomethingWith(i);
}

因为使用 let的每次循环迭代都会创建一个 different i变量,而使用 var的只有一个 i变量。

反对 的理由是,var是在循环外声明的,而 let只是在循环内声明的,这可能提供了优化优势。

在实践中 ,在2018年,现代的 JavaScript 引擎对循环进行了足够的反思,以知道什么时候可以消除这种差异。(甚至在此之前,您的循环所做的工作已经足够多,以至于与 let相关的额外开销无论如何都被清除了。但现在你甚至不用担心了。)

当心合成基准 ,因为它们非常容易出错,并以实际代码无法触发的方式(好的方式和坏的方式)触发 JavaScript 引擎优化器。然而,如果你想要一个合成的基准,这里有一个:

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">

It says that there's no significant difference in that synthetic test on either V8/Chrome or SpiderMonkey/Firefox. (Repeated tests in both browsers have one winning, or the other winning, and in both cases within a margin of error.) But again, it's a synthetic benchmark, not your code. Worry about the performance of your code when and if your code has a performance problem.

As a style matter, I prefer let for the scoping benefit and the closure-in-loops benefit if I use the loop variable in a closure.

Details

The important difference between var and let in a for loop is that a different i is created for each iteration; it addresses the classic "closures in loop" problem:

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);

为每个循环体(规格链接)创建新的 Environment Record 是一项工作,而且工作需要时间,这就是为什么理论上 let版本比 var版本慢。

但是只有在使用 i的循环中创建一个函数(闭包)时才会产生差异,正如我在上面的可运行代码片段示例中所做的那样。否则,就不能观察到这种区别,而且可以进行优化。

在2018年,似乎 V8(以及 Firefox 中的 SpiderMonkey)正在进行充分的自省,以便在不使用 let的每次迭代变量语义的循环中没有性能成本。见 这个测试


In some cases, const may well provide an opportunity for optimization that var wouldn't, especially for global variables.

The problem with a global variable is that it's, well, global; 任何 code 任何地方 could access it. So if you declare a variable with var that you never intend to change (and never do change in your code), the engine can't assume it's never going to change as the result of code loaded later or similar.

但是,对于 const,您显式地告诉引擎值不能更改1。因此,它可以自由地进行任何优化,包括发出一个文本而不是使用它的变量引用代码,同时知道这些值不能更改。

1记住,对于对象,值是指向对象的 reference < em > ,而不是指向对象本身。因此,使用 const o = {},您可以更改对象的状态(o.answer = 42) ,但是您不能让 o指向新对象(因为这将需要更改它包含的对象引用)。


当在其他类似 var的情况下使用 letconst时,它们不太可能有不同的性能。例如,无论使用 var还是 let,这个函数的性能都应该完全相同:

function foo() {
var i = 0;
while (Math.random() < 0.5) {
++i;
}
return i;
}

当然,这一切都不太重要,只有当真正的问题需要解决时才需要担心。

T.J. Crowder 的回答太棒了。

这里还有一个补充: “什么时候编辑现有的 var 声明到 const 最划算?”

我发现最大的性能提升与“导出”函数有关。

因此,如果文件 A、 B、 R 和 Z 正在调用文件 U 中的“实用程序”函数,而这个“实用程序”函数通常在应用程序中使用,那么将该实用程序函数切换到“ const”,父文件对 const 的引用可能会泄露出一些改进的性能。对我来说,它似乎并没有明显地更快,但是对于我的庞大的弗兰肯斯坦式应用程序来说,总体内存消耗减少了大约1-3% 。如果你在云计算或者裸金属服务器上花费大量的现金,这可能是一个很好的理由去花费30分钟来梳理和更新一些 var 声明来常量。

我意识到,如果你仔细阅读 const、 var 和 let work under the cover,你可能已经得出了上面的结论... ... 但是如果你“扫了一眼”的话: D。

根据我在更新时对节点 v8.12.0的基准测试的记忆,我的应用程序从大约240MB 内存的空闲消耗变成了大约233MB 内存。

"LET" IS BETTER IN LOOP DECLARATIONS

通过这样一个简单的导航测试(5次) :

// WITH VAR
console.time("var-time")
for(var i = 0; i < 500000; i++){}
console.timeEnd("var-time")

平均执行时间超过2.5 ms

// WITH LET
console.time("let-time")
for(let i = 0; i < 500000; i++){}
console.timeEnd("let-time")

平均执行时间超过1.5 ms

我发现 let 的循环时间更好。

克劳德的回答很好,但是:

  1. “ let”是为了让代码更易读,而不是更强大
  2. by theory let will be slower than var
  3. by practice the compiler can not solve completely (static analysis) an uncompleted program so sometime it will miss the optimization
  4. 无论如何,使用“ let”将需要更多的 CPU 来进行自省,当 google v8开始解析时,必须启动这个平台
  5. if introspection fails 'let' will push hard on the V8 garbage collector, it will require more iteration to free/reuse. it will also consume more RAM. the bench must take these points into account
  6. Google Closure will transform let in var...

Var 和 let 之间的性能差距的影响可以在现实生活中的完整程序中看到,而不是在单个基本循环中。

无论如何,在不需要的地方使用 let,会降低代码的可读性。

使用“ let”的代码将比使用“ var”的代码更优化,因为使用 var 声明的变量在作用域过期时不会被清除,但使用 let 声明的变量会被清除。所以 var 使用更多的空间,因为它在循环中使用时会产生不同的版本。

刚刚做了一些更多的测试,最初我的结论是,有一个实质性的差异,有利于 Var。我的结果最初显示,在 Const/Let/Var之间,执行时间的比率从4/4/1到3/3/1。

在29/01/2022编辑之后(根据 jmrk 在 let 和 const 测试中删除全局变量的注释)。 我给出了下面使用的代码。让我提一下,我从 AMN的代码开始,做了很多调整和编辑。

我在 w3school _ tryit 编辑器和 Google _ script 中都进行了测试

笔记:

  • 在 GoogleScripts 中,似乎第一个测试总是需要更长的时间,无论是哪一个,尤其是对于 reps < 5.000.000并且在将它们分离到单独的函数之前
  • 对于 Reps < 5.000.000 JS 引擎优化来说,最重要的是结果的上下波动,而没有安全的结论
  • GoogleScripts 不断地延长大约1.5倍的时间,我认为这是意料之中的
  • 当所有的测试在单独的函数中分离时,执行速度至少增加了一倍,第一次测试的延迟几乎消失了,这是一个很大的不同!

请不要评判代码,我试过了,但不要假装自己是 JS 方面的专家。 我很高兴看到你的测试和意见。

function mytests(){
var start = 0;
var tm1=" Const: ", tm2=" Let: ", tm3=" Var: ";
    

start = Date.now();
tstLet();
tm2 += Date.now() - start;


start = Date.now();
tstVar();
tm3 += Date.now() - start;


start = Date.now();
tstConst();
tm1 += (Date.now() - start);


var result = "TIMERS:" + tm1 + tm2 + tm3;
console.log(result);
return result;
}


// with VAR
function tstVar(){
var lmtUp = 50000000;
var i=0;
var item = 2;
var sum = 0;


for(i = 0; i < lmtUp; i++){sum += item;}
item = sum / 1000;
}


// with LET
function tstLet(){
let lmtUp = 50000000;
let j=0;
let item = 2;
let sum=0;


for( j = 0; j < lmtUp; j++){sum += item;}
item = sum/1000;
}


// with CONST
function tstConst(){
const lmtUp = 50000000;
var k=0;
const item = 2;
var sum=0;


for( k = 0; k < lmtUp; k++){sum += item;}
k = sum / 1000;
}