объем и оценка - PullRequest
       45

объем и оценка

1 голос
/ 03 августа 2011

У меня есть вызов Ajax, когда я возвращаю JavaScript в виде строки. В методе onSuccess я хочу оценить этот код. В коде JavaScript есть объявления-функции. Все эти функции должны быть доступны после eval.

Я составил небольшой пример. (В этом примере все происходит в методе onFailure, потому что в JFiddle я не могу выполнить успешный Ajax-вызов).

Пример вы можете найти здесь: http://jsfiddle.net/ubXAV/6/

Пример, который вы видите, работает во всех браузерах (К сожалению, это не будет работать в JSFiddle в IE). Я отметил несколько строк, относящихся к вопросам ниже. Вот код снова:

function evalScript(script)
{
   that.eval(script); //1.
}

var that = this;

//  AJAX-Call - GadgetActionServlet
new Ajax.Request("THISWILLFAIL.com", {
    method: 'post',
    onSuccess: function(ajaxResponse) {
            alert("success");
    },
    onFailure: function(){
        var script = "{function sayHello(){alert('Hello');}}";
        //that.eval(script); //not working in IE 2.
        evalScript(script);  //working in all browsers
    }
});

Я много читал в интернете об областях и контекстах в Java, но я просто не могу объяснить поведение здесь:

  1. Зачем мне вызывать eval на «это»? Согласно многим источникам в Интернете, контекст глобально определенной функции является наиболее глобальным контекстом. (Здесь должно быть окно). И код, вычисленный с помощью eval, должен выполняться в контексте, который вызывает функцию eval.

  2. Предполагая, что существует новый глобальный контекст для вызова Ajax (не так ли?), Почему я могу получить доступ к функции evalScript, но не оценивать скрипт здесь напрямую.

Общий вопрос, который у меня возникает: какие конкретные правила применяются к использованию eval? Где мои функции привязаны к контексту? И: имеет ли прототип Ajax-вызов, как в примере, свой глобальный объект?

Ответы [ 2 ]

4 голосов
/ 03 августа 2011

Прежде всего: если вы можете избежать использования eval, избегайте использования eval. Ваш код должен вернуться с POST? Потому что если вы хотите вместо этого использовать GET, вы можете просто добавить элемент script на страницу:

var script = document.createElement('script');
script.src = "http://example.com" +
                 "?" + encodeURIComponent("param1name") + "=" + encodeURIComponent("param1value") +
                 "&" + encodeURIComponent("param1name") + "=" + encodeURIComponent("param2value");
var parent = document.body
             || document.documentElement
             || document.getElementsByTagName('head')[0];
parent.appendChild(script);

Готово.

Или, если это POST, действительно ли должно быть реальным кодом сценария? Разве это не могут быть данные, которые интерпретируются по коду, уже указанному на странице? JSON - это полезный формат данных, если вы можете пойти по этому пути.

Но если он имеет , чтобы быть POST, и то, что вы получите , имеет , чтобы быть реальным кодом сценария, а не данными, тогда мы должны будем сделать что-то вроде eval. : -)

eval сам по себе очень, очень особенный. Он работает в той области, в которой он используется, даже если он немного похож на функцию, а функции работают не так. Таким образом, на самом деле вычислить код скрипта в глобальной области видимости сложно, если только вызов eval на самом деле не равен в глобальной области видимости (не в вызове любой функции), и, конечно, вы не можете сделать это здесь & mdash; Вы должны вызвать это из вашего обратного вызова ajax, и поэтому по определению это происходит внутри функции. ( Редактировать : Я только что подумал, как на самом деле использовать eval в глобальном масштабе, изнутри функции. См. Обновление в конце ответа. Но это зло, ужасно и неправильно.)

Причина, по которой вы, возможно, видели совет, говорящий об использовании window.eval, заключается в том, что многие современные браузеры предлагают window.eval (в отличие от eval), который оценивает данный код в глобальной области видимости. Но это доступно не во всех браузерах, и уж точно не в старых.

Однако есть обходные пути. Семейство IE предоставляет execScript, что очень аналогично window.eval, предлагаемому другими браузерами, и в худшем случае вы можете использовать элемент script. Вот глобальная функция eval, которая работает практически во всем:

window.evalInGlobalScope = (function() {
    var fname, scr;

    // Get a unique function name
    do {
        fname = "__eval_in_global_test_" + Math.floor(Math.random() * 100000);
    }
    while (typeof window[fname] !== 'undefined');

    // Create test script
    scr = "function " + fname + "() { }";

    // Return the first function that works:
    return test(evalInGlobalScope_execScript) ||
           test(evalInGlobalScope_windowEval) ||
           test(evalInGlobalScope_theHardWay) ||
           evalInGlobalScope_fail;

    function test(f) {
        try {
            f(scr);
            if (typeof window[fname] === 'function') {
                return f;
            }
        }
        catch (e) {
            return false;
        }
        finally {
            try { delete window[fname]; } catch (e) { window[fname] = undefined; }
        }
    }
    function evalInGlobalScope_execScript(str) {
        window.execScript(str);
    }
    function evalInGlobalScope_windowEval(str) {
        window.eval(str);
    }
    function evalInGlobalScope_theHardWay(str) {
        var parent, script, d = document;

        parent = d.body || d.documentElement || d.getElementsByTagName('head')[0];
        if (parent) {
            script = d.createElement('script');
            script.appendChild(d.createTextNode(str));
            parent.appendChild(script);
        }
    }
    function evalInGlobalScope_fail() {
        throw "evalInGlobalScope: Unable to determine how to do global eval in this environment";
    }
})();

.. и вот живой пример его использования .

Обратите внимание, что весь код, определяющий, что использовать, запускается только один раз; выбранная функция присваивается свойству evalInGlobalScope в window.

Также обратите внимание, что я не дал ему никакого возвращаемого значения. Это связано с тем, что версия «трудным путем» в принципе не может возвращать никакого возвращаемого значения, поэтому безопаснее, если ни один из них не возвращает. Имейте в виду, я не уверен, какие браузеры все еще требуют "трудного пути" & mdash; почти все имеет execScript и / или window.eval сейчас.


Обновление : Я сказал выше, что вы не можете использовать eval в глобальном масштабе изнутри функции. И технически это правда, но я подумал о том, как покончить с этим. Это зло, ужасно и неправильно, но оно работает: используйте вместо него setTimeout и установите таймаут 0:

setTimeout("your code here", 0);

Когда вы даете setTimeout строку, она выполняет eval над ней & mdash; после таймаута в глобальном масштабе .

Опять же, это зло, ужасно и неправильно, и у него есть дополнительный недостаток - он асинхронный (тогда как с нашей функцией evalInGlobalScope вычисление происходит синхронно), но это ... вроде ... работает. ( Live copy ) Я не рекомендую.

2 голосов
/ 03 августа 2011

думаю эта статья поможет http://perfectionkills.com/global-eval-what-are-the-options/

...