ES6模板文字比字符串串联更快吗?

在 ES6中使用字符串串联或模板文字时,HTML 代码生成在现代浏览器中运行的速度是否快得多?

例如:

字符串连接

"<body>"+
"<article>"+
"<time datetime='" + date.toISOString() +"'>"+ date +"</time>"+
"</article>"+
"</body>"

字面上的模板

`<body>
<article>
<time datetime='${ date.toISOString() }'>${ date }</time>
</article>
</body>`
40518 次浏览

It seems for the moment string concatenation is faster: http://jsperf.com/es6-string-literals-vs-string-concatenation

ES6 with variable                     19,992,512    ±5.21%    78% slower
String concatenation with variable    89,791,408    ±2.15%    fastest
ES6 with function                     461,358       ±3.12%    99% slower
String concatenation with function    503,255       ±1.77%    99% slower

I tested was run on Chrome 43.0.2334.0 canary (64-bit), which is using V8 4.3.31, with the #enable-javascript-harmony flag enabled.

For reference, the latest version on Node.js (0.12.0 at the time of writing) is using V8 3.28.73: https://raw.githubusercontent.com/joyent/node/master/ChangeLog

I'm sure all the possible performance optimizations that could be applied have not been applied yet, so it would be reasonable to expect performance to get better as ES6 gets closer to finalization and these features get migrated to the stable branch.


Edit: Thanks for the comments @user1329482, @icl7126, Nicolai Borisik, and FesterCluck. Now that about 2 years have passed since this question was asked, ES6 browser support has greatly increased, and a good amount of performance optimization has taken place. Here are some updates.

Edit: (February 2020) Updated Chrome result based on @JorgeFuentesGonzález comments and subsequent confirmation.

In Chrome (as of 59.0.3035), ES6 string literals are faster:

ES6 with variable                     48,161,401       ±1.07%    fastest
String concatenation with variable    27,046,298       ±0.48%    44% slower
ES6 with function                     820,441          ±1.10%    98% slower
String concatenation with function    807,088          ±1.08%    98% slower

Update: In Chrome (as of 79.0.3945), String concatenation is faster... See comments.

In Firefox (as of 57.0.0), ES6 string literals are faster:

ES6 with variable                     1,924,610,984    ±0.50%    fastest
String concatenation with variable    1,876,993,458    ±0.79%    3% slower
ES6 with function                     539,762          ±5.04%    100% slower
String concatenation with function    546,030          ±5.88%    100% slower

In Safari (as of 11.0.2), it depends:

ES6 with variable                     1,382,752,744    ±0.71%    fastest
String concatenation with variable    1,355,512,037    ±0.70%    2% slower
ES6 with function                     876,516          ±1.01%    100% slower
String concatenation with function    883,370          ±0.79%    100% slower

When using a typecast string, ES6 string literals are faster. However, when calling a function from the literal, string concatenation is faster in this example.

If you really want to go deep and need to squeeze every drop of performance out of Safari, I would suggest setting up tests that see if/how incorrectly typed variables and multiple references within a literal effect performance.

I did a naive test on node.js v6.0.0 and got nearly the same performance. Since the test is so naive, don't believe the numbers too much. But it seems the JIT compiler generates very optimised code nowadays. This let me decide to prefer templates over concatenation for my node apps.

For reference this is the code I used:

'use strict'


function strConcat(i) {
return 'abc' + i + 'def'
}


function strTemplate(i) {
return `abc${i}def`
}


function run(strategy) {
let before = new Date().getTime()
let len = 0
for ( let i = 0; i < 10000000; i+=1 ) {
len += strategy(i).length
}
console.log(len + ' - ' + ((new Date().getTime()) - before) + 'ms')
}


console.log('strConcat')
run(strConcat)


console.log('strTemplate')
run(strTemplate)

And the output was:

strConcat
128888890 - 1904ms
strTemplate
128888890 - 1979ms

I used len to absolutely make sure that the optimizer doesn't optimize the whole loop away. Anyway, it is still a very simple test. Maybe someone can make a more sophisticated one.

For a simple test with random numbers as string, both are coming so close in Chrome & FF

Testing in Chrome 58.0.3029 / Windows 10

String literals 2,996,883 ±2.36% fastest

Operator (+) 3,054,078 ±2.01% fastest

Concat function 2,659,391 ±2.35% 13% slower

Testing in Firefox 53.0.2 / Windows 10

String literals 1,923,835 ±1.52% fastest

Operator (+) 1,948,503 ±1.13% fastest

Concat function 1,810,857 ±1.81% 8% slower

Test here at jsperf

TL;DR

Concatenation is faster and more consistent regarding its speed. But the difference is very little for 1 or 2 variables (below .3 seconds for 100 million calls).

Edit

After the second run it seems that concatenation is mostly the faster of the two.


So, I wanted to expand analog-nico's answer by providing a test that was more extensive and also looked (a bit) into scalability of the two functions.

Code on pastebin

