通过 JSON 传递 JavaScript 函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7137731/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 23:04:21  来源:igfitidea点击:

Pass a JavaScript function through JSON

javascriptjsonfunctionobject

提问by Kit

I have a server side Python script that returns a JSON string containing parameters for a client side JavaScript.

我有一个服务器端 Python 脚本,它返回一个包含客户端 JavaScript 参数的 JSON 字符串。

# Python
import simplejson as json

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

This foobarfunshould refer to a JavaScript function. Here is my main client side script

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...
}

Of course, options.formatting_function()will complain that "a string is not callable" or something to that effect.

当然,options.formatting_function()会抱怨“字符串不可调用”或类似的东西。

Upon using Chrome's Inspect Element, under the Resourcestab, and navigating the left sidebar for XHR > query, I find that client_scriptinterprets optionsas below. foobarfunis seen as a string.

在使用 Chrome 的 Inspect Element,在Resources选项卡下,并导航XHR > query的左侧边栏后,我发现client_script解释options如下。foobarfun被视为一个字符串。

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

I would have liked client_scriptto see optionsas

我本来希望client_script看到options

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

Of course, doing the following within Python will have it complaining that it doesn't know anything about foobarfun

当然,在 Python 中执行以下操作会让它抱怨它对 foobarfun

# Python
params = {'formatting_function': foobarfun}

QUESTION:
How should I prepare my JSON string from the server side so that the client script can interpret it correctly? In this case, I want foobarfunto be interpreted as a function object, not as a string.

问题:
我应该如何从服务器端准备我的 JSON 字符串,以便客户端脚本可以正确解释它?在这种情况下,我想foobarfun被解释为一个函数对象,而不是一个字符串。

Or maybe it's something I should do on the client side?

或者也许这是我应该在客户端做的事情?

回答by T.J. Crowder

There's nothing you can do in the JSON to get the result you want because JSON has no concept of functions, it's purely a data notation. But there are things you can do client-side.

您无法在 JSON 中做任何事情来获得您想要的结果,因为 JSON 没有函数的概念,它纯粹是一种数据符号。但是有些事情你可以在客户端做。

If your foobarfunfunction is a global function (which I would recommend against, we'll come to that), then you can call it like this:

如果您的foobarfun函数是全局函数(我不建议这样做,我们会这样做),那么您可以这样调用它:

window[options.formatting_function]();

That works because global functions are properties of the windowobject, and you can access properties either by using dotted notation and literals (window.foobarfun), or by using bracketed notation and strings (window["foobarfun"]). In the latter case, of course, the string doesn't have to be a string literal, it can be a string from a property -- your options.formatting_functionproperty, for instance.

这是有效的,因为全局函数是window对象的属性,您可以通过使用点表示法和文字 ( window.foobarfun) 或使用方括号表示法和字符串 ( window["foobarfun"])来访问属性。当然,在后一种情况下,字符串不必是字符串文字,它可以是来自属性的字符串——options.formatting_function例如,您的属性。

But I don't recommend using global functions, the windowobject is already very crowded. Instead, I keep all of my functions (or as many as possible, in some edge cases) within a master scoping function so I don't add anything to the global namespace:

但是我不建议使用全局函数,window对象已经很拥挤了。相反,我将所有函数(或在某些边缘情况下尽可能多)保留在主作用域函数中,因此我不会向全局命名空间添加任何内容:

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

Now, if you do that, you can't access foobarfunon windowbecause the whole point of doing it is to avoid having it be on window. Instead, you can create your own object and make it a property of that:

现在,如果你这样做,你就不能访问foobarfunonwindow因为这样做的全部意义在于避免让它处于 on 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);
    }
})();

Frequently, rather than this:

通常,而不是这样:

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

you'll see people write:

你会看到人们这样写:

myStuff.foobarfun = function() {
};

I don't recommend that, either, because then your function is anonymous (the property on myStuffthat refers to the function has a name, but the function doesn't). Giving your functions names is a good thing, it helps your tools help you(showing you the names in call stacks, error messages, etc.).

