2012-05-14 14 views
5

Similar a python: make a variable equal an operator (+,/,*,-)¿Hay alguna manera de hacer que un operador de comparación sea una variable?

Tengo un poco de código donde el usuario puede elegir un tipo de comparación para ejecutar, y un valor para comparar. Tengo curiosidad por saber si hay alguna manera en Javascript para convertir ese valor de comparación de usuario proporcionada en una comparación real, lo que permite que haga algo como:

if (user_val user_comparison other_val) { 
    do_something(); 
} 

En lugar de tener que hacer algo como:

if (user_comparison = '<') { 
    if (user_val < other_val) { 
     do_something(); 
    } 
else if (user_comparison = '<=') { 
    if (user_val <= other_val) { 
     do_something(); 
    } 
....etc 

Tenga en cuenta que si coinciden algunas de las comparaciones, se ejecutará el mismo código.

+0

pues no, no se puede hacer eso en JS –

Respuesta

10

n que no es posible. Pero puedes estructurar tu código de una mejor manera.Por ejemplo, puede tener una tabla de búsqueda:

var operator_table = { 
    '>': function(a, b) { return a > b; }, 
    '<': function(a, b) { return a < b; } 
    // ... 
}; 

y posterior:

if(operator_table[user_comparison](user_val, other_val)) { 
    // do something 
} 

Por supuesto, también debe tratar el caso en user_comparison no existe en la tabla.

Estos también le dan un mejor control sobre los operadores permitidos y no permitidos.

Aquí está un DEMO crear por @Jesse.

+2

Aquí hay un jsFiddle que demuestra esto: http://jsfiddle.net/jonypawks/Cq8Hd/ – Jesse

+0

¿Por qué el voto a favor? –

+0

¿Quién sabe? Es una solución agradable y elegante que funciona mucho mejor que un interruptor. Utilizo una técnica similar para cambiar de vista en mi aplicación. –

4

Suponiendo que está comprobando correctamente los operandos y operadores proporcionados por el usuario para asegurarse de que contienen los datos que desea en lugar de otro código ejecutable de JavaScript, puede concatenar los dos operandos con el operador intermedio y alimentarlo al eval() para obtener se ejecutó.

Ahora, eval() es peligroso porque puede ejecutar cualquier código JavaScript. El usuario puede alimentar código JavaScript ejecutable y posiblemente malicioso como el operador y eval() lo evaluaría. Por lo tanto, cuando realiza la concatenación, debe hacerlo después de validar que el operando es seguro. Para enfatizar este punto, escribiré uno de los principios más importantes de la seguridad informática en fuentes grandes:

Todas las entradas son malas hasta que se demuestre lo contrario.

Además, tenga en cuenta que eval() llama al intérprete de JavaScript para interpretar, compilar y ejecutar su código. Esto es lento Si bien es posible que no observe ningún problema de rendimiento observable si solo está usando eval() de vez en cuando, puede notar problemas de rendimiento si llama al eval() con mucha frecuencia, por ejemplo, en cada evento.

Teniendo en cuenta estos inconvenientes de eval(), es posible que desee ir a una solución más ordenada como la publicada por Felix Kling. Sin embargo, también es posible resolver este problema utilizando eval() de una manera segura, como se muestra a continuación:

function compare(a, op, b) 
{ 
    // Check that we have two numbers and an operator fed as a string. 
    if (typeof a != 'number' || typeof b != 'number' || typeof op != 'string') 
    return 

    // Make sure that the string doesn't contain any executable code by checking 
    // it against a whitelist of allowed comparison operators. 
    if (['<', '>', '<=', '>=', '==', '!='].indexOf(op) == -1) 
    return 

    // If we have reached here, we are sure that a and b are two integers and 
    // op contains a valid comparison operator. It is now safe to concatenate 
    // them and make a JavaScript executable code. 
    if (eval(a + op + b)) 
    doSomething(); 
} 

Tenga en cuenta que la validación de la entrada en contra de una lista blanca es casi siempre una idea mejor que validarla contra una lista negra. Ver https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet#White_List_Input_Validation para una breve discusión sobre el mismo.

Aquí es una demostración de esta solución: http://jsfiddle.net/YrQ4C/ (Código también se reproducen a continuación):

function doSomething() 
{ 
    alert('done something!') 
} 

function compare(a, op, b) 
{ 
    if (typeof a != 'number' || typeof b != 'number' || typeof op != 'string') 
    return 

    if (['<', '>', '<=', '>=', '==', '!='].indexOf(op) == -1) 
    return 

    if (eval(a + op + b)) 
    doSomething(); 
} 

// Positive test cases 
compare(2, '<', 3) 
compare(2, '<=', 3) 

// Negative test cases 
compare(2, '>', 3) 
compare(2, '>=', 3) 

// Attack tests 
compare('alert(', '"attack!"', ')') 

// Edit: Adding a new attack test case given by Jesse 
// in the comments below. This function prevents this 
// attack successfully because the whitelist validation 
// for the second argument would fail. 
compare(1, ';console.log("executed code");2==', 2) 

Editar: Demostración de caso de prueba de Jesse incluye: http://jsfiddle.net/99eP2/

+0

Nunca se debe utilizar eval. – Jesse

+0

Si el usuario proporciona cualquiera de estos valores, podría ejecutar cualquier cosa ... –

+0

@Jesse ¿podría explicar por qué no debería usar eval? – Esen

-1

Dado que el código de @Susam Pal no está funcionando. Estoy publicar una versión de trabajo

<html> 
    <head> 
    <script> 
     function CompareSomething(val1, compareString, val2) { 
      eval('if(' + val1 + ' ' + compareString + ' ' + val2 + '){conditionPassed();}else{conditionFailed();}'); 
    } 
    function compare(a, op, b) { 
     if (eval(a + op + b)) 
      conditionPassed(); 
     else 
     conditionFailed(); 
    } 
    function conditionPassed() { 
     alert('condition passed'); 
    } 
    function conditionFailed() { 
     alert('condition failed'); 
    } 
    </script> 
    </head> 
<body> 
a:<input id='txt1' type="text" />&nbsp;op:<input id='txt2' type="text" />&nbsp;b:<input id='txt3' type="text" /><br/> 
<button id='compare' onclick='CompareSomething(document.getElementById("txt1").value,document.getElementById("txt2").value,document.getElementById("txt3").value)'>Compare Esen Method</button><br/> 
<button id='compare' onclick='Compare(document.getElementById("txt1").value,document.getElementById("txt2").value,document.getElementById("txt3").value)'>Compare Susam Method</button> 
    </body> 
</html> 
+1

He probado mi código (http://jsfiddle.net/YrQ4C/) en Firefox, Chrome en Linux, así como en Windows, así como en IE8, y funciona en los tres navegadores. ¿Qué navegador usaste cuando mi código no funcionó para ti? ¿Qué error obtuviste en la consola de JavaScript? –

+0

ha editado su respuesta. su versión anterior tenía este código. Pruébalo y dime si esto funciona en todos los navegadores. Si he visto su edición, no habría publicado una versión actualizada. Si eso te molesta y reduce un voto, muchas gracias. función compare (a, b, op) { if (eval (a + op + b)) do_something() } – Esen

+1

Si revisa mi código actual, tiene el código que menciona 'if (eval (a + op + b)) doSomething(); '. Este es el mismo código que estaba presente en mi primera respuesta incompleta. Más tarde, agregué que solo agregué las comprobaciones de seguridad para asegurarme de que la solución esté completa. Todavía no me has dicho qué navegador no pudo ejecutar 'if (eval (a + op + b)) doSomething();'. Este código en mi solución actual se ejecuta en los tres navegadores. –

Cuestiones relacionadas