Asignar variable en la declaración de condición if, ¿buena práctica o no? [cerrado]

113

Hace un año me mudé de los lenguajes OO clásicos como Java a JavaScript. El siguiente código definitivamente no se recomienda (o incluso no es correcto) en Java:

if(dayNumber = getClickedDayNumber(dayInfo))
{
    alert("day number found : " + dayNumber);
}
function getClickedDayNumber(dayInfo)
{
    dayNumber = dayInfo.indexOf("fc-day");
    if(dayNumber != -1) //substring found
    {
        //normally any calendar month consists of "40" days, so this will definitely pick up its day number.
        return parseInt(dayInfo.substring(dayNumber+6, dayNumber+8));
    }
    else return false;
}

Básicamente, acabo de descubrir que puedo asignar una variable a un valor en una declaración de condición if e inmediatamente verificar el valor asignado como si fuera booleano.

Para una apuesta más segura, generalmente lo separo en dos líneas de código, primero lo asigno y luego verifico la variable, pero ahora que encontré esto, me pregunto si es una buena práctica o no a los ojos de los desarrolladores de JavaScript experimentados.

Michael Mao
fuente
"The following code is definitely not recommended (or event not correct) in Java..."¿Es incluso correcto en JavaScript? Porque, por lo que puedo ver, devuelve un entero ( return parseInt(...)) si dayNumber != -1es verdadero, pero un booleano si es falso.
Daniel Kvist

Respuestas:

117

Yo no lo recomendaría. El problema es que parece un error común en el que intenta comparar valores, pero usa un solo en =lugar de ==o ===. Por ejemplo, cuando ve esto:

if (value = someFunction()) {
    ...
}

no sabes si eso es lo que querían hacer o si tenían la intención de escribir esto:

if (value == someFunction()) {
    ...
}

Si realmente desea hacer la tarea en su lugar, le recomendaría hacer una comparación explícita también:

if ((value = someFunction()) === <whatever truthy value you are expecting>) {
    ...
}
Matthew Crumley
fuente
1
@Matthew Crumley: esto responde a mi pregunta de una manera clara. No estoy verificando asignando, sino verificando lo que el valor evalúe después de la asignación. ¿Es correcto este entendimiento?
Michael Mao
1
@Michael: sí, eso es correcto. Agregar la comparación básicamente solo hace que sus intenciones sean más claras.
Matthew Crumley
4
Sin embargo, el último ejemplo no funciona si está probando el error / éxito de una función que devuelve un booleano. En otras palabras, while if (resultArr = myNeedle.exec(myHaystack)) {...}funciona, if ((resultArr = myNeedle.exec(myHaystack)) === true) {...}no porque la asignación a resultArr es siempre verdadera incluso cuando la función result no lo es. Si alguien usa esta construcción .., recuerde declarar primero la variable de resultado; 'var' no es legal dentro de la declaración de condición if.
Ville
3
Puedes usar if (!!(value = someFunction())), pero como dijiste, el problema es que no puedes usar varinside, ifpor lo que terminas creando un global o no logras nada, ya que valuede todos modos tienes que declarar en una línea separada. Es una pena, me gustó mucho esta construcción en C ++.
riv
1
@riv, tienes razón; en caso de que la función devuelva un booleano, como dije en mi comentario anterior, entonces el condicional funciona como se esperaba. Pero si la función devuelve un valor booleano (como en mi ejemplo), entonces toda la construcción no tiene sentido; claramente, mi línea de pensamiento fue, a juzgar por el ejemplo, que la función devolvería una matriz. Una prueba rápida indica que la condición se evalúa truesolo cuando la función regresa true, pero en todos los demás casos (incluso cuando se devuelve una matriz, cadena, número o nulo) se evalúa false.
Ville
28

No veo ninguna prueba de que no sea una buena práctica. Sí, puede parecer un error, pero se remedia fácilmente con comentarios juiciosos. Tomemos, por ejemplo:

