40

I am creating the AI engine for a JS game, and it's made of Finite State Machines. I am loading the number of states and their variable values from the XML. I also want to load the behaviour, and since I don't have the time to create a scripting language, I thought it would be a good idea to 'insert' JS code on external files (inside XML nodes), and execute it on demand.

Something like that

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

To something like that:

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

My question is, now that I have the code loaded, how can I execute it? I would like to create a function with an unique name for each code 'node', and then call them from the game logic, but I don't know if this is possible (Since you can load more JS code from the HTML, you should also be able to do it from the JS code, no?). If not, is there any similar solution? Thanks in advance!

(PS:The less external-library-dependent, the better)

Edit 1:

Ok, so now I know how to create functions to contain the code

window[classname] = function() { ... };
Pang
  • 9,564
  • 146
  • 81
  • 122
user3018855
  • 403
  • 1
  • 4
  • 5

4 Answers4

83

Well, you could use Function constructor, like in this example:

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

This way you're defining a new function with arguments and body and assigning it to a variable f. You could use a hashset and store many functions:

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

Loading xml depends if it is running on browser or server.

kaizora
  • 23
  • 5
Erick
  • 992
  • 6
  • 6
  • Thank you for succinctly answering the question that brought me to this page. Combined with the information added by @MetalGodwin, I successfully solved that issue. Now I have only to address a cross-origin issue, and I should be home free. – David A. Gray Jan 25 '21 at 02:45
  • This is approach is not recommended and you should avoid using it as it cause a performance issue. For more information, ready **Cheating Lexical** in chapter two (Lexical Scope) of **You don't know JS Book 2: Scope and Closures** – Budi Salah Aug 20 '21 at 19:45
  • if you need to use this inside class, then you don't have access to ```this``` coz it will be pointing to that function. – Alaa M. Jaddou Sep 06 '21 at 06:04
9

To extend Ericks answer about the Function constructor.

The Function constructor creates an anonymous function, which on runtime error would print out anonymous for each function (created using Function) in the call stack. Which could make debugging harder.

By using a utility function you can dynamically name your created functions and bypass that dilemma. This example also merges all the bodies of each function inside the functions array into one before returning everything as one named 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 + '}')();
}

A heads up about the Function constructor:

A function defined by a function expression inherits the current scope. That is, the function forms a closure. On the other hand, a function defined by a Function constructor does not inherit any scope other than the global scope (which all functions inherit).

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

Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
MetalGodwin
  • 3,784
  • 2
  • 17
  • 14
  • Can you please explicit this: "*makes debugging harder*"? Ultimately, an example would be helpful. – Stephan Jul 30 '17 at 11:59
  • 1
    Hello, what I mean is that, upon runtime error, the stack trace and debugger prints out the custom name instead of "anonymous" for each created function. I can update the post. – MetalGodwin Jul 30 '17 at 15:23
  • Can you show this being called after it's been instantiated? I'm not used to calling the _constant to use a function. Thanks! – AppDreamer Oct 25 '19 at 22:13
  • Thank you, @MetalGodwin, for posting the citation of the Mozilla document about function constructors, from which I learned much that paved the way for eventually solving the problem that brought me to this page. – David A. Gray Jan 25 '21 at 02:42
5

Assuming you have an array of node names and a parallel array of function body's:

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();

or as globals:

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]);
}
Nuriel
  • 3,731
  • 3
  • 23
  • 23
Matthew Graves
  • 3,226
  • 1
  • 17
  • 20
0

All of the above answers use the new Function() approach which is not recommended as it effects your app performance. You should totally avoid this approach and use window[classname] = function() { ... }; as @user3018855 mention in his question.

Budi Salah
  • 153
  • 2
  • 11