Javascript: Как динамически построить метод из строковых данных? - PullRequest
2 голосов
/ 01 марта 2012

У меня есть XML-документ, который определяет задачу, которая представляет собой список действий, которые необходимо выполнить с определенными данными. Мне нужно преобразовать этот «список задач» в метод Javascript, который может быть вызван позднее, который, в свою очередь, вызывает ряд предопределенных методов с соответствующими данными. Как бы вы этого достигли?

Важное уточнение:
Я не беспокоюсь о разборе XML. Меня больше интересует, как на самом деле построить метод задачи, в том числе связать основные данные с заранее определенными методами действия. Это та часть, с которой я борюсь.

Редактировать: Я пересмотрел свой пример, чтобы сделать его немного интереснее и, надеюсь, немного понятнее.

XML:

<task id="enter-castle">
    <if holding="castle-key">
        <print message="You unlock the castle door and enter." />
        <destroy item="castle-key" />
        <goto location="castle" />

        <else>
            <print message="The castle door is locked." />
        </else>
    </if>
</task>

Javascript:

Game = {

    print: function(message) {
        // display message
    },

    destroy: function(item) {
        // destroy the object
    },

    goto: function(location) {
        // change player location
    },

    ifHolding: function(item) {
        // return true if player has item
    }
};

parseTask(taskNode) {

    var taskId = taskNode.getAttribute('id');

    // What goes here??

    Game.tasks[taskId] = /* ??? */;
}

Когда я вызываю parseTask() на узле <task id="enter-castle">, это должно создать функцию, которая, в действительности , выполняет следующее при вызове:

Game.tasks.enterCastle = function() {
    if (Game.ifHolding('castle-key')) {
        Game.print("You unlock the castle door and enter.");
        Game.destroy('castle-key');
        Game.goto('castle');
    } else {
        Game.print("The castle door is locked.");
    }
}

Ответы [ 3 ]

3 голосов
/ 03 марта 2012

То, что вы хотите, это замыкания .

function createMethod(arguments) {
    var task = doSomethingWithYour(arguments);
    return function(xmlData) { // <- this is the fundamental part
        // do the task with your data
        // the "task" vars are still available
        // even if the returned function is executed in a different context
    }
}

Это позволяет вам создать собственный метод для каждой задачи. Не используйте конструктор Function или eval.

2 голосов
/ 03 марта 2012

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

Конечно, есть недостатки в использовании "eval", которые я не буду рассматривать в этом ответе, поскольку вы можете найти бесчисленное множество обоснований того, почему не для его использования в Интернете. Однако создание и оценка простой исходной строки JS в краткосрочной перспективе будет намного проще, чем, скажем, решение на основе замыкания, несмотря на любые потенциальные недостатки производительности и безопасности. Более того, решение на основе eval будет легко протестировать, поскольку вы можете просто проверить исходную строку перед ее оценкой.

Так попробуйте что-то вроде этого:

function buildTaskFunction(taskXml) {
  var source='', depth=0, node /*a visitor to each DOM node*/;
  // foreach (node in traverseInOrder(taskXml)) {
    switch (node.nodeName) {
      case 'TASK':
        source += 'Game.tasks.' + makeFunctionName(node.id) + '= function(){';
        depth++;
        break;
      case 'IF':
        source += 'if(' + getConditionalAttribute(node) + '){'
        depth++;
        break;
      case 'ELSE':
        source += '}else{';
        break;
      case 'DESTROY':
        source += 'Game.destroy("' + node.getAttribute('item') + '");'
        break;
      case 'PRINT':
        source += 'Game.print("' + node.getAttribute('message') + '");'
        break;
      // case etc...
      default: throw new Error('unhandled node type "' + node.nodeName + '"');
    }
  // end "foreach node".
  while (depth-- > 0) { // You'll need to account for nested "if"s somehow...
    source += '}';
  }
  eval(source);
}

И снова, существует много потенциальных проблем (не окончательных) с использованием "eval", поэтому, пожалуйста, прочитайте и постарайтесь понять их в контексте вашего решения. Принимая решение о том, стоят ли недостатки в вашей собственной программе, используйте свое собственное суждение - просто потому, что инструмент может быть опасным, не означает, что вы не должны его использовать.

0 голосов
/ 02 марта 2012

Пример использования dojo :

dojo.require("dojox.xml.parser");

dojo.ready(function(){
  // Parse text and generate an XML DOM
  var xml = "<tnode><node>Some Text</node><node>Some Other Text</node></tnode>";
  var dom = dojox.xml.parser.parse(xml);

  var docNode = dom.documentElement();
  // ...
}

Остальная часть функции нетривиальна, но в основном будет состоять из поиска атрибутов с использованием ['<attribute-name>'] и итерации дочерних узлов с использованием dojo.forEach(<node>.childNodes, function(childNode) { /* do stuff */ });

...