天天看點

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代碼

    window .eval ( 'var x=3;' );

    document .writeln (x);

}

浏覽器 IE Firefox Chrome Safari Opera
版本 7.0 8.0 3.0.8 1.0 4.0 4.0.2 9.62
運作結果 3

可見各浏覽器及版本對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()的作用域問題,恐怕就比較罕見了。

        var a = document .createElement ("script" );

        a.type= "text/javascript" ;

        a.text= strScript ;

        document .getElementsByTagName ("head" )[0 ].appendChild (a) ;

雖然這個方法有點變态,需要新增一個<script>元素,但優點是各種浏覽器及版本通用,比方法一要好一些了。。

但是如果你有再多一點完美主義者的傾向,那麼事情還不能到此為止,畢竟添加了一個HTML元素嘛,影響了頁面原本的DOM結構。

那麼是不是有更好更簡潔的方法的呢?答案是肯定的。經過我的研究,找到了同時具備簡潔和可靠的方法三。

方法之三:

還是eval。回歸原生态。

我們别忘了javascript裡面有一個改變上下文環境的關鍵字,強大的with .

原來事情可以更簡單更有效!

        with ( window )eval (strScript) ;

看看,都這麼簡單了,我們完全可以不用封裝為函數了,直接在代碼中用。

文章最開始的代碼我們就可以這樣來了:

    eval ( 'var x=1;' );   //局部變量

    with ( window ){ eval ( 'var x=3;' );}   //全局變量

    //也可以用封裝的 evalGlobal( 'var x=3' );

    document .writeln (x);   //1  局部變量

    document .writeln ( window .x);   //3  全局變量

特别:

有時候,我們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)  最佳  簡潔,幹淨,可靠,通用

繼續閱讀