eval和with經(jīng)常被嫌棄,好像它們的存在就是錯誤。在CSS中,表格被嫌棄,在網(wǎng)頁(yè)中只是用表格來(lái)展示數據,而不是做布局,都可能被斥為不規范,矯枉過(guò)正。那關(guān)于eval和with到底是什么情況呢?本文將詳細介紹eval()函數和with語(yǔ)句
eval()是一個(gè)全局函數,javascript通過(guò)eval()來(lái)解釋運行由javascript源代碼組成的字符串
var result = eval('3+2');console.log(result,typeof result);//5 'number'
eval()只有一個(gè)參數,如果傳入的參數不是字符串,它直接返回這個(gè)參數。如果參數是字符串,它會(huì )把字符串當成javascript代碼進(jìn)行編譯。如果編譯失敗則拋出一個(gè)語(yǔ)法錯誤(syntaxError)異常。如果編譯成功,則開(kāi)始執行這段代碼,并返回字符串中的最后一個(gè)表達式或語(yǔ)句的值,如果最后一個(gè)表達式或語(yǔ)句沒(méi)有值,則最終返回undefined。如果字符串拋出一個(gè)異常,這個(gè)異常將把該調用傳遞給eval()
var num = 1;var str = 'test';console.log(eval(num));//1console.log(eval(str));//ReferenceError: test is not defined
var strLong1 = 'var x = 1;var y = 2;';console.log(eval(strLong1),x,y);//undefined 1 2var strLong2 = 'var x = 1; x++;';console.log(eval(strLong2),x);//1 2
eval()使用了調用它的變量作用域環(huán)境。也就是說(shuō),它查找變量的值和定義新變量和函數的操作和局部作用域中的代碼完全一樣
當通過(guò)別名調用時(shí),eval()會(huì )將其字符串當做頂層的全局代碼來(lái)執行。執行的代碼可能會(huì )定義新的全局變量和全局函數,或者給全局變量賦值,但卻不能使用或修改函數中的局部變量
var geval = eval; var x = 'global',y = 'global'; function f(){ var x = 'local'; eval('x += "changed";'); return x;}function g(){ var y = 'local'; geval('y += "changed";'); return y;}console.log(f(),x);//localchanged globalconsole.log(g(),y);//local globalchanged
[注意]IE8-瀏覽器通過(guò)別名調用eval()和正常調用eval()的結果相同
javascript解釋器進(jìn)行了大量的代碼分析和優(yōu)化。而eval()的問(wèn)題在于,用于動(dòng)態(tài)執行的代碼通常不能分析,于是解釋器也無(wú)法對其進(jìn)行優(yōu)化,這會(huì )導致性能下降
與eval()類(lèi)似的有setTimeout()、setInterval()、new Function()等,這些函數都可以以字符串作為參數,在程序運行時(shí)動(dòng)態(tài)執行。這種執行機制帶來(lái)的好處無(wú)法抵消其性能上的損失,所以應該盡量避免使用
由于eval()函數過(guò)于強大,嚴格模式對其進(jìn)行了嚴格的限制
【1】不能通過(guò)eval()函數來(lái)創(chuàng )建變量或函數,但可以查詢(xún)和更改其值
'use strict';eval('var x = 1;');console.log(x);//ReferenceError: x is not defined'use strict';var x = 1;eval('x = 2;');console.log(x);//2
【2】禁止使用eval作為標識符
'use strict';var eval = 10;//SyntaxError: Unexpected eval or arguments in strict mode
定義with語(yǔ)句的目的主要是為了簡(jiǎn)化多次編寫(xiě)同一對象的工作
with語(yǔ)句將object添加到作用域鏈的頭部,然后執行statement,最后把作用域鏈恢復到原始狀態(tài)
with(object){ statement;}
在對象嵌套層次很深的時(shí)候通常會(huì )使用with語(yǔ)句來(lái)簡(jiǎn)化代碼編寫(xiě)。而本質(zhì)上是通過(guò)將一個(gè)對象的引用當作作用域來(lái)處理,將對象的屬性當作作用域中的標識符來(lái)處理,從而創(chuàng )建了一個(gè)新的詞法作用域
在客戶(hù)端javascript中,可能會(huì )使用類(lèi)似下面這種表達式來(lái)訪(fǎng)問(wèn)一個(gè)HTML表單中的元素
document.forms[0].address.value
如果這種表達式在代碼中多次出現,則可以使用with語(yǔ)句將form對象添加到作用域鏈的頂層
with(document.forms[0]){ name.value = ''; address.value = ''; emai.value = '';}
這種方法減少了大量的輸入,不用再為每個(gè)屬性名添加document.forms[0]前綴。這個(gè)對象臨時(shí)掛載在作用域鏈上,當javascript需要解析諸如address的標識符時(shí),就會(huì )自動(dòng)在這個(gè)對象中查找
[注意]with語(yǔ)句提供了一種讀取對象的屬性的快捷方式,但它并不能創(chuàng )建對象的屬性
如果對象o有一個(gè)屬性x,那么下面代碼給這個(gè)屬性賦值為1
var o = {x:0};with(o) x = 1;console.log(o.x);//1
如果o中沒(méi)有定義屬性x,下面代碼和不使用with語(yǔ)句的代碼x=1是一模一樣的。這是因為對變量x進(jìn)行了LHS查詢(xún),并將1賦值給它
var o = {};with(o) x = 1;console.log(o.x);//undefinedconsole.log(x);//1
與eval類(lèi)似,with語(yǔ)句的javascript代碼非常難于優(yōu)化,同時(shí)也會(huì )給調試代碼造成困難,并且同沒(méi)有使用with語(yǔ)句的代碼相比,它運算得更慢
而且,如果with語(yǔ)句不當,還有可能造成變量泄漏,污染全局作用域的情況
嚴格模式下,禁止使用with語(yǔ)句
//SyntaxError: Strict mode code may not include a with statement'use strict';var o = {};with(o){ x = 2;}
使用eval和with會(huì )使得引擎無(wú)法在編譯時(shí)對作用域查找進(jìn)行優(yōu)化,從而導致性能下降,代碼運行變慢。因為eval和with在實(shí)際工作中很少使用,所以嚴格模式下的限制,對我們來(lái)說(shuō)影響不大。就像比如外交部某一天發(fā)布公告,我國不再發(fā)放去牙買(mǎi)加的簽證,牙買(mǎi)加雖然都聽(tīng)過(guò),但大多數人這輩子都可能不去一回,所以,無(wú)所謂了。同樣地,eval和with被嫌棄不嫌棄的,也是無(wú)所謂了
但即使不去牙買(mǎi)加,也不妨礙我了解到牙買(mǎi)加是加勒比海的其中一個(gè)島國,它是英聯(lián)邦成員國,也就是原來(lái)是英國殖民地,后來(lái)獨立了。它的國旗上面是一個(gè)X
類(lèi)比于eval和with
以上
聯(lián)系客服