validate test regular regexp preg_match ejemplo javascript regex optimization coding-style

test - regexp javascript



Estilo/optimización de JavaScript: String.indexOf() v. Regex.test() (5)

Recientemente me he encontrado con este fragmento de código JavaScript:

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

Estaba intrigado, porque para escribir esta prueba lo hubiera hecho:

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

¿Es esto solo una diferencia de estilo, o el autor del otro código sabe algo acerca de la optimización que yo no? ¿O tal vez hay una buena razón para hacerlo, o para no usar expresiones regulares ...?

Me parece que usar String.indexOf() para esto es un poco más difícil de leer (pero entonces, estoy bastante cómodo con las expresiones regulares), pero ¿hay casos en los que podría ser "mejor" que escribir una expresión regular equivalente? ?

Por "mejor" que podría ser más rápido o más eficiente (aunque obviamente eso depende del motor de JavaScript del navegador), o por algún otro motivo que desconozco. ¿Alguien puede iluminarme?


¿Es una pieza de código realmente antigua? Puede haber sido escrito antes de que las expresiones regulares fueran ampliamente utilizadas en javascript. En general, parece que alguien estaba tratando de ser demasiado inteligente y "optimizado" esa afirmación o provenía de un entorno C y no se utilizó para expresiones regulares. Una expresión regular puede ser costosa de usar, pero la concatenación de cadenas también puede ser y si no está en un bucle iría con la que sea más fácil de entender (para mí, la expresión regular).


Dudo que se trate de rendimiento u optimización. Sospecho que el autor de ese código simplemente no estaba cómodo o familiarizado con las expresiones regulares. También observe cómo la cadena separada por comas no se divide para aprovechar las propiedades del objeto, posiblemente también un caso de falta de familiaridad con el lenguaje.

Por ejemplo, otra forma de probar para un operador en una lista de operadores permitidos separados por comas sería dividir la lista de operadores permitidos separados por comas y hacer una inicialización de un objeto por única vez con los operadores como propiedades:

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

Esta pequeña sobrecarga de inicialización probablemente se verá compensada por la capacidad de realizar búsquedas rápidas:

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

Por supuesto, esto podría terminar siendo más lento en general, pero podría decirse que es un enfoque más limpio.


Ejecuté algunas pruebas. El primer método es ligeramente más rápido, pero no lo suficiente como para hacer una diferencia real, incluso bajo un uso intensivo ... excepto cuando sCompOp podría ser una cadena muy larga. Como el primer método busca una cadena de longitud fija, su tiempo de ejecución es muy estable sin importar cuánto tiempo llegue sCompOp , mientras que el segundo método posiblemente iterará a lo largo de toda la longitud de sCompOp .

Además, el segundo método potencialmente combinará cadenas inválidas - "blah blah blah <= blah blah" satisface la prueba ...

Dado que probablemente esté haciendo el trabajo de analizar el operador en otro lugar, dudo que cualquiera de los dos extremos sea un problema. Pero incluso si este no fuera el caso, una pequeña modificación a la expresión resolvería ambos problemas:

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

Código de prueba:

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

Probado en IE6, FF3, Chrome 0.2.149.30


Puede haber habido una notable diferencia de velocidad una vez, pero ya no es el caso. Creo que esto es:

  1. Código heredado de (sin Dios) The Land Before REGEX.
  2. Escrito por alguien que no sabe acerca de REGEX o le tiene miedo.

Eso me recuerda algunas implementaciones tempranas de getElementsByClassName basadas en JavaScript.

indexOf fue mucho más rápido que utilizar una expresión regular, pero el código que utilizaba indexOf comenzó partiendo de la suposición de que el desarrollador separaría los nombres de las clases con espacios (y no con pestañas y alimentaciones de línea). Para ser justos, algunas implementaciones basadas en expresiones regulares estaban usando / b (límite de palabras), que es incompatible con las especificaciones de CSS (porque CSS permite guiones en los nombres de las clases).

Usar indexOf para soportar getElementsByClassName en IE realmente puede hacer una diferencia, porque no hay alternativas más rápidas, y el getter / setter subyacente de la propiedad className reemplaza convenientemente las pestañas y los avances de línea con espacios.