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