怎樣在不使用eval的狀況下從字符串中調用函數

在Javascript中eval是惡魔!MDN中關於eval的部分這樣寫道:javascript

廢除
該功能被廢除。儘管如今瀏覽器依然支持此功能,在新的項目中並不鼓勵這種用法。儘可能避免使用它。java

eval 會執行一段包含着代碼的字符串,例如:數組

eval("var x = 'Hello from eval!';"); console.log(x);瀏覽器

eval會帶來如下問題:安全

安全性:你的字符串能被第三方的Javascript庫或者用戶輸入的內容注入。
調試:很難去調試錯誤 - 你將得不到發生錯誤的行數以及錯誤發生的點。
優化:Javascript解釋器將不會預編譯代碼由於它隨時可能改變。這樣的作法一般比原生代碼要運行的慢,即使是在解釋器變得更有效率的前提下。app

不幸的是,eval的功能很是強大,缺少經驗的開發者常常過分使用這個命令。函數

儘管有警告,eval任然在瀏覽器內繼續工做 - 即便是在嚴格模式下 -可是你一般能夠避免使用它。過去它主要用來解析JSON字符串可是如今咱們有了更安全的JSON.parse方法。優化

然而,要是咱們有一個包含函數名的字符串,好比說:spa

// 咱們想要運行的函數
var fnstring = "runMe";

function runMe() {
    // do stuff
}

咱們怎麼樣在不使用eval的前提下執行 runMe() 函數呢?我最近在使用HTML5 History API的時候就遇到了這個問題;pushState方法並不容許你將一個指向存儲爲一個函數以你你須要將它的名字定義爲一個字符串。當你使用Web Workers和其餘相似的序列化對象API是也會遇到相同的挑戰。調試

最簡單最安全的方法是編寫列條件,例如:

// 咱們想運行的函數
var fnstring = "runMe";

switch (fnstring) {
    case "functionX": functionX(); break;
    case "functionY": functionY(); break;
    case "functionZ": functionZ(); break;
    case "runMe": runMe(); break;
}

這種方式很安全,可是當你有一堆函數須要調用時很是低效並且痛苦。

一種更好的解決方法是使用window對象,它能夠指向當前窗口以及其中全部的項目。咱們能夠來檢查window中的fnstring是不是一個對象,若是它是一個函數那麼就執行它。例如:

// 咱們想要執行的函數
var fnstring = "runMe";

// 發現對象
var fn = window[fnstring];

// 對象是否是函數
if (typeof fn === "function") fn();

若是須要確保函數有一個預想的名字,你也能夠進行其餘檢查。

要是咱們想要調用的函數有參數怎麼辦 - 也許儲存在一個數組裏?沒問題,咱們只須要使用apply方法:

// 函數名和傳遞的參數

var fnstring = "runMe";
var fnparams = [1, 2, 3];

// 發現對象
var fn = window[fnstring];

// 對象是函數
if (typeof fn === "function") fn.apply(null, fnparams);

阻止咱們使用eval的理由多種多樣。比起eval來,上面的方法更加安全,更少發生錯誤,更易調試,而且一般能更快執行。我但願本文對你會有幫助。


在文章下面的評論中,有人提供了另外一個方法

setTimeout("runMe",0);

不過我記得setTimeout也調用了eval方法


本文譯自博文How to Call a JavaScript Function From a String Without Using eval,原文地址:http://www.sitepoint.com/call-javascript-function-string-without-using-eval/

相關文章
相關標籤/搜索