這篇文章主要介紹了理解Javascript的動態語言特性,需要的朋友可以參考下
Javascript是一種解釋性語言,而並非編譯性,它不能編譯成二進制文件。
理解動態執行與閉包的概念
動態執行:javascript提供eval()函數,用於動態解釋一段文本,並在當前上下文環境中執行。
首先我們需要理解的是eval()方法它有全局閉包和當前函數的閉包,比如如下代碼,大家認為會輸出什麼呢?
?
1 2 3 4 5 6 7 var i = 100; function myFunc() { var i = 'test'; eval('i = "hello."'); } myFunc(); alert(i); // 100首先我們來看看先定義一個變量i=100,然後調用myFunc這個函數,然後修改局部變量i,使他從值 'test'變成'hello', 但是我們知道eval的含義是立即執行一段文本的含義;因此上面的代碼我們可以寫成如下代碼:
?
1 2 3 4 5 6 7 8 9 var i = 100; function myFunc() { var i = 'test'; (function(){ return (i = "hello."); })(); } myFunc(); alert(i); // 100這樣就很明顯了,執行myFunc()這個方法後,i的值從test變為hello的值,但是由於是閉包,i的值為hello,它不能被外部使用,所以浏覽器打印的都是100值;
我們都知道eval()是javascript的全局對象Global提供的方法,而如果要訪問Global對象的方法,可以通過宿主對象-在浏覽器中是window來提供;按道理來說,下面的代碼應該也是輸出100;如下:
?
1 2 3 4 5 6 7 var i = 100; function myFunc() { var i = 'test'; window.eval('i="hello."'); } myFunc(); alert(i);然後不幸的是:在IE下不管是window.eval()還是eval()方法輸出的都是100;但是在標准浏覽器下使用window.eval(),輸出的是hello,使用eval()方法的輸出的是100; 因為IE下使用的是JScript引擎的,而標准浏覽器下是SpiderMonkey Javascript引擎的,正是因為不同的javascript引擎對eval()所使用的閉包環境的理解並不相同。
理解eval使用全局閉包的場合
如下代碼:
?
1 2 3 4 5 6 7 var i = 100; function myFunc() { var i = 'test'; window.eval('i="hello."'); } myFunc(); alert(i);在標准浏覽器下,打印的是hello,但是在IE下打印的是100;如果使用如下代碼:
?
1 2 3 4 5 6 7 8 var i = 100; function myFunc() { var i = 'test'; //window.eval('i="hello."'); eval.call(window,'i="hello"'); } myFunc(); alert(i);也是一樣的,也是給eval方法提供一種訪問全局閉包的能力;但是在IE下Jscript的eval()沒有這種能力,IE下一只打印的是100;不過在IE下可以使用另一種方法得到一個完美的結果,window.execScript()方法中執行的代碼總是會在全局閉包中執行,如下代碼:
?
1 2 3 4 5 6 7 8 var i = 100; function myFunc() { var i = 'test'; window.execScript('i="hello."'); //eval.call(window,'i="hello"'); } myFunc(); alert(i); // 打印helloJScript()引擎使用execScript()來將eval在全局閉包與函數閉包的不同表現分離出來,而Mozilla的javascript引擎則使用eval()函數的不同調用形式來區分它們。二者實現方法有不同,但是可以使用不同的方式實現全局閉包;
理解eval()使用當前函數的閉包
一般情況下,eval()總是使用當前函數的閉包,如下代碼:
?
1 2 3 4 5 6 7 var i = 100; function myFunc() { var i = 'test'; eval('i="hello."'); } myFunc(); alert(i); // 100如上代碼:因為eval作用與是函數內的代碼,所以輸出的是全局變量i等於100;
eval()總是被執行的代碼文本視為一個代碼塊,代碼塊中包含的是語句,復合語句或語句組。
我們可以使用如下代碼取得字符串,數字和布爾值;
eval('true'); // true
eval('"this is a char"'); // string
eval('3'); // 數字3
但是我們不能使用同樣的方法取得一個對象;如下代碼:
eval('{name:"MyName",value:1}');
如上代碼會報錯;如下:Uncaught SyntaxError: Unexpected
其實如上那樣寫代碼,{name:"MyName",value:1},eval會將一對大括號視為一個復合語句來標識,如下分析:
第一個冒號成了 “標簽聲明”標示符。
{“標簽聲明”的左操作數}name成了標簽。
MyName成了字符串直接量;
Value成了變量標示符。
對第二個冒號不能合理地作語法分析,出現語法分析期異常;
如果我們只有這樣一個就不會報錯了,如下代碼:
eval('{name:"MyName"}')
輸出"MyName";
那如果我們想要解決上面的問題要如何解決呢?我們可以加一個小括號包圍起來,使其成為一個表達式語句,如下代碼:
eval('({name:"MyName",value:1})')
輸出一個對象Object {name: "MyName", value: 1}
但是如下的匿名函數加小括號括起來在IE下的就不行了,如下代碼:
var func = eval('(function(){})');
alert(typeof func); // IE下是undefined
在標准浏覽器chrome和firefox是打印function,但是在IE下的JScript引擎下打印的是undefined,在這種情況下,我們可以通過具名函數來實現;如下:
eval('function func(){}');
alert(typeof func); // 打印是function
我們使用eval時候,最常見的是ajax請求服務器端返回一個字符串的格式的數據,我們需要把字符串的格式的數據轉換為json格式;如下代碼:
// 比如服務器返回的數據是如下字符串,想轉換成json對象如下:
var data = '{"name":"Mike","sex":"女","age":"29"}';
console.log(eval("("+data+")"));
打印Object {name: "Mike", sex: "女", age: "29"} 就變成了一個對象;
// 或者直接如下 ,都可以
console.log(eval("("+'{"name":"Mike","sex":"女","age":"29"}'+")"));
我們還需要明白的是使用eval或者with語句,他們都會改變作用域的問題,比如使用eval如下代碼:
?
1 2 3 4 5 6 7 var