运行动态Javascript代码

运行动态Javascript代码,javascript,dynamic,interpreter,Javascript,Dynamic,Interpreter,我正在做一个小游戏,其中一部分我想要一个非常简单的自定义编程语言。如果用户输入代码,比如变量“helloWorld”=5,“解释器”会将变量更改为var,并删除引号,使其成为普通JavaScript 我应该如何运行该代码?我读过关于eval(),但也读过它速度慢,不应该使用。我已经研究过如何使用lexer、parser和tokenizer创建编程语言,但我不打算创建深入的东西 任何关于方向的帮助都会很好。您的语法可能会得到一些改进,但想法是这样的 首先,创建函数: function variab

我正在做一个小游戏,其中一部分我想要一个非常简单的自定义编程语言。如果用户输入代码,比如
变量“helloWorld”=5
,“解释器”会将变量更改为var,并删除引号,使其成为普通JavaScript

我应该如何运行该代码?我读过关于
eval()
,但也读过它速度慢,不应该使用。我已经研究过如何使用lexer、parser和tokenizer创建编程语言,但我不打算创建深入的东西


任何关于方向的帮助都会很好。

您的语法可能会得到一些改进,但想法是这样的

首先,创建函数:

function variable(name, value){
window[name] = value;
}
下一步是解析用户输入(这就是语法改进可以发挥作用的地方)。在您的示例中,您可以完全去掉引号,然后首先将字符串除以“=”,然后将其左半部分除以空格。得到3个元素,分别表示: 1) 函数名 2) 变量名 3) 可变值

然后,调用如下函数:

window[functionName](variableName, variableValie);
(function(){
// user script goes here.  This will cause it to be in it's own scope!
})();
(function(){Array.isArray = function() { return 2;};})()
Array.isArray([]);
// returns 2    

我假设您不需要“如何编写代码?”方面的帮助,而是需要如何执行用户脚本

关于
eval
  • 评估慢吗?对慢到底有多慢?如果脚本以10ms编译时间运行,否则以20ms编译时间运行,这对您和您的应用程序是否有问题
  • 用户会把eval搞砸吗?对他们可能会重新分配函数、全局变量等。他们可能会意外地破坏页面
  • 危险吗?对您可能容易受到XSS攻击。你有任何敏感数据吗?您的应用程序是否有服务器端?如果没有,我认为
    eval
    可以
  • 以下是来自不同SO问题的更多信息:
    关于防止全球重新分配的想法 用生命包装剧本!按如下方式包装脚本:

    window[functionName](variableName, variableValie);
    
    (function(){
    // user script goes here.  This will cause it to be in it's own scope!
    })();
    
    (function(){Array.isArray = function() { return 2;};})()
    Array.isArray([]);
    // returns 2    
    
    Javascript具有函数作用域,因此这将保护全局空间不被用户变量和函数填满。用户仍可能恶意影响全局变量,如下所示:

    window[functionName](variableName, variableValie);
    
    (function(){
    // user script goes here.  This will cause it to be in it's own scope!
    })();
    
    (function(){Array.isArray = function() { return 2;};})()
    Array.isArray([]);
    // returns 2    
    
    更多关于评估速度的信息。一个真实的例子:
    正如你所见,“大评估”稍微慢一点。您可能会执行大评估,一次运行用户脚本的所有行。“邪恶评估”要慢得多,因为js引擎正在运行评估10000000次

    这一切都取决于你真正想用你的语言做什么。我猜你真的不想让用户运行任意的javascript,它在你的应用所在的同一个全局空间中运行

    因此,根据您真正想要做的事情,您不一定需要使用
    eval()
    。例如,让我们以您的陈述为例:

    variable "helloWorld" = 5
    
    假设您解析了它,并将其解析为一个数组中包含四项的语句对象:

    var statement = ["variable", "helloWorld", "=", 5];
    
    好的,您可以从数组中的第一项看到用户正在声明一个变量。现在,您可能不希望用户的变量进入全局命名空间(不使用
    eval()
    的另一个原因)。因此,您可以为用户的变量创建一个对象:

    var userVars = {};
    
    现在,当您处理上述语句时,您将看到它是对用户变量的赋值,您只需将其转换为:

    userVars[statement[1]] = statement[3];
    
    现在,在名为
    userVars
    的变量中有一个对象,其属性名为
    “helloWorld”
    ,值为
    5
    。用户的变量都不在全局命名空间中,也不会影响您自己的程序操作

    现在,很明显,如果您想进入详细的表达式和逻辑流,那么事情会变得比这更困难,但我希望您看到您可能不只是想在全局名称空间中使用
    eval()


    执行任意用户javascript的另一种“安全”方法是将其放在iframe中,该iframe由与主网页不同的域托管。您可以将JS发送到服务器,并将其传递到另一台服务器(可能在同一个框中),然后为iframe提供服务,也可以使用
    window.postMessage()
    将JS文本传递到另一个协作iframe并在那里执行

    由于iframe托管在不同的域上,因此它与您自己的网页和您自己的web应用服务器隔离。这就是JSFIDLE的基本工作方式,允许用户运行任意javascript,同时确保自己的应用程序免受多种攻击


    或者,在使用
    window.postmasage()
    到一个独立的iframe的过程中,您甚至可以使用
    eval()
    在另一个iframe中。如果用户的JS的目的是与您的游戏交互,那么您必须找出如何允许两个iframe相互通信,但要安全地进行。可以使用IE8或更新版本支持的
    window.postMessage()
    来完成


    隔离iframe的美妙之处在于它有自己的一组隔离的全局变量,除了通过
    window.postMessage()公开的接口之外,它根本不能干扰你的游戏

    如果您需要JavaScript的完整表达能力,那么请坚持使用更多人已经熟悉的原始JavaScript。使用原始JavaScript有两种可能性(没有限定的解析器):

  • 如果您希望允许用户放入应用程序的JavaScript能够拥有与您的站点的完全交互权限,其方式类似于允许特权加载项进入您的站点(请记住巨大的风险:用户可能不知道他们在做什么,或者更糟糕的是,可能是从某个恶意源粘贴的,完全访问将包括允许访问您可能用于存储会话信息或密码的cookie),然后继续,让自己接受
    eval()

  • 如果您希望JavaScript具有完整的表现力,但不希望用户代码干扰您的应用程序(并且可以自由地在单独的站点上托管代码,而无需访问您的应用程序)