Передать функцию JavaScript через JSON - PullRequest
2 голосов
/ 21 августа 2011

У меня есть сценарий Python на стороне сервера, который возвращает строку JSON, содержащую параметры для JavaScript на стороне клиента.

# Python
import simplejson as json

def server_script()
  params = {'formatting_function': 'foobarfun'}
  return json.dumps(params)

Это foobarfun должно относиться к функции JavaScript. Вот мой основной клиентский скрипт

// JavaScript
function client_script() {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", url, async=true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4) {
      options = JSON.parse(xhr.responseText);
      options.formatting_function();
    }
  };
  xhr.send(null);
}

function foobarfun() {
  //do_something_funny_here...
}

Конечно, options.formatting_function() будет жаловаться, что "строка не вызывается" или что-то в этом роде.

После использования Chrome Inspect Element на вкладке Resources и навигации по левой боковой панели для XHR> query я обнаружил, что client_script интерпретирует options, как показано ниже. foobarfun рассматривается как строка.

// JavaScript
options = {"formatting_function": "foobarfun"}

Мне бы хотелось client_script видеть options как

// JavaScript
options = {"formatting function": foobarfun}

Конечно, выполнение следующих действий в Python вызовет недовольство тем, что он ничего не знает о foobarfun

# Python
params = {'formatting_function': foobarfun}

ВОПРОС:
Как подготовить строку JSON со стороны сервера, чтобы клиентский скрипт мог правильно ее интерпретировать? В этом случае я хочу, чтобы foobarfun интерпретировался как функциональный объект, а не как строка.

Или, может быть, это то, что я должен сделать на стороне клиента?

Ответы [ 6 ]

8 голосов
/ 21 августа 2011

Вы ничего не можете сделать в JSON, чтобы получить желаемый результат, потому что у JSON нет понятия функций, это просто нотация данных.Но есть вещи, которые вы можете делать на стороне клиента.

Если ваша foobarfun функция является глобальной функцией (против которой я бы порекомендовал, мы к этому придем), то вы можете вызвать ее так:

window[options.formatting_function]();

Это работает, потому что глобальные функции являются свойствами объекта window, и вы можете получить доступ к свойствам либо с помощью точечной нотации и литералов (window.foobarfun), либо с помощью нотации в скобках и строк (window["foobarfun"]).В последнем случае, конечно, строка не обязательно должна быть строковым литералом, это может быть строка из свойства - например, вашего options.formatting_function свойства.

Но я неРекомендуем использовать глобальные функции, объект window уже очень переполнен.Вместо этого я храню все свои функции (или как можно больше, в некоторых крайних случаях) в основной функции области видимости, поэтому я ничего не добавляю в глобальное пространство имен:

(function() {
    function foobarfun() {
    }
})();

Теперь, если высделать это, вы не можете получить доступ к foobarfun на window, потому что весь смысл этого состоит в том, чтобы избежать его на window.Вместо этого вы можете создать свой собственный объект и присвоить ему следующее свойство:

(function() {
    var myStuff = {};

    myStuff.foobarfun = foobarfun;
    function foobarfun() {
    }

    function client_script() {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url, async=true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          options = JSON.parse(xhr.responseText);
          myStuff[options.formatting_function]();   // <== using it
        }
      };
      xhr.send(null);
    }
})();

Часто вместо этого:

myStuff.foobarfun = foobarfun;
function foobarfun() {
}

вы увидите, что люди пишут:

myStuff.foobarfun = function() {
};

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

Вы также можете увидеть:

myStuff.foobarfun = function foobarfun() {
};

и что должно быть действительным, это правильный JavaScript.Но, к сожалению, различные реализации JavaScript имеют различные ошибки вокруг этого (который называется выражением именованной функции ), особенно Internet Explorer до IE9, который создаст две совершенно разные функции на двухразные времена.

Все это говорит, что передача имен функций между клиентом и сервером обычно предполагает, что вы хотите отступить назад и снова взглянуть на проект.Данные должны управлять логикой, но не таким буквальным образом.Тем не менее, есть определенно допустимые варианты использования для этого, у вас вполне может быть один в вашей ситуации.

1 голос
/ 14 июня 2018

Есть трюк, который вы можете сделать. Анализируя код модуля json в python, я замечаю, что можно заставить сериализатор поверить, что это метод int , который сериализует таким образом * __ str __ . 1005 *

import json

class RawJS(int):
    def __init__(self):
        self.code = "null"
    def __repr__(self):
        return self.code
    def __str__(self):
        return self.__repr__()
    @classmethod
    def create(cls, code):
        o = RawJS()
        o.code = code
        return o


js = RawJS.create("""function() {
    alert("hello world!");
}""")

x = {
    "func": js,
    "other": 10
}

print json.dumps(x)

вывод:

{"other": 10, "func": function() {
    alert("hello world!");
}}

Недостатком этого метода является то, что выходные данные не являются допустимым JSON в python, поэтому его нельзя десериализовать, но это допустимый JavaScript.

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

может быть, вы можете считать возвращаемую строку как javascript, а не json, установив MIME-тип text / javascript

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

Этот вопрос может показаться вам полезным:

Как выполнить функцию JavaScript, когда у меня есть имя в виде строки

Я думаю, что я хотел бы сохранить ссылки на эти методы в литерале объекта, а затем получить к ним доступ через свойства.

Например, если я хотел вызвать foobarfun, среди других функций

var my_functions = {
   foobarfun: function(){

   },
   ...
};

...

var my_fn = my_functions[options.formatting_function];
my_fn();
0 голосов
/ 21 августа 2011

Ну, я не эксперт по Python, но я знаю некоторый Java-скрипт.С сервера вы можете передавать информацию клиенту только в виде строки.Если в любом случае вы хотите передать какую-либо функцию сценария Java, вы можете передать имя этой функции в виде строки и оценить эту строку на стороне клиента.

Пример.Если вы передаете xyz как строку с сервера и на стороне клиента, вы вызываете
var funcName = "xyz";// поиск этой переменной будет проходить здесь каким-то образом how
eval (funcName);// Эта строка будет вызывать функцию java-скрипта xyz

Подсказка: вы можете представить утилиту eval скрипта java как утилиту отражения в java.

Спасибо shaILU

0 голосов
/ 21 августа 2011

Я не знаю, можно ли обмануть сериализатор JSON Python, чтобы он делал то, что вы хотите, но вы можете использовать функцию eval() для выполнения любого кода JavaScript, хранящегося в строковой переменной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...