我也不建议这样做,因为那样你的函数是匿名的(myStuff引用该函数的属性有一个名称,但该函数没有)。为您的函数命名是一件好事,它可以帮助您的工具帮助您(显示调用堆栈中的名称、错误消息等)。

You might also see:

您可能还会看到:

myStuff.foobarfun = function foobarfun() {
};

and that shouldbe valid, it's correct JavaScript. But unfortunately, various JavaScript implementations have various bugs around that (which is called a named function expression), most especially Internet Explorer prior to IE9, which will create two completely different functionsat two different times.

应该是有效的,它是正确的 JavaScript。但不幸的是,各种 JavaScript 实现都有各种错误(称为命名函数表达式),尤其是 IE9 之前的 Internet Explorer,它将在两个不同的时间创建两个完全不同的函数

All of that said, passing the names of functions around between the client and server usually suggests that you want to step back and look at the design again. Data should drive logic, but not in quite such a literal way. That said, though, there are definitely valid use cases for doing this, you may well have one in your situation.

综上所述,在客户端和服务器之间传递函数名称通常表明您想退后一步重新审视设计。数据应该驱动逻辑,但不是以字面的方式。话虽如此,但这样做肯定有有效的用例,您的情况很可能有这样的用例。

回答by user3615780

There is a trick you can do. Analyzing the code of the json module in python, I notice that it is possible to make the serializer believe that it is an intwhat is serializing that way __str__method will be executed.

有一个技巧你可以做。分析python中json模块的代码,我注意到有可能让序列化器相信它是一个int,以这种方式序列化__str__方法将被执行。

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)

the output is:

输出是:

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

The disadvantage of this method is that the output is not a valid JSON in python so it can't be deserialized, but it is a valid javascript.

这种方法的缺点是输出不是python中的有效JSON,因此无法反序列化,但它是有效的javascript。

回答by jkeesh

This question seems like it may be helpful for you:

这个问题似乎对你有帮助:

How to execute a JavaScript function when I have its name as a string

当我将其名称作为字符串时如何执行 JavaScript 函数

I think what I would do is store references to these methods in an object literal, and then access them through properties.

我想我要做的是将这些方法的引用存储在一个对象字面量中,然后通过属性访问它们。

For example, if I wanted to call foobarfun, among other functions

例如,如果我想调用foobarfun其他函数

var my_functions = {
   foobarfun: function(){

   },
   ...
};

...

var my_fn = my_functions[options.formatting_function];
my_fn();

回答by justadeveloper

may be you can think the return string as javascript not json, by set MIME type text/javascript

可能您可以通过设置 MIME 类型 text/javascript 将返回字符串视为 javascript 而不是 json

回答by shaILU

Well I am not a Python expert but I know some java script. From server you can pass the information to client only as string. If in any case you want to pass any java script function then you can pass that function name as string and evaluate that string on client side.

好吧,我不是 Python 专家,但我知道一些 Java 脚本。从服务器,您只能将信息作为字符串传递给客户端。如果在任何情况下您想传递任何 java 脚本函数,那么您可以将该函数名称作为字符串传递并在客户端评估该字符串。

Ex. If you passes xyz as string from server and on client side you call
var funcName = "xyz"; // pursuing that variable would pass here some how
eval (funcName); // This line would make a call to java script function xyz

前任。如果您从服务器和客户端将 xyz 作为字符串传递,您可以调用
var funcName = "xyz"; // 追求那个变量会在这里传递一些如何
eval (funcName); // 这一行将调用 java 脚本函数 xyz

Hint: You can think of java script eval utility as a reflection utility in java.

提示:您可以将 java 脚本 eval 实用程序视为 java 中的反射实用程序。

Thanks shaILU

谢谢 shaILU

回答by Crack

I don't know whether it's possible to trick Python's JSON serializer to do what you want, but you can use eval()function to execute any JavaScript code stored in a string variable.

我不知道是否可以欺骗 Python 的 JSON 序列化程序来执行您想要的操作,但是您可以使用eval()函数来执行存储在字符串变量中的任何 JavaScript 代码。