js eval() 全域性作用域(真是好文章),解決了大問題
eval函式是強大的數碼轉換引擎,字串經eval轉換後得到一個javascript物件,
舉簡單例子:
var a = eval("5");等效於var a = 5;
var a = eval("'5'");等效於var a = '5';
var obj = eval("({name:'cat',color:'black'})");等效於 var obj = {name:'cat',color:'black'};
eval("alert('hello world!');");等效於 alert('hello world!');
js的資料型別為弱型別,可以在定義的時候指定資料型別,也可以在運算過程中強制資料型別轉換
一個物件經過eval轉換後資料型別不確定,在相加過程中自動與其他資料型別一致
eval(str) 用來傳入一個字串動態執行一段指令碼,這個方法非常有用。當直接用eval()時,作用域為當前作用域,有時候我們需要讓它在全域性作用域範圍內執行,比如 ajax返回的指令碼字串,然而瀏覽器對eval的差異可能使事情剛開始並不是那麼順利,本文通過在7種瀏覽器(IE, Firefox, Chrome, Safari, Opera)環境中測試,並提出三種解決方案,使這個問題比較完美的解決。
看這一段javascript程式碼:
function xx(){
var x= 1 ;
window .eval( 'var x=3;' );
document .writeln(x);
}
xx();
在你自己測試和看我接下來的分析之前,先想想,你認為輸出結果會是什麼呢?是1還是3?
根據本文的標題,可知肯定是在不同瀏覽器下有不同表現的。
以下是我的實測資料:
JS程式碼 | function xx(){ var x= 1 ; window .eval ( 'var x=3;' ); document .writeln (x); } xx(); |
||||||
---|---|---|---|---|---|---|---|
瀏覽器 | IE | IE | Firefox | Chrome | Chrome | Safari | Opera |
版本 | 7.0 | 8.0 | 3.0.8 | 1.0 | 4.0 | 4.0.2 | 9.62 |
執行結果 | 3 | 3 | 1 | 3 | 1 | 1 | 1 |
可見各瀏覽器及版本對window.eval()的作用域處理是有差異的。
IE中,window.eval()和eval()一樣只在當前作用域生效。
Firefox,Safari,Opera中,直接呼叫eval()為當前作用域,window.eval()呼叫為全域性作用域。
尤其值得注意的是,Google Chrome 的不同版本之間對於eval的處理也有差異。
如果需要在全域性作用域eval()的效果,且通用於所有瀏覽器,那就得好好變通一下了。
方法之一:
使用IE專有的window .execScript 。
如果你碰到這個問題不知所措,並上網搜尋,這個方法大概是最先也幾乎唯一能搜尋到的方法。
window .execScript (sExpression , sLanguage )。
比如上面那一段程式碼中eval一句如果換成window .execScript( 'var x=3;' ); IE中的執行結果就是1了。
非IE核心的瀏覽器並不支援window .execScript 。
IE之所以有這個window .execScript ,還和IE能夠執行其他語言的指令碼有關,通過給不同的sLanguage 引數,IE這個方法除能夠執行javascript之外,還可以執行vbscript或是其他任何安裝過相應解釋引擎的指令碼如perl,python等。
當需要在區域性環境中執行的時候,我們就直接用eval()。
當需要在全域性環境中執行的時候,我們可以封裝一個通用的函式,就像下面這樣:
//在全域性環境中執行
function evalGlobal(strScript){
if( window .execScript) window .execScript( strScript );
else window . eval ( strScript );
}
就是將IE和非IE區別開來對待。
看起來,問題似乎圓滿解決了。但是顯然是有問題的,比如上表中的Chrome 1.0也和IE的eval()規則一致,況且還不知道其他瀏覽器其他版本是否有差異呢,因此,這種方法並不很可靠。
但是如果你有一點完美主義者的傾向,那麼事情還不能到此為止,肯定是有更好更簡潔的方法的嘛。
不知道閱讀此文的你是否有想到呢?
是否和我的想法一致呢?
方法之二:
新建一個<script>元素裝載指令碼。
這種方法常用來解決innerHTML中的指令碼不能執行的問題。但用來解決eval()的作用域問題,恐怕就比較罕見了。
//在全域性環境中執行
function evalGlobal(strScript){
var a = document .createElement ("script" );
a.type= "text/javascript" ;
a.text= strScript ;
document .getElementsByTagName ("head" )[0 ].appendChild (a) ;
}
雖然這個方法有點變態,需要新增一個<script>元素,但優點是各種瀏覽器及版本通用,比方法一要好一些了。。
但是如果你有再多一點完美主義者的傾向,那麼事情還不能到此為止,畢竟添加了一個HTML元素嘛,影響了頁面原本的DOM結構。
那麼是不是有更好更簡潔的方法的呢?答案是肯定的。經過我的研究,找到了同時具備簡潔和可靠的方法三。
不知道閱讀此文的你是否有想到呢?
是否和我的想法一致呢?
方法之三:
還是eval。迴歸原生態。
我們別忘了javascript裡面有一個改變上下文環境的關鍵字,強大的with .
原來事情可以更簡單更有效!
//在全域性環境中執行
function evalGlobal(strScript){
with ( window )eval (strScript) ;
}
看看,都這麼簡單了,我們完全可以不用封裝為函數了,直接在程式碼中用。
文章最開始的程式碼我們就可以這樣來了:
function xx(){
eval ( 'var x=1;' ); //區域性變數
with ( window ){ eval ( 'var x=3;' );} //全域性變數
//也可以用封裝的 evalGlobal( 'var x=3' );
document .writeln (x); //1 區域性變數
document .writeln ( window .x); //3 全域性變數
}
xx();
特別:
有時候,我們eval()要求既不是在全域性執行,也不是在當前作用域執行,而是在父物件或子物件中執行,這時,用 with ( objContext )eval (strScript) 就更加是不可替代的選擇了。
總結:
讓eval()全域性作用域執行的方法主要有:
(1)window .execScript + window . eval 級別:弱。 缺點:不簡潔,不可靠,不通用。
(2)document . createElement ("script" ) 級別:湊合。缺點:不簡潔,不乾淨。優點:可靠,通用。
(3)with ( objContext )eval (strScript) 級別:最佳。優點:簡潔,乾淨,可靠,通用。
N整一個表格比較清楚:
讓eval()全域性作用域執行的方法 | 級別 | 缺點 | 優點 |
---|---|---|---|
(1)window .execScript + window . eval | 弱 | 不簡潔,不可靠,不通用 | - |
(2)document . createElement ("script" ) | 湊合 | 不簡潔,不乾淨 | 可靠,通用 |
(3)with ( window )eval (strScript) | 最佳 | - | 簡潔,乾淨,可靠,通用 |
事情到這裡,才可以去休息一下了。