【问题标题】:Creating functions dynamically in JS在 JS 中动态创建函数
【发布时间】:2013-12-06 09:51:51
【问题描述】:

我正在为 JS 游戏创建 AI 引擎,它由有限状态机组成。我正在从 XML 加载状态数及其变量值。我还想加载行为,由于我没有时间创建脚本语言,我认为在外部文件(XML 节点内)上“插入”JS 代码并执行它是个好主意需求。

类似的东西

<evilguy1>
    <behaviour>
        this.x++;
    </behaviour>
    <behaviour>
        this.y++;
    </behaviour>
</evilguy1>

类似的事情:

function behaviour_1(){
    this.x++;
}
function behaviour_2(){
    this.y++;
}

我的问题是,现在我已经加载了代码,我该如何执行它?我想为每个代码“节点”创建一个具有唯一名称的函数,然后从游戏逻辑中调用它们,但我不知道这是否可能(因为你可以从 HTML 加载更多的 JS 代码,你应该也可以从 JS 代码中做到这一点,不是吗?)。如果没有,有没有类似的解决方案?提前致谢!

(PS:外部库依赖越少越好)

编辑 1:

好的,现在我知道如何创建包含代码的函数了

window[classname] = function() { ... };

【问题讨论】:

  • 我认为您需要详细说明并可能显示一些代码......
  • eval,但要小心,因为它是邪恶的。
  • @Triode 非常感谢命名问题。现在我只需要弄清楚如何处理代码加载。

标签: javascript


【解决方案1】:

假设你有一个节点名称数组和一个函数体的并行数组:

var functions = {};
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviorsNames.length; i++){
    functions[behaviorsNames[i]] =  new Function(behaviorsBodies[i]);
}

//run a function
functions.behavior1();

或作为全局变量:

var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviors.length; i++){
    window[behaviors[i]] = new Function(behaviorsBodies[i]);
}

【讨论】:

    【解决方案2】:

    好吧,你可以使用Function 构造函数,就像这个例子:

    var f = new Function('name', 'return alert("hello, " + name + "!");');
    f('erick');
    

    这样你就定义了一个带有参数和函数体的新函数,并将它分配给一个变量 f。您可以使用哈希集并存储许多函数:

    var fs = [];
    fs['f1'] = new Function('name', 'return alert("hello, " + name + "!");');
    fs['f1']('erick');
    

    加载 xml 取决于它是在浏览器还是服务器上运行。

    【讨论】:

    • 感谢您简洁地回答将我带到此页面的问题。结合@MetalGodwin添加的信息,我成功解决了这个问题。现在我只需要解决一个跨域问题,我应该可以回家了。
    • 不推荐使用这种方法,您应该避免使用它,因为它会导致性能问题。更多信息,请准备你不知道的JS书2:作用域和闭包的第二章(词法作用域)中的作弊词法
    • 如果你需要在类中使用这个,那么你就不能访问this 因为它会指向那个函数。
    【解决方案3】:

    扩展 Ericks 对 Function 构造函数的回答。

    Function 构造函数创建一个匿名函数,在运行时出错时会为调用堆栈中的每个函数(使用 Function 创建)打印出匿名函数。这可能会使调试变得更加困难。

    通过使用实用程序函数,您可以动态命名您创建的函数并绕过该困境。此示例还将函数数组中每个函数的所有主体合并为一个,然后将所有内容作为一个命名函数返回。

    const _createFn = function(name, functions, strict=false) {
    
        var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
    
        for(var i=0, j=functions.length; i<j; i++) {
            var str = functions[i].toString();
            var s = str.indexOf(cr) + 1;
            a.push(str.substr(s, str.lastIndexOf(cr) - s));
        }
        if(strict == true) {
            a.splice(1, 0, '\"use strict\";' + cr)
        }
        return new Function(a.join(cr) + cr + '}')();
    }
    

    关于 Function 构造函数的提示:

    由函数表达式定义的函数继承当前的 范围。也就是说,该函数形成了一个闭包。另一方面,一个 由 Function 构造函数定义的函数不继承任何范围 除了全局范围(所有函数都继承)。

    来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Differences

    【讨论】:

    • 您能否明确指出:“使调试更加困难”?最终,一个例子会有所帮助。
    • 您好,我的意思是,在运行时出错时,堆栈跟踪和调试器会为每个创建的函数打印出自定义名称而不是“匿名”。我可以更新帖子。
    • 你能证明它被实例化后被调用了吗?我不习惯调用 _constant 来使用函数。谢谢!
    • 感谢@MetalGodwin 发布了 Mozilla 文档中关于函数构造函数的引用,我从中学到了很多东西,这为最终解决导致我来到这个页面的问题铺平了道路。
    【解决方案4】:

    以上所有答案都使用new Function() 方法,不推荐这种方法,因为它会影响您的应用性能。您应该完全避免这种方法,并使用@user3018855 在他的问题中提到的window[classname] = function() { ... };

    【讨论】:

    • 引用“不推荐使用新的 Function() 方法”?
    • 阅读你不知道的JS书2:作用域和闭包的第二章(词法作用域)中的作弊词法 @SimpleFellow
    猜你喜欢
    • 1970-01-01
    • 2016-06-09
    • 2010-12-22
    • 2019-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-13
    • 2014-12-21
    相关资源
    最近更新 更多