TestComplete: Использование необъявленных переменных в JScript

Язык JScript позволяет использовать необъявленные переменные в скриптах. Безусловно, это плохо, так как при первой же попытке считать значение этой переменной мы получим ошибку “Microsoft JScript runtime error. VAR_NAME is undefined”, где VAR_NAME – имя необъявленной переменной.

Тем не менее, необъявленные переменные можно использовать в цикле FOR. При этом переменная автоматически будет создана и проинициализирована. Есть лишь одна проблема: при выходе из цикла эта переменная не будет уничтожена.

Представим себе такую функцию:

function UndeclaredVariable() {
 for (i = 1; i <= 3; i++) {
   Log.Message (i);
 }
 Log.Message (i);
}

Функция в цикле выводит значение переменной i (1, 2, 3), а затем снова выводит значение этой же переменной (4). Вроде бы все замечательно.

А теперь представим, что функция UndeclaredVariable рекурсивно вызывает саму себя. Например, необходимо пройти по всем элементам дерева, независимо от уровня вложенности, и найти некий определенный элемент.

Первый вариант функции будет выглядеть так:

function TreeFindNode(tree, node) {
  var nodes = tree.Nodes.Count;
  for(i = 0; i < nodes; i++) {
    Log.Message("Checking item '" + tree.Nodes.Item(i).Text + "'");
    if(tree.Nodes.Item(i).Text.OleValue == node)
      return tree.Nodes.Item(i);
  }
 return null;
}

Она ищет элемент дерева только на первом уровне и отработает нормально. Теперь модифицируем функцию, чтобы проверять все вложенные элементы дерева. Выглядеть она будет так:

function TreeFindNode(tree, node) {
  var ret;
  var nodes = tree.Nodes.Count;
  for(i = 0; i < nodes; i++) {
    Log.Message("Checking item '" + tree.Nodes.Item(i).Text + "'");
    if(tree.Nodes.Item(i).Nodes.Count > 0) {
      ret = TreeFindNode(tree.Nodes.Item(i), node);
      if(ret != null)
        return ret;
    }
    if(tree.Nodes.Item(i).Text.OleValue == node)
      return tree.Nodes.Item(i);
  }
  return null;
}

И создадим простую функцию, которая будет искать в дереве элемент Node3:

function Test1() {
  var w = Sys.Process("NETTreeView").WinFormsObject("Form1");
  var tree = w.WinFormsObject("treeView1");
  Log.Message(TreeFindNode(tree, "Node3") != null);
}

Теперь запустим ее и посмотрим, что выведется в логе.

Checking item ‘Node1’
Checking item ‘Node1.1’
Checking item ‘Node1.2’
Checking item ‘Node1.3’
Checking item ‘Node5’

Как видим, скрипт пропустил элементы Node2, Node3 и Node4. Это случилось из-за того, что JScript использовал одну и ту же переменную i для всех копий функции TreeFindNode вместо того, чтобы создавать каждый раз новую копию. Нетрудно представить, что возможны две ситуации: первую мы только что рассмотрели, в ней некоторые элементы были пропущены из-за того, что переменная цикла была изменена второй копией этой же функции. Другая ситуация заключается в том, что вторая копия функции уменьшит значение переменной до значения, которое уже было проверено. Вследствие этого функция будет работать бесконечно.

Чтобы исправить это достаточно лишь указать явное объявление переменной i в начале функции.

function TreeFindNode(tree, node) {
  var ret;
  var i;
  .............
}