eval() 是 JavaScript 中一個很是有用的函數,它能夠一段代碼字符串動態執行。然而各類編碼規範和最佳實踐都強烈抵制 eval,幾乎將 eval 打入了死牢,大牛 Douglas Crockford 也在《JavaScript 語言精粹》一書中將 eval 視爲 JavaScript 中糟粕。這篇文章將帶你們從新認識這個函數,知道爲何不用它,以及爲何不得不用它。node
在分析 eval 的利弊前,首先來認識一下它。在不清楚一項技術的狀況下,就對它作出武斷地評價,是有失公允的。git
eval 是全局對象上的一個函數,會把傳入的字符串當作 JavaScript 代碼執行。若是傳入的參數不是字符串,它會原封不動地將其返回。eval 分爲直接調用和間接調用兩種,一般間接調用的性能會好於直接調用。github
直接調用時,eval 運行於其調用函數的做用域下;正則表達式
var context = 'outside';
(function(){
var context = 'inside';
return eval('context');
})();
// return 'inside'
複製代碼
而間接調用時,eval 運行於全局做用域。json
var context = 'outside';
(function(){
var context = 'inside';
geval = eval;
return geval('context');
// 下面兩種也屬於間接調用
// return eval.call(null, 'context');
// return (1, eval)('context');
})();
// return 'outside'
複製代碼
所以,間接調用時,eval 並不會修改調用函數做用域內的任何東西。JS 解釋器有 fast path 和 slow path 兩種模式,當直接調用 eval 時,解釋器處於 slow path。由於此時做用域是不可控的,須要監聽整個做用域,不能應用 v8 的一些編譯優化,相應的編譯效率也會比 fast path 低。小程序
你們抵制 eval 的緣由主要是如下幾個緣由:瀏覽器
鑑於以上各類緣由,不少人說 eval 是 evil(魔鬼)。另外,eval 還有一些難兄難弟,好比 new Function, setTimeout, setInterval。它們也具有執行一段代碼字符串的能力。 究其本質緣由,仍是由於 JS 賦予這個方法的權限太大了,做爲新手很難駕馭它,若是對 eval 沒有很好地理解,很容易寫出問題來。這有點像 C 語言中 goto 語句,一樣是由於權限太大而被封殺的典範。安全
事實上,eval 一直在被誤解,它多是最強大的一個 JavaScript 函數,但卻由於一些人的誤用,而被開發者們打入了冷宮。接下來,我來根據上述被質疑最多的幾個點,給出一點本身的見解。bash
從測試結果可知,eval 的確會拖慢函數執行性能,並且隨着函數規模增大,性能也越慢。可是在通常狀況下(N < 1000000),性能差別並無 10 倍那麼誇張。框架
雖然你們嘴上說不要用,可是 eval 用起來倒是真香。
筆者作過的項目中,曾經爲了讓 HTML 模板(應該說是一套頁面主題)也具有動態解析內聯表達式的能力,用了 data-eval 將 js 代碼存儲在 dom 節點,而後渲染時用 with 語句(另外一個 JS 「毒瘤」,如今嚴格模式下已經禁用 with 了,rip...)將 data 加到做用域鏈上,再用 eval 解析執行。實現出來的效果相似這樣:<div data-eval="data.count = data.count + 1">
{{data.count}}
</div>
複製代碼
渲染出來的結果是 eval 計算後的值。
不少庫和框架都用了 eval 實現各類黑魔法。早期的有用 eval 解析 json 的,好比 Douglas Crockford 的 json2.js(真香!)。到後來,各類 MVVM 框架也用 new Function 這個 eval 的好基友,來實現模板內嵌表達式的計算,好比 Vue 和 avalon。要達到的效果和筆者上面介紹的例子大體相同,不一樣的是這些 MVVM 框架還須要先解析模板,基於正則表達式提取出 new Function 的參數。
甚至不能用 eval 的時候,也要本身造一個 eval 出來。好比小程序上就不能使用 eval 和 new Function,那麼若是想動態注入並執行代碼的話,須要繞一個大彎,從編譯原理出發,自行實現一個 JS parser。
關於 eval,筆者我的的見解是,你能夠不去用它,但要去了解它。寫這篇文章的目的也不是爲了推薦你們使用 eval。就平時的業務開發而言,eval 幾乎沒有用武之地。但在一些特殊場合,eval 就像一枚核彈,無往不利。
參考連接: