1. 程式人生 > >js eval() 全域性作用域(真是好文章),解決了大問題

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)  最佳  -  簡潔,乾淨,可靠,通用

事情到這裡,才可以去休息一下了。