JavaScript中,為什麽eval和with會有性能問題?
詞法作用域
詞法作用域意味著作用域是由書寫代碼時函數聲明的位置來決定的。編譯的詞法分析階段 基本能夠知道全部標識符在哪裏以及是如何聲明的,從而能夠預測在執行過程中如何對它 們進行查找。
eval(..) 和 with
JavaScript 中有兩個機制可以“欺騙”詞法作用域:eval(..) 和 with。前者可以對一段包 含一個或多個聲明的“代碼”字符串進行演算,並借此來修改已經存在的詞法作用域(在 運行時)。後者本質上是通過將一個對象的引用當作作用域來處理,將對象的屬性當作作 用域中的標識符來處理,從而創建了一個新的詞法作用域(同樣是在運行時)。
這兩個機制的副作用是引擎無法在編譯時對作用域查找進行優化,因為引擎只能謹慎地認 為這樣的優化是無效的。使用這其中任何一個機制都將導致代碼運行變慢。不要使用它們。
性能
eval(..) 和 with 會在運行時修改或創建新的作用域,以此來欺騙其他在書寫時定義的詞法作用域。 你可能會問,那又怎樣呢?如果它們能實現更復雜的功能,並且代碼更具有擴展性,難道不是非常好的功能嗎?答案是否定的。
JavaScript 引擎會在編譯階段進行數項的性能優化。其中有些優化依賴於能夠根據代碼的 詞法進行靜態分析,並預先確定所有變量和函數的定義位置,才能在執行過程中快速找到標識符。
但如果引擎在代碼中發現了 eval(..) 或 with,它只能簡單地假設關於標識符位置的判斷 都是無效的,因為無法在詞法分析階段明確知道 eval(..) 會接收到什麽代碼,這些代碼會如何對作用域進行修改,也無法知道傳遞給 with 用來創建新詞法作用域的對象的內容到底是什麽。
最悲觀的情況是如果出現了 eval(..) 或 with,所有的優化可能都是無意義的,因此最簡 單的做法就是完全不做任何優化。
如果代碼中大量使用 eval(..) 或 with,那麽運行起來一定會變得非常慢。無論引擎多聰 明,試圖將這些悲觀情況的副作用限制在最小範圍內,也無法避免如果沒有這些優化,代碼會運行得更慢這個事實。
摘抄自《你不知道的JavaScript上卷》
JavaScript中,為什麽eval和with會有性能問題?