Стиль/оптимизация JavaScript: String.indexOf() v. Regex.test()

Недавно я столкнулся с этим фрагментом кода JavaScript:

if (",>=,<=,<>,".indexOf("," + sCompOp + ",") != -1)

Я был заинтригован, потому что для написания этого теста я бы сделал:

if (/(>=|<=|<>)/.test(sCompOp))

Это просто стилистическая разница, или автор другого кода знает что-то об оптимизации, чего я не знаю? Или, может быть, есть другая веская причина сделать это или не использовать регулярные выражения...?

Мне кажется, что использование String.indexOf() для этого немного труднее читать (но тогда мне вполне комфортно с регулярными выражениями), но есть случаи, когда это может быть "лучше", чем писать эквивалент регулярное выражение?

"Лучше", что может быть быстрее или эффективнее (хотя очевидно, что это зависит от механизма JavaScript браузера) или по какой-то другой причине, о которой я не знаю. Может ли кто-нибудь просветить меня?

Ответ 1

Я провел несколько тестов. Первый метод немного быстрее, но не настолько, чтобы сделать реальную разницу даже при интенсивном использовании... за исключением случаев, когда sCompOp потенциально может быть очень длинной строкой. Поскольку первый метод ищет строку с фиксированной длиной, время ее выполнения очень стабильно, независимо от того, как долго будет sCompOp, а второй метод будет истребителем по всей длине sCompOp.

Кроме того, второй метод потенциально будет соответствовать недопустимым строкам - "blah blah blah <= blah blah" удовлетворяет тесту...

Учитывая, что вы, вероятно, выполняете работу по разборке оператора в другом месте, я сомневаюсь, что проблема с краем будет проблемой. Но даже если это не так, небольшая модификация выражения решит обе проблемы:

/^(>=|<=|<>)$/

Код тестирования:

function Time(fn, iter)
{
   var start = new Date();
   for (var i=0; i<iter; ++i)
      fn();
   var end = new Date();
   console.log(fn.toString().replace(/[\r|\n]/g, ' '), "\n : " + (end-start));
}

function IndexMethod(op)
{
   return (",>=,<=,<>,".indexOf("," + op + ",") != -1);
}

function RegexMethod(op)
{
   return /(>=|<=|<>)/.test(op);
}

function timeTests()
{
   var loopCount = 50000;

   Time(function(){IndexMethod(">=");}, loopCount);
   Time(function(){IndexMethod("<=");}, loopCount);
   Time(function(){IndexMethod("<>");}, loopCount);
   Time(function(){IndexMethod("!!");}, loopCount);
   Time(function(){IndexMethod("the quick brown foxes jumped over the lazy dogs");}, loopCount);
   Time(function(){IndexMethod("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");}, loopCount);

   Time(function(){RegexMethod(">=");}, loopCount);
   Time(function(){RegexMethod("<=");}, loopCount);
   Time(function(){RegexMethod("<>");}, loopCount);
   Time(function(){RegexMethod("!!");}, loopCount);
   Time(function(){RegexMethod("the quick brown foxes jumped over the lazy dogs");}, loopCount);
   Time(function(){RegexMethod("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");}, loopCount);
}

timeTests();

Протестировано в IE6, FF3, Chrome 0.2.149.30

Ответ 2

Это напоминает мне о ранних реализациях getElementsByClassName на основе javascript.

indexOf был намного быстрее, чем использование регулярного выражения, но код, который использовал indexOf, исходил из предположения, что разработчик будет разделять имена классов с пробелами (а не вкладками и фидами строк). Справедливости ради следует отметить, что в некоторых реализациях на основе regexp использовался \b (граница слова), что несовместимо с спецификацией CSS (поскольку CSS допускает дефисы в именах классов).

Использование indexOf для поддержки getElementsByClassName в IE может действительно иметь значение, поскольку нет более быстрых альтернатив, а свойство className, лежащее в основе getter/setter, удобно заменяет вкладки и переводы строк пробелами.

Ответ 3

Это действительно старый кусок кода? Возможно, это было написано до того, как регулярные выражения были широко использованы в javascript. В целом, похоже, что кто-то пытался быть слишком умным и "оптимизированным" этим утверждением или исходил из фона C и не использовался для регулярных выражений. Регулярное выражение может быть дорогостоящим, но конкатенация строк может быть слишком, и если бы он не был в цикле, я бы пошел с тем, что легче понять (для меня это регулярное выражение).

Ответ 4

Возможно, однажды была заметная разница в скорости, но это уже не так. Я думаю, что это либо:

  • Устаревший код (безбожник) Земля до REGEX.
  • Написано кем-то, кто не знает об REGEX или боится этого.

Ответ 5

Я сомневаюсь, что речь идет о производительности или оптимизации. Я бы заподозрил, что автор этого кода просто не устраивает или не знаком с регулярными выражениями. Также обратите внимание, как разделенная запятая строка не раздроблена, чтобы использовать свойства объекта - возможно, также и случай незнания языка.

Например, другим способом тестирования для оператора в списке разрешенных операторов, разделенных запятыми, было бы разделить список допустимых операторов, разделенных запятыми, и выполнить однократную инициализацию объекта с операторами как свойства:

var aOps = ">=,<=,<>".split(",");
var allowableOps = {};
for (var iLoop = 0; iLoop < aOps.length; iLoop++) {
  allowableOps[aOps[iLoop]] = true;
} //for

Эта небольшая служебная нагрузка инициализации, вероятно, будет компенсирована возможностью быстрого поиска:

if (allowableOps[sCompOp]) { ... }

Конечно, это может привести к замедлению в целом, но это, возможно, более чистый подход.