if (x = processorIntensiveFunction()) { // declaration inside if intended
    alert(x);
}

¿Por qué debería permitirse que esa función se ejecute por segunda vez con:

alert(processorIntensiveFunction());

¿Porque la primera versión se ve mal? No puedo estar de acuerdo con esa lógica.

Adrian Bartolomé
fuente
32
No quiero desenterrar un comentario antiguo, pero no estoy de acuerdo con sus argumentos. El código legible debe explicarse por sí mismo sin la necesidad de un comentario; agregar un comentario al código confuso no es una solución. En cuanto a la segunda parte, que dice que la alternativa es volver a llamar a la función, no creo que nadie tenga la intención de hacer eso. En su lugar, lo haríax = processorItensiveFunction(); if(x) { alert(x); }
maksim
9
@maksim: Me gusta el código legible, pero eso no significa necesariamente que el código deba ser simplificado o demasiado detallado. Distribuir cosas en varias líneas y hacer malabares con los valores entre variables puede conducir a un código peor. El código insertado puede tener efectos secundarios imprevistos en un lenguaje flexible / débilmente escrito como JS. La asignación en una declaración condicional es válida en javascript, porque simplemente pregunta "si la asignación es válida, haga algo que posiblemente incluya el resultado de la asignación". Pero de hecho, asignar antes del condicional también es válido, no es demasiado detallado y se usa más comúnmente.
okdewit
1
@maksim ¿por qué crees if ( ! x = anyFunction() )que no es legible? No necesita ningún comentario.
JDrake
Si arregla OPC, trabaja con otros desarrolladores de diferentes niveles de habilidad (en otras palabras, es un profesional) odiará que esto sea posible.
davidjmcclelland
2
@maksim: también su solución sería muy inconveniente en una if-elsesituación. Considere: if (condition) {...} else if (x = processorIntensiveFunction()) {alert(x)} su anterior x = processorIntensiveFunction();sería un esfuerzo en vano si la inicial conditionfuera cierta.
Adrian Bartholomew
16

Lo hice muchas veces. Para omitir la advertencia de JavaScript, agrego dos parens:

if ((result = get_something())) { }

Debes evitarlo, si realmente quieres usarlo, escribe un comentario encima diciendo lo que estás haciendo.

Ming-Tang
fuente
1
@SHiNKiROU: ¿cómo puedo ver las advertencias de JavaScript? ¿Existe un compilador de Javascript? ¿O el intérprete generará algún tipo de advertencia? Estoy usando la consola de Firefox como en la depuración de JavaScript todo el tiempo, pero nunca veo salidas similares. Perdón por mi experiencia limitada.
Michael Mao
5
@Michael: JSLint ( jslint.com ) es un programa / biblioteca popular que comprueba los programas JavaScript en busca de posibles errores o código incorrecto.
Matthew Crumley
Utilice Mozilla Firefox con Firebug y / o la extensión Web Developer para comprobar las advertencias.
Ming-Tang
Lo intenté con if ((a = [1, 2]).length > 0) { console.log(a); }where aaún no se ha inicializado en ninguna parte y de hecho funcionó (¡bueno! Hace que usar expresiones regulares sea mucho más fácil). ¿Es correcto que no necesito nada de var|const|letaquí? ¿Sabes dónde puedo leer más sobre este truco ?
t3chb0t
4

También puede hacer esto en Java. Y no, no es una buena práctica. :)

(Y use ===en Javascript para la igualdad de tipos. Lea el libro The Good Parts de Crockford sobre JS).