I decided to use four test cases for each function, having a variable in the front, one at the end, one in the middle and two variables in the middle. The basic setup is the same. I'm just using 100,000,000 iterations of the function and these iterations are run 100 times. I used the same mechanisms to prevent optimization, namely getting the sum of the lengths of the resulting strings and logging it. I also logged the time needed (for me to guess how long it'll take) but also saved it into an array.

Afterwards, I calculated the average, minimum, maximum, and standard deviation for each method.

Here are the results:

{
sum: {
t: {
start: 2072751,
mid: 2338476,
end: 2083695,
double: 2950287
},
c: {
start: 2086059,
mid: 2345551,
end: 2074732,
double: 2922929
}
},
avg: {
t: {
start: 20727.51,
mid: 23384.76,
end: 20836.95,
double: 29502.87
},
c: {
start: 20860.59,
mid: 23455.51,
end: 20747.32,
double: 29229.29
}
},
sd: {
t: {
start: 335.6251329981114,
mid: 282.9490809315344,
end: 286.2220947096852,
double: 216.40844045461824
},
c: {
start: 255.4803356424913,
mid: 221.48744862858484,
end: 238.98242111084238,
double: 209.9309074433776
}
},
min: {
t: {
start: 20490,
mid: 23216,
end: 20588,
double: 29271
},
c: {
start: 20660,
mid: 23258,
end: 20534,
double: 28985
}
},
max: {
t: {
start: 23279,
mid: 25616,
end: 22887,
double: 30843
},
c: {
start: 22603,
mid: 25062,
end: 22403,
double: 30536
}
}
}

values in t-objects are for templates, values in c-objects are for concatenation. start means that the variable is at the beginning, mid that it is in the middle, end that it is at the end and double that there are two variables. sum is the sum of all 100 runs. avg is the average run, meaning it is sum / 100. sd Here is the easy way out, wikipedia (simple english). min and max are the minimum and maximum value of a run respectively.

Results

It seems like templates are faster for single variables that are not located at the end of a string, considering that the average is lower and the minimum is lower. If you put a variable at the end of a string or have multiple variables in your string, concatenation is faster.

Although the minimum as well as the average of templates is better than their concatenation counterparts regarding the first two conditions, the standard deviation is consistently worse. The difference seems to shrink with more variables (more tests needed).

Since most templates won't probably be used for only one variable in a string, it is save to say that sticking to concatenation yields a better performance. But the difference is (at least for now) very marginally. At 100,000,000 (100 million) evaluations with two variables, the difference are merely 273,58 ms, about a quarter second...


Second Run

The second run looks somewhat different. Except for the maximum value, average absolute deviation, and standard deviation, every measurement proofed that concatenation is faster than templates.

The three mentioned measurements had lower (thus better) values for templates when the variable was at the end of the string or when there were two variables in the string.

Here are the results:

{
"sum": {
"t": {
"start": 1785103,
"mid": 1826679,
"end": 1719594,
"double": 2110823,
"many": 4153368
},
"c": {
"start": 1720260,
"mid": 1799579,
"end": 1716883,
"double": 2097473,
"many": 3836265
}
},
"avg": {
"t": {
"start": 17851.03,
"mid": 18266.79,
"end": 17195.94,
"double": 21108.23,
"many": 41533.68
},
"c": {
"start": 17202.6,
"mid": 17995.79,
"end": 17168.83,
"double": 20974.73,
"many": 38362.65
}
},
"sd": {
"t": {
"start": 858.7857061572462,
"mid": 886.0941856823124,
"end": 786.5366719994689,
"double": 905.5376950188214,
"many": 1744.9005638144542
},
"c": {
"start": 599.0468429096342,
"mid": 719.1084521127534,
"end": 935.9367719563112,
"double": 991.5642274204934,
"many": 1465.1116774840066
}
},
"aad": {
"t": {
"start": 579.1207999999996,
"mid": 576.5628000000003,
"end": 526.8268,
"double": 586.9651999999998,
"many": 1135.9432000000002
},
"c": {
"start": 467.96399999999966,
"mid": 443.09220000000016,
"end": 551.1318000000008,
"double": 610.2321999999999,
"many": 1020.1310000000003
}
},
"min": {
"t": {
"start": 16932,
"mid": 17238,
"end": 16387,
"double": 20016,
"many": 39327
},
"c": {
"start": 16477,
"mid": 17137,
"end": 16226,
"double": 19863,
"many": 36424
}
},
"max": {
"t": {
"start": 23310,
"mid": 24102,
"end": 21258,
"double": 26883,
"many": 49103
},
"c": {
"start": 19328,
"mid": 23203,
"end": 22859,
"double": 26875,
"many": 44352
}
},
"median": {
"t": {
"start": 17571,
"mid": 18062,
"end": 16974,
"double": 20874,
"many": 41171.5
},
"c": {
"start": 16893.5,
"mid": 18213,
"end": 17016.5,
"double": 20771,
"many": 38849
}
}
}

The code is here

I think that the benchmark above is not useful. The result of interpolation or concatenation hasn't used. So yes, concatenation is pretty fast, because no string coping there and result string have only links to the parent strings. But if you'll try out the result string or compare with another one, the string will be serialized to plane string and, yes it will take some time. So interpolation could be more effective for CPU and memory usage then concatenation in real cases.