摘要:五總結和應對方案安全性分析是否安全主要由數據源決定,如果數據源不安全,只是提供了一種攻擊方法而已。方案嚴格管控數據源。方案低頻使用時影響不大,不要高頻使用,建議尋找替代方案。方案了解直接調用和間接調用的區別,遇到問題時不要懵逼即可。
為什么要少用eval?
eval是 js 中一個強大的方法。都說eval == evil等于true,這篇文章將研討eval的幾個缺點和使用注意事項。
目錄一、安全性
二、運行效率
三、作用域
四、內存▲
五、總結和應對方案
一、安全性太明顯了,暫不討論
二、運行效率都知道 eval 比較慢,到底慢多少,自己測測看,下面是代碼(對比運行 1萬次 eval("sum++") 和 500萬次 sum++ 所需要的時間)
var getTime = function(){ // return Date.now(); return new Date().getTime() //兼容ie8 } var sum; // 測試 1萬次 eval("sum++") sum = 0; var startEval = getTime(); for(var i = 0;i<10000;i++){ eval("sum++"); } var durEval = getTime() - startEval; console.log("durEval = ",durEval,"ms"); // 測試 500萬次 sum++ sum = 0; var startCode = getTime(); for(var i = 0;i<5000000;i++){ sum++; } var durCode = getTime() - startCode; console.log("durCode = ",durCode,"ms"); //輸出結果 console.log("直接運行 sum++ 的速度約是 運行 eval("sum++") 的",(durEval * 500 / durCode).toFixed(0),"倍");測試結果
在同一臺PC上,測試3款瀏覽器和nodejs環境,結果如下:
Chrome 73
durEval = 236 ms durCode = 14 ms 直接運行 sum++ 的速度約是 運行 eval("sum++") 的 8429 倍
Firefox 65
durEval = 766 ms durCode = 167 ms 直接運行 sum++ 的速度約是 運行 eval("sum++") 的 2293 倍
IE8
durEval = 417ms durCode = 572ms 直接運行 sum++ 的速度約是 運行 eval("sum++") 的365倍
Nodejs 10.15.0
durEval = 5 ms durCode = 14 ms 直接運行 sum++ 的速度約是 運行 eval("sum++") 的 179 倍
Chrome 的 V8 果然是王者,Firefox 在運行eval的PK上輸給了古董IE8,node環境中eval的表現最好(只慢100多倍)
三、作用域在作用域方面,eval 的表現讓人費解。直接調用時:當前作用域;間接調用時:全局作用域。
3.1 直接調用eval被直接調用并且調用函數就是eval本身時,作用域為當前作用域,function中的foo被修改了,全局的foo沒被修改。
var foo = 1; function test() { var foo = 2; eval("foo = 3"); return foo; } console.log(test()); // 3 console.log(foo); // 13.2間接調用
間接調用eval時 執行的作用域為全局作用域,兩個function中的foo都沒有被修改,全局的foo被修改了。
var foo = 1; (function(){ var foo = 1; function test() { var foo = 2; var bar = eval; bar("foo = 3"); return foo; } console.log(test()); // 2 console.log(foo); // 1 })(); console.log(foo); // 3四、內存 ▲
使用eval會導致內存的浪費,這是本文要討論的重點。
下面用測試結果來對比,使用eval 和 不使用eval 的情況下,以下代碼內存的消耗情況。
var f1 = function(){ // 創建一個f1方法 var data = { name:"data", data: (new Array(50000)).fill("data 111 data") }; // 創建一個不會被使用到的變量 var f = function(){ // 創建f方法然后返回 console.log("code:hello world"); }; return f; }; var F1 = f1();測試結果
在Chrome上查看內存使用情況,開發者工具->Momery->Profiles->Take snapshot,給內存拍個快照。
為了便于查找,在過濾器中輸入window,查看當前域的window:
可以看到,window占用了68612,2%的內存。然后在其中找F1變量:
F1占用了32,0%的內存。
這似乎說明不了什么。沒有對比就沒有傷害,下面我們來傷害一下eval。
4.2 使用eval的情況修改上面的代碼,把 console.log 修改為 eval 運行:
- console.log("code:hello world"); + eval("console.log("eval:hello world");");測試結果
方法同上。在Chrome上查看內存使用情況,開發者工具->Momery->Profiles->Take snapshot。
window占用了251048,4%的內存,其中F1占用了200140,相當于總量的3%的內存,F1.context.data,占用了200044,約等于F1的占用量,可見這些額外的內存開銷都是來自于F1.context.data。
4.3 分析使用eval時:F1占用了200140,3%的內存;
不用eval時:F1占用了32,0%的內存;
這樣的差別來自于javascript引擎的優化。在方法f1運行時創建了data,接著創建了一個方法f,f中可以訪問到data,但它沒有使用data,然后f被返回賦值給變量F1,經過javascript引擎優化,這時data不會被加入到閉包中,同時也沒有其他指針指向data,data的內存就會被回收。然而在f中使用了eval后,情況就不同了,eval太過強大,導致javascript引擎無法分辨f會不會使用到data,從而只能將全部的環境變量(包括data),一起加入到閉包中,這樣F1就間接引用了data,data的內存就不會被回收。從而導致了額外的內存開銷。
我們可以進一步測試,這時在開發者工具->Console 中輸入:
F1 = "Hello" //重設F1,這樣就沒什么引用到data了
然后用同樣的方法查看內存,可以發現 window占用的內存,從200000+下降到了60000+。
說到這里,再回頭看eval奇怪的作用域。直接調用時:當前作用域;間接調用時:全局作用域,也就可以理解了。當間接調用時,javascript引擎不知道它是eval,優化時就會移除不需要的變量,如果eval中用到了那些變量,就會發生意想不到的事情。這違背了閉包的原則,變得難以理解。索性把間接調用的作用域設置為了全局。
五、總結和應對方案 安全性分析:eval是否安全主要由數據源決定,如果數據源不安全,eval只是提供了一種攻擊方法而已。
方案:嚴格管控數據源。
分析:eval比直接運行慢很多倍,但主要的消耗在于編譯代碼過程,簡單項目中,不會這樣高頻率的運行eval。
方案:低頻使用時影響不大,不要高頻使用,建議尋找替代方案。
分析:實際項目中直接調用都很少,間接調用更是少之又少。
方案:了解直接調用和間接調用的區別,遇到問題時不要懵逼即可。
分析:實際應用中很常見,卻很少有人會注意到內存管理,大項目中被重復使用會浪費較多的內存。
方案:優化編碼規范,使用eval時注意那些沒有被用到局部變量。
源碼鏈接:github
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103428.html
摘要:當中的是一個用于指向當前上下文對象的關鍵字。創建實例時的構造函數中的,永遠指向那個實例后對象,不是外部環境使用來調用函數時,先改變其上下文環境,在對其構造函數進行調用。 javascript 當中的 this是一個用于指向當前上下文對象的關鍵字。在面向對象編程及日常開發當中我們經常與其打交道,初學javscript的朋友非常容易誤入歧途從而理解錯誤。 上下文對象概念 在我的深入貫徹閉包...
摘要:每個專業的開發者都知道用戶上傳的文件都是極其危險的。如何防止引入用戶上傳的文件重命名文件名可以嗎不,辦不到解析器不關心文件的后綴名。服務器通常被設置成執行文件并將執行結果回復輸出。如何進行檢查這很簡單。用戶可以上傳文件到該站點。 showImg(https://segmentfault.com/img/remote/1460000017893665?w=1200&h=627); 每個專...
摘要:寫在前面對于一個前端開發者,應該沒有不知道作用域的。欺騙詞法作用域有兩個機制可以欺騙詞法作用域和。關于你不知道的的第一部分作用域和閉包已經結束了,但是,更新不會就此止住未完待續 這是《你不知道的JavaScript》的第一部分。 本系列持續更新中,Github 地址請查閱這里。 寫在前面 對于一個前端開發者,應該沒有不知道作用域的。它是一個既簡單有復雜的概念,簡單到每行代碼都有它的影子...
摘要:詞法作用域定義在詞法階段的作用域由你在寫代碼時將變量和塊作用域寫在哪來決定的,因此當詞法分析器處理代碼時會保持作用域不變。欺騙詞法作用域在詞法分析器處理過后依然可以修改作用域。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 JavaScript 開發者,如果沒...
摘要:作用域有兩種主要工作模型詞法作用域和動態作用域??赡軙幸恍┩瑢W認為是,那就是沒有搞清楚詞法作用域的概念。在嚴格模式下,在運行時有自己的詞法作用域,意味著其中的聲明無法修改所在的作用域。 1. 兩種作用域 作用域我們知道是一套規則,用來管理引擎如何在當前作用域以及嵌套的子作用域中根據標識符名稱進行變量查找。 作用域有兩種主要工作模型:詞法作用域和動態作用域。 大多數語言采用的都是詞法作...
閱讀 2312·2021-11-17 09:33
閱讀 843·2021-10-13 09:40
閱讀 574·2019-08-30 15:54
閱讀 778·2019-08-29 15:38
閱讀 2417·2019-08-28 18:15
閱讀 2475·2019-08-26 13:38
閱讀 1842·2019-08-26 13:36
閱讀 2129·2019-08-26 11:36