Ben Zotto
fuente
@quixoto: ¿Puedo hacer este truco en Java? Me pregunto ... No tengo jdk a mano en un cajero automático, por lo que no puedo obtener un código de muestra en Java. De mi mala memoria, Java solo te dará un error de tiempo de ejecución si el valor de retorno evalúa algo no booleano como en la declaración condicional if, ¿verdad?
Michael Mao
1
Ah, sí, en Java se verifica que sea un tipo booleano. Pero usted puede hacerif (foo = getSomeBoolValue()) { }
Ben Zotto
sí es cierto. una variable booleana para probar si algo tuvo éxito y otra variable para almacenar el valor devuelto. Así es como Java hace su trabajo, estoy demasiado familiarizado con eso, así que me siento extraño al ver que Javascript puede hacer dos cosas en una sola línea :)
Michael Mao
@BenZotto no es una buena práctica ¿por qué? "Para evitar un uso indebido accidental de una variable, suele ser una buena idea introducir la variable en el ámbito más pequeño posible. En particular, suele ser mejor retrasar la definición de una variable hasta que se le pueda dar un valor inicial ... Una de las aplicaciones más elegantes de estos dos principios es declarar una variable en un condicional ". - Stroustrup, "El lenguaje de programación C ++".
JDrake
1
Hola, vine aquí como usuario javascript de node.js. Por qué no es una buena práctica en un caso con el que tuve dolor: if (myvar = 'just a test') Crea una variable GLOBAL de node.js myvar ( nodejs.org/docs/latest-v12.x/api/globals.html #globals_global ). Entonces, si es como yo y usó esa variable en el manejo de solicitudes del servidor (volviendo a ella después de unos segundos cuando otras solicitudes podrían haber aparecido y cosas así), puede sorprenderse de los resultados que obtiene. Entonces, la recomendación es: tenga en cuenta que este patrón crea una variable global en node.js.
pein-consulting.de
4

Hay un caso en el que lo haces, con while-loops.
Cuando lee archivos, normalmente lo hace así:

void readFile(String pathToFile) {
    // Create a FileInputStream object
    FileInputStream fileIn = null;
    try {
        // Create the FileInputStream
        fileIn = new FileInputStream(pathToFile);
        // Create a variable to store the current line's text in
        String currentLine;
        // While the file has lines left, read the next line,
        // store it in the variable and do whatever is in the loop
        while((currentLine = in.readLine()) != null) {
            // Print out the current line in the console
            // (you can do whatever you want with the line. this is just an example)
            System.out.println(currentLine);
        }
    } catch(IOException e) {
        // Handle exception
    } finally {
        try {
            // Close the FileInputStream
            fileIn.close();
        } catch(IOException e) {
            // Handle exception
        }
    }
}

Mire el while-loop en la línea 9. Allí, se lee una nueva línea y se almacena en una variable, y luego se ejecuta el contenido del bucle. Sé que esto no es una ifdeclaración-, pero supongo que también se puede incluir un ciclo while en su pregunta.

La razón de esto es que cuando usa a FileInputStream, cada vez que llama FileInputStream.readLine(), lee la siguiente línea en el archivo, por lo que si lo hubiera llamado desde el bucle con solo fileIn.readLine() != nullsin asignar la variable, en lugar de llamar (currentLine = fileIn.readLine()) != null, y luego lo hubiera llamado desde dentro del bucle también, solo obtendría cada segunda línea.

Espero que lo entiendas y ¡buena suerte!

Daniel Kvist
fuente
3

También puede hacer asignaciones dentro de declaraciones if en Java. Un buen ejemplo sería leer algo y escribirlo:

http://www.exampledepot.com/egs/java.io/CopyFile.html?l=new

El código:

// Copies src file to dst file.
// If the dst file does not exist, it is created
void copy(File src, File dst) throws IOException 
{
    InputStream in = new FileInputStream(src);
    OutputStream out = new FileOutputStream(dst);

    // Transfer bytes from in to out
    byte[] buf = new byte[1024];
    int len;
    while ((len = in.read(buf)) > 0) {
        out.write(buf, 0, len);
    }
    in.close();
    out.close();
}
Nitrodista
fuente
@Nitrodist: gracias por este ejemplo. Realmente no soy profesional ni en Java ni en javascript ... Es bueno saber que este enfoque también es factible en Java :)
Michael Mao
No veo el sentido de esto. Puedes hacerlo en Java, PHP y muchos otros lenguajes. La pregunta era sobre Javascript.
pmrotule
No, no es necesariamente, debe volver a leer la pregunta con atención.
Nitrodist
3

