為什麼不建議使用eval和with?

coyan發表於2021-09-09

圖片描述

假裝被面試

面試官:為什麼不建議使用eval和with?

因為影響效能、減低程式碼的安全性、程式碼更加難於閱讀。

那為什麼會影響效能?、為何會減低安全性?、怎麼就讓程式碼更加難於閱讀?

這個些問題主要是考察Js開發人對詞法作用域的理解,如果你和我一樣一臉矇蔽那就跟著我一起來看看這其中的"奧秘"吧!

先了解一下什麼是詞法作用域

簡單地來說,詞法作用域就是定義在詞法階段的作用域。詞法作用域是由你在寫程式碼時將變數和塊作用域寫在哪了來決定的,因此當詞法分析器處理程式碼時會儲存作用域不變(大部分情況下是這樣)

下面用一張圖幫助理解作用域:


圖片描述

來自《你不知道的JavaScript上卷》

(1)、包含著整個全域性作用域,其中只有一個識別符號:foo
(2)、包含著foo所建立的作用域,其中有三個識別符號: a、b、 bar
(3)、包含著bar所建立的作用域,其中只有一個識別符號:c

從上面那張圖片可以看到,作用域是逐級巢狀的。在執行console.log(a, b, c)時,引擎會從最內部的作用域(3)開始查詢,如果沒有找到,就逐層往外找。作用域查詢會在找到第一個匹配的識別符號時停止


下面迴歸正題,為什麼會影響效能?

這裡就要提到“欺騙詞法(程式碼在執行的時候"修改"詞法作用域)”這個詞,  JavaScript引擎在編譯階段有一些效能上的最佳化是依賴於能夠根據程式碼的詞法進行靜態分析,並預先確定所有變數和函式的定義位置,才能在執行過程中快速的找到識別符號。

但如果引擎在程式碼中發現了eval或with,它只能簡單地假設關於識別符號位置但判斷都是無效的,因為無法在詞法分析階段明確的知道eval會接收到什麼程式碼,這些程式碼是如何對作用域進行修改,因此最簡單的做法就是完全不做任何最佳化。如果程式碼中大量使用eval和with,導致引擎沒有對這些進行最佳化。那麼程式碼執行起來一定會變得更慢。

程式碼安全問題

我們都知道eval接收一個字串,然後解釋為一段可執行的程式碼,在傳遞進來的字元為不可預知的情況,這是多麼危險的行為。在JavaScript中有類似功能,還有: setTimeoutsetIntervalnew Function,我們應該儘量避免這樣使用它們。

說到with,它會在你不知的情況下把一些內部作用域宣告的函式或者變數洩露到其他作用域,例如洩露到全域性:

function a() { console.log('hello')
}function foo(obj) { with(obj) {
   a = function() {     console.log("hi")
   }
 }
}var obj = { b: 3}
a();   // hellofoo(obj)console.log(obj.a)   // undefineda();                        // hi

在obj建立了作用域沒有找到a,然後在foo()作用域找,也沒有找到、最後在全域性作用域中找到了,然後就把它給改了。

程式碼更加難於閱讀

這個就不難說明,如果傳遞給它們的引數都是不可預知的變數。那肯定難於閱讀,因為你根本不知道程式碼下一步執行會不會改變這個變數。

總結

with和eval的本意是好,但它們阻斷了變數名的詞法作用域繫結,導致的副作用遠遠大於它的使用價值,所以我們應該在開發中避免使用它們。


參考資料

《你不知道的Javascript》上卷



作者:內孤
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2236/viewspace-2813444/,如需轉載,請註明出處,否則將追究法律責任。

相關文章