博客 / 詳情

返回

JavaScript 作用域之eval()欺騙詞法作用域

最近在讀凱爾辛普森的《你不知道的JavaScript》,感覺挺有意思的,在理解作用域之後,看到了一個有意思的東西:欺騙詞法作用域。

首先來看看作用域是什麼吧。

作用域

簡而言之就是一套儲存變量並規定如何訪問並修改變量的規則,是幾乎所有編程語言最基本的功能之一。

作為JavaScript引擎的首席檢察官,他也會被自己人給騙了。


少廢話來看東西

欺騙方法一 :eval()函數

原理:JavaScript中的eval(str)函數可以接受一個字符串為參數,並將字符串內容視為好像在書寫時就存在於eval()函數所在位置的代碼。


function foo(str,a){
    eval(str); //欺騙代碼               
    console.log(a,b);
 }
 var b = 2;
 foo("var b = 3;" , 1);

猜猜運行結果是什麼呢?

是1, 3

乍一看,foo()中console.log(a,b)裏的b會會去外層訪問我們定義的全局變量b,作用域也是這麼想的,所以他只是一如往常的去檢查。

但是在執行eval(..)之後的代碼時,引擎並不“知道”或“在意”前面的代碼是以動態形式插入進來。

eval(..)調用中的"var b = 3; "這段代碼會被當作本來就在那裏一樣來處理。由於那段代碼聲明瞭一個新的變量b,因此它對已經存在的foo(..)的詞法作用域進行了修改。事實上,和前面提到的原理一樣,這段代碼實際上在foo(..)內部創建了一個變量b,並遮蔽了外部(全局)作用域中的同名變量。

當console.log(..)被執行時,會在foo(..)的內部同時找到a和b但是永遠也無法找到外部的b。因此會輸出“1, 3”而不是正常情況下會輸出的“1, 2”。

注意
嚴格模式的程序中,eval(..)在運行時有其自己的詞法作用域,意味着其中的聲明無法修改所在的作用域。
如下:
 function foo(str) {
 "use strict";
 eval(str);
 console.log(a);
        // ReferenceError: a is not defined
    }
    foo("var a = 2");`

其中eval(str);運行時無法修改所在的作用域了,此時a便無法找到。

JavaScript中還有其他一些功能效果和eval(..)很相似。setTimeout(..)和setInterval(..)的第一個參數可以是字符串,字符串的內容可以被解釋為一段動態生成的函數代碼。這些功能已經過時且並不被提倡。不要使用它們!

詞法作用域的"欺騙",eval()只是其中一個,更多詳情推薦看看凱爾辛普森的《你不知道的JavaScript》,他會帶你深入的瞭解JavaScript。

凱爾辛普森的《你不知道的JavaScript》
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.