¡Si tuviera que consultar el libro de Martin Fowlers Refactorización mejorando el diseño del código existente ! Luego, hay varios casos en los que sería una buena práctica, por ejemplo. condicionales largos y complejos para usar una función o llamada a un método para afirmar su caso:

"Motivación

Una de las áreas de complejidad más comunes en un programa radica en la lógica condicional compleja. A medida que escribe código para probar condiciones y para hacer varias cosas dependiendo de varias condiciones, rápidamente termina con un método bastante largo. La longitud de un método es en sí mismo un factor que dificulta la lectura, pero las condiciones aumentan la dificultad. El problema generalmente radica en el hecho de que el código, tanto en las verificaciones de condición como en las acciones, le dice lo que sucede, pero puede ocultar fácilmente por qué sucede.

Al igual que con cualquier bloque grande de código, puede aclarar su intención descomponiéndolo y reemplazando fragmentos de código con una llamada a un método con el nombre de la intención de ese bloque de código. > Con condiciones, puede recibir un beneficio adicional al hacer esto para la parte condicional y cada una de las alternativas. De esta manera, resalta la condición y deja claramente en qué se está ramificando. También destaca el motivo de la ramificación ".

Y sí, su respuesta también es válida para implementaciones de Java. Aunque no asigna la función condicional a una variable en los ejemplos.

Alano
fuente
1

No es una buena práctica. Pronto te confundirás al respecto. Parece similar a un error común: mal uso de los operadores "=" y "==".

Debes dividirlo en 2 líneas de códigos. No solo ayuda a que el código sea más claro, sino que también es fácil de refactorizar en el futuro. ¿Imagina que cambia la condición IF? Puede eliminar accidentalmente la línea y su variable ya no obtendrá el valor asignado.

thethanghn
fuente
@thethanghn: eso es exactamente lo que temo. cuando sea mayor y perezoso, simplemente no quiero escribir más en el código si menos pulsaciones de teclas son suficientes :)
Michael Mao
1
No, no me confundo y lo hago todo el tiempo. Tiene beneficios.
JDrake
Sin embargo, realmente depende, ¿no? Si viene de un entorno 'C' (y otros lenguajes basados ​​en C), entonces la construcción es muy familiar y las alternativas son muy incómodas. En mi opinión, es algo que se aprende una vez y luego se sabe. No es algo con lo que te tropieces más de una vez.
Max Waterman
0

Consideraría esto más como un estilo C de la vieja escuela; no es una buena práctica en JavaScript, por lo que debe evitarlo.

Justin Ethier
fuente
8
Tampoco lo considero una buena práctica en C.
Matthew Crumley
1
Lo considero una buena práctica en muchos idiomas.
JDrake
Simplemente decir 'no es una buena práctica' no es suficiente, en mi opinión. En realidad, se trata solo de educación, se aprende una vez, y eso es todo.
Max Waterman
0

podrías hacer algo como esto:

if (value = /* sic */ some_function()){
  use_value(value)
}
Joseph Le Brech
fuente
0

Vine aquí desde Golang, donde es común ver algo como

if (err := doSomething(); err != nil) {
    return nil, err
}

En el que errestá limitado a ese ifbloque únicamente. Como tal, esto es lo que estoy haciendo en es6, que parece bastante feo, pero no hace que mis estrictas reglas de eslint se quejen, y logra lo mismo.

{
  const err = doSomething()
  if (err != null) {
    return (null, err)
  }
}

¿Las llaves extra definen un nuevo, eh, "alcance léxico"? Lo que significa que puedo usar consty errno está disponible para el bloque externo.

Adam Barnes
fuente