@老趙的一個微博 “由eval生成的代碼效率真的很差嗎?http://t.cn/zWTUBEo 內含人身攻擊,不喜勿入。”
引發了最近對eval火爆的討論,教主@Franky 和灰大@otakustay 也給了精彩的數據分析。
剛好之前也做過類似的測試,我也跟風湊個熱鬧,提供兩組數據供大家參考。
更新1: 感謝灰大 @otakustay 的指導,為排除eval('')調用本身對結果的影響,增加一組新數據A3, B3。並對舊的全部數據重測。
更新2: 感謝莫大@賁吃饃香的強力拍磚,增加了1). A4, B4;A5,B5的eval覆蓋後的測試數據; 2). A6,B6 eval別名;3). A7,B7 eval.call。
測試環境:
a. 機器:Intel(R) Corei7-2720 2.2Ghz (4核心8執行緒)、記憶體8Gb
b. OS:Windows 7 Enterprise SP1 64-bit
c. 瀏覽器:
b.1 Google Chrome 21.0.1180.79 m
b.2 Firefox 14.0.1
b.3 IE9.0.8112.16421
d.d. .1 每個用例測試5次,耗時取最小值。
d.2 測試過程中沒有開啟Firebug或Chrome Console,開啟這些工具會使時間倍增,很難在有效時間內得到該用例結果
用例A1:
我們在內聯函數中呼叫空的eval("")
!function () {
var a = 1,
b = 2,
c = true;
function func() {
var d = 2;
e = !c;
eval("");
}
for (var i = 0; i func(i, i 1, i 2);
}
}();
用例A2:
註釋掉內聯函數中的eval("")
!function() {
var a = 1,
b = 2,
c = true;
function func() {
var d = 2;
e = !c;
//eval("");
}
for (var i = 0; i func(i, i 1, i 2);
}
}();
用例A3:
為排除eval("")呼叫本身產生的影響,我們在外層函數中調用eval("")
!function() {
var a = 1,
b = 2,
c = true;
function func() {
var d = 2;
e = !c;
}
for (var i = 0; i eval("");
func(i, i 1, i 2);
}
}();
使用案例A4:
將eval()函數覆蓋成普通的空函數
function eval(){}
!function() {
var a = 1,
b = 2,
c = true;
function func() {
var d = 2;
e = !c;
eval("");
}
for (var i = 0; i func(i, i 1, i 2);
}
}();
用例A55>}();
用例A55>}();
同樣是函數調用,不是eval而且另一個空函數f
複製程式碼
複製程式碼
程式碼如下:
function f(){}
!function() {
var a = 1,
b = 2,
c = true;
function func( ) {
}();
用例A6:
將eval賦給另一個變數f,然後呼叫f
複製程式碼
程式碼如下:
var f = eval; {
var a = 1,
b = 2,
c = true;
function func() {
var d = 2;
e = !c;
用例A7:
使用eval.call的方式去調用
複製代碼
程式碼如下:
!function() {
var a = 1,
b = 2, c = true; function func() { var d = 2; e = !c; eval.call(null, ''); } for (var i = 0; i func(i, i 1, i 2); } }();
A组测试结果:
|
A1 |
A2 |
A3 |
A4 |
A5 |
A6 |
A7 |
A1 : A2 |
A1 : A3 |
A1 : A4 |
A4 : A5 |
Chrome |
1612ms |
8ms |
1244ms |
897ms |
7ms |
718ms |
680ms |
201.5 |
1.3 |
1.8 |
128.1 |
Firefox |
2468ms |
69ms |
732ms |
2928ms |
134ms |
5033ms |
4984ms |
35.8 |
3.4 |
0.8 |
21.9 |
IE |
1207ms |
23ms |
233ms |
1147ms |
37ms |
148ms |
224ms |
52.5 |
5.2 |
1.0 |
31.0 |
用例B1:
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
eval("");
}();
}();
}
用例B2:
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
//eval("");
}();
}();
}
用例B3:
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
}();
}();
eval("");
}
用例B4:
var eval = function(){}
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
eval("");
}();
}();
}
用例B5:
var f = function(){}
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
f("");
}();
}();
}
用例B6:
var f = eval;
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
f("");
}();
}();
}
用例B7:
for (var i = 0; i < 2999999; i++) {
!function() {
var a = 1,
b = 2,
c = true;
!function () {
var d = 2;
e = !c;
eval.call(null, '');
}();
}();
}
B组测试结果:
|
B1 |
B2 |
B3 |
B4 |
B5 |
B6 |
B7 |
B1 : B3 |
B1 : B2 |
B1 : B4 |
B4 : B5 |
Chrome |
1569ms |
134ms |
1093ms |
1022ms |
173ms |
830ms |
916ms |
11.7 |
1.4 |
1.5 |
5.9 |
Firefox |
5334ms |
1017ms |
5503ms |
5280ms |
1171ms |
6797ms |
6883ms |
5.2 |
1.0 |
1.0 |
4.5 |
IE |
3933ms |
560ms |
680ms |
4118ms |
583ms |
745ms |
854ms |
7.0 |
5.8 |
1.0 |
111.3 |
結論(僅限於文中的CASE):
1. eval本身的重複呼叫非常耗時,即使是空的eval("");
2. eval對內嵌函數執行效率有所影響,依具體環境、程式碼有所不同;
3. 我們可以看到無論哪種瀏覽器,無論是A組或B組,2 和 5速度較佳。說明例中內嵌函數的eval無論以何種方式呼叫(即使eval被空函數覆寫)仍會對運作效率造成較大影響。推斷是(黑盒推斷,非權威,很可能是臆測)內聯函數中只要發現eval,即使這個eval是被覆蓋的空函數,在Scope Variables中都將會把所有的外部定義的變量等內容初始化到當前的Scope中。類似的,eval會對內聯函數在運行時JS引擎的最佳化功能產生較大影響,降低執行效率。
4. 說點題外話,雖然沒用IE10,而是IE9,在對eval的處理上,表現非常的優異。 IE一直被開發人員詬病,但它的快速成長也是值得肯定的,本例就是很好的證明。
更詳細的原因剖析下列文章描述已十分詳細,不再累述。歡迎拍磚:)尤其是莫大...
@老趙 的 《由eval產生的程式碼效率真的很差嗎? 》
@Franky 的 《Eval科普》
@otakay @otakay
@otakay