2010-08-12 6 views
23

Estoy intentando crear una declaración de cambio, pero parece que no puedo usar una expresión que se evalúa (en lugar de una cadena de caracteres/un número entero). Puedo hacer esto fácilmente con sentencias if, pero espero que el caso sea más rápido.Expresión dentro de la declaración de la caja del interruptor

Estoy tratando los siguientes

function reward(amount) { 
    var $reward = $("#reward"); 
    switch (amount) { 
     case (amount >= 7500 && amount < 10000): 
      $reward.text("Play Station 3"); 
      break; 
     case (amount >= 10000 && amount < 15000): 
      $reward.text("XBOX 360"); 
      break; 
     case (amount >= 15000): 
      $reward.text("iMac"); 
      break; 
     default: 
      $reward.text("No reward"); 
      break; 
    } 
} 

Me estoy perdiendo algo obvio o no es esto posible? Google no ha sido amistoso en este caso.

Cualquier ayuda/punteros apreciados

M

Respuesta

78

Siempre se puede hacer

switch (true) { 
    case (amount >= 7500 && amount < 10000): 
    //code 
    break; 
    case (amount >= 10000 && amount < 15000): 
    //code 
    break; 

    //etc... 

Funciona porque true es una constante, por lo que se ejecutará el código debajo de la primera sentencia case con una expresión que se evalúa como verdadera.

Supongo que es un poco "complicado", pero no veo nada de malo en usarlo. Una declaración simple de if/else probablemente sería más conciso, y no tendría que preocuparse por una caída accidental. Pero ahí está de todos modos.

+3

Esto es mejor como respuesta que la "respuesta" de Daniels. Con una ligera advertencia: todas las expresiones anteriores a la que conduce al caso verdadero también serán evaluadas. ten cuidado. –

+1

Sí, esto funciona porque siempre se puede pensar en una función de interruptor como una tabla de salto, solo tiene que coincidir y recuperar un valor. Esto es diferente de las sentencias if/else porque todas las if deben evaluarse realmente. Por encima de ti estás pidiendo a tu interruptor que evalúe el partido. Es por eso que los casos de cambio son más rápidos. – Vontei

+0

@Vontei: con una estructura if/else if/else no se evalúan * todos *, solo se evalúan hasta que una condición en particular sea verdadera, que es lo mismo que ocurre al evaluar expresiones 'case'. – nnnnnn

2

En primer lugar, no es así como switch obras. Debe especificar las constantes para cada case, y esas constantes se compararán con la expresión entre paréntesis (en su caso, amount). Así es como funciona switch, punto.

En segundo lugar, el interruptor no es más rápido que varios if s

Y en tercer lugar, que en realidad no debería preocuparse de optimizaciones de rendimiento minúsculas cuando se está tratando con javascript.

+0

Bueno, siempre es mejor preocuparse por el rendimiento. Te da espacio cuando tienes que hacer algo que consuma mucho rendimiento. – MindlessRanger

+0

* "Debe especificar constantes para cada caso" * - No, no. El uso de constantes es la forma más común de usar un 'interruptor', pero puede especificar cualquier expresión en cada 'caso', con o sin variables o llamadas a funciones, y el valor es lo que se compara. – nnnnnn

1

El problema es que la expresión de cambio no puede igualar las expresiones de caso, porque la expresión de caso se evaluará como verdadera o falsa, pero la expresión de cambio será un número.

La solución donde la expresión del interruptor se establece en verdadero no funciona porque true es una constante, sino porque la igualdad con las expresiones de casos es realmente posible.

No es cierto que tenga que especificar constantes para cada expresión de caso.

para respaldar mi respuesta, se refieren a Douglas Crockford, Javascript las partes buenas (2008), página 12:

La sentencia switch realiza una rama de múltiples vías.Compara la expresión de igualdad con todos los casos seleccionados ... Cuando se encuentra una coincidencia exacta, se ejecutan las declaraciones de la cláusula de caso coincidente ... Una cláusula de caso contiene una o más expresiones de caso. Las expresiones de caso no necesitan ser constantes.

+0

Podría agregarse que la evaluación del bloque de casos se realiza en el orden del código fuente. De arriba a abajo. – dotnetCarpenter

8

@ MooGoo de switch (true) le dará un Weird condition error in jsLint, así que vamos a conseguir un poco más creativos en caso que es un problema, y, creo, aumentar la legibilidad un toque.

Así que no estamos evaluando si cada case es true o false; estamos comparando si el valor de case es igual a nuestro término switch. Así que aprovechemos eso lanzando una abreviatura if en nuestra declaración case y devolveremos nuestro término original del interruptor si la condición es verdadera.

También incluyo un tipo de ejemplo del mundo real, donde desea tener dos "valores predeterminados": uno si su término está fuera de su rango "importante" en la dirección positiva, y otro en caso de que ' re en la dirección negativa.

Frase clave: case (x > 0 ? x : null):

"Si mi mandato, x, es mayor que cero, de manera que regresan xx === x y tomo la rama caso".

http://jsfiddle.net/rufwork/upGH6/1/

/*global document*/ 
/*jslint evil:true*/ 
var x = 10; 

switch (x) { 
    case (x > 0 ? x : null): 
     document.write('ha ha ha! I fooled switch AND jsLint! Muhahahahaha!'); 
     break; 
    case 0: 
     document.write('zero is nothing.'); 
     break; 
    case -1: 
     document.write('low'); 
     break; 
    case -2: 
     document.write('lower'); 
     break; 
    case -3: 
     document.write('lowest I care about'); 
     break; 
    default: // anything lower than -3. 
     document.write('TOO LOW!!!! (unless you cheated and didn\'t use an int)'); 
} 
document.write('<br>done.'); 
+0

Esto puede engañar a JSLint, pero es más extraño y difícil de leer que lo que JSLint se quejaba en primer lugar. Si has tomado la decisión consciente de usar un 'interruptor' con expresiones en los casos como el OP deseado y MooGoo explicado, entonces no vas a preocuparte por esa advertencia JSLint en particular. – nnnnnn

+0

@nnnnnn Bueno, yo diría que 'switch (true)' es completamente antipatrón, ya que el MooGoo está esencialmente recreando 'if ... else if ... else'. Mi punto principal es que la pregunta me hace pensar en un uso posiblemente discutible para los rangos en los interruptores - ** si te encuentras en un paradigma de 'cambio' que esencialmente necesita dos (+?) Valores predeterminados **. Limpio saber que puedes usar una expresión en un 'caso', devolver el valor de param de' switch' y forzar la invocación en esa declaración. Pero parece que estás votando por las respuestas "* that's not that how switch works *" de @Daniel & @FyodorSoikin, y realmente no puedo argumentar eso;) – ruffin

1

También puede probar uno de mis construcciones preferidas:

function reward(amount) { 
    var $reward = $("#reward"); 
    $reward.text(
     (amount >= 7500 && amount < 10000) ? "Play Station 3" : 
     (amount >= 10000 && amount < 15000)? "XBOX 360" : 
     (amount >= 15000) ?      "iMac" : 
               "No reward" 
    ); 
} 
+0

¿Todavía deberías verificar que el monto esté debajo de un número si usted los evaluó en reversa? –

+0

Supongo, por supuesto, que impediría a las personas con mayores recompensas obtenerlas más rápidamente;) –

+2

Los operadores ternarios anidados a menudo son temidos y desalentados por completo. Por lo tanto, es mejor evitar usarlos a menos que sean más simples, evidentes y más fáciles de mantener que las construcciones alternativas. Prefiero las pruebas que funcionan en cualquier orden. Independientemente de cómo elija escribirlos, la simplicidad y la facilidad de mantenimiento triunfan sobre la eficiencia aquí. – robert

0

Mis 2 centavos:

interruptor ideal (como principio) debe evaluar a un solo caso sucursal, logrando así O (1) rendimiento y (aparte de caer en los casos) las declaraciones de casos se pueden reordenar de cualquier manera sin cambiar la estrategia de ramificación del compilador y

Si se utilizan expresiones (suponiendo que el idioma lo permita), entonces teóricamente, puede seguir más que una bifurcación.

El compilador (que no sean aquellos que pueden decir inteligentemente lo que el desarrollador está tratando de hacer) no podrá optimizar la estrategia de ramificación de manera estática e idealmente perdiendo así su eficacia.

Ejemplo:

var x = 6, factors = []; 

switch(x){ 

    case (x%2 == 0): factors.push(2); 
    break; 

    case (x%3 == 0): factors.push(3); 
    break; 
    .... 
} 

{Esperar comentarios en código pobres}

En el ejemplo anterior, no hay manera práctica para que el compilador para optimizar de forma estática, por lo tanto, no obtiene ninguna ventaja de rendimiento sobre si otra cosa.

La única parte es que "puede" parecer más limpia para el desarrollador, pero podría causar interrupciones en caso de condiciones adicionales.

1

Bueno, puede tener expresiones en la declaración case por lo que su conmutador no es un error de sintaxis. Pero debe comprender que la Case Clause se compara usando === (comparación estricta).Una vez que comprenda esto, que el valor debe coincidir exactamente con el valor de expresión en su switch(expression), puede buscar expresiones en js.

llamadas a funciones son expresiones, por lo que vamos a tratar con ellos:

function xbox(amount) { return amount >= 10000 && amount < 15000 && amount; } 

function reward(amount) { 
    var ps3 = function(amount) { return amount >= 7500 && amount < 10000 && amount; } 

    function imac(amount) { return amount >= 15000 && amount; } 

    var $reward = $("#reward"); 
    switch (amount) { 
    case ps3(amount): 
     $reward.text("Play Station 3"); 
     break; 
    case xbox(amount): 
     $reward.text("XBOX 360"); 
     break; 
    case imac(amount): 
     $reward.text("iMac"); 
     break; 
    default: 
     $reward.text("No reward"); 
     break; 
    } 
} 
reward(8200)// -> Play Station 3 
reward(11000)// -> XBOX 360 
reward(20000)// -> iMac 

Como se puede ver, se puede ambas expresiones de funciones de uso y definiciones de funciones. No importa. Solo que la expresión en la cláusula de caso es una expresión para evaluar. Lo cual es lo mismo que tú, solo que no devolviste un valor que era igual al monto, sino más bien un valor verdadero o falso. En mi ejemplo, devuelvo la cantidad exacta si mi condición es verdadera, por lo tanto, disparo la comparación para que coincida.

Aquí es su código fijo:

function reward(amount) { 
    var $reward = $("#reward"); 
    switch (amount) { 
     case (amount >= 7500 && amount < 10000 && amount): 
      $reward.text("Play Station 3"); 
      break; 
     case (amount >= 10000 && amount < 15000 && amount): 
      $reward.text("XBOX 360"); 
      break; 
     case (amount >= 15000 && amount): 
      $reward.text("iMac"); 
      break; 
     default: 
      $reward.text("No reward"); 
      break; 
    } 
} 

Aquí es la especificación: https://tc39.github.io/ecma262/#sec-switch-statement El enlace es a es2016 porque es más fácil para buscar que el anterior pdf ES3 de 1999. Pero siempre ha trabajado de esta manera, pero es un hecho poco conocido.

Sin embargo, dudo que esto sea más rápido que las declaraciones if. Si desea que su ejecución se ejecute rápidamente, entonces no toque el DOM.

Cuestiones relacionadas