JavaScript OR (||) explicación de asignación de variable

351

Dado este fragmento de JavaScript ...

var a;
var b = null;
var c = undefined;
var d = 4;
var e = 'five';

var f = a || b || c || d || e;

alert(f); // 4

¿Puede alguien explicarme cómo se llama esta técnica (mi mejor conjetura está en el título de esta pregunta)? ¿Y cómo / por qué funciona exactamente?

Entiendo que a la variable fse le asignará el valor más cercano (de izquierda a derecha) de la primera variable que tenga un valor que no sea nulo o indefinido, pero no he logrado encontrar mucho material de referencia sobre esta técnica y tengo visto que se usa mucho.

Además, ¿es esta técnica específica de JavaScript? Sé que hacer algo similar en PHP daría como resultado fun verdadero valor booleano, en lugar del valor de dsí mismo.

chattsm
fuente
44
Cuestión de edad, sino en relación con PHP, hay una construcción que puede utilizar: $f=$a or $f=$b or $f=$c; // etc. PHP tiene tanto el ||operador como el oroperador, que hacen el mismo trabajo; sin embargo, orse evalúa después de la asignación mientras ||se evalúa antes. Esto también le da el estilo de$a=getSomething() or die('oops');
Manngo
1
En PHP 5.3 puedes dejar fuera la parte media del operador ternario, así que basándote en eso ... También puedes acortarlo un poco en algo como esto: $f = $a ?: $b ?: $c;
Rei
1
A partir de PHP 7 puede usar ??para esto. $a = $b ?? 'default'
Spencer Ruskin
@SpencerRuskin, ¿ $ase le asignará el valor de $bsi $bes verdadero, otro 'default'?
oldboy
Así es. Mire la sección de operador de fusión
Spencer Ruskin

Respuestas:

212

Ver evaluación de cortocircuito para la explicación. Es una forma común de implementar estos operadores; No es exclusivo de JavaScript.

relajarse
fuente
57
Solo tenga en cuenta el 'gotcha', que es que el último siempre se asignará incluso si todos son indefinidos, nulos o falsos. Establecer algo que sabe que no es falso, nulo o indefinido al final de la cadena es una buena manera de indicar que no se encontró nada.
Erik Reppen
He visto esta técnica durante años, pero lo que me llamó la atención justo cuando quería usarla es que el resultado de la expresión no se convierte en booleano. No puedes hacer más tarde if( true == f ). Si se almacenó un número entero en f, entonces esta prueba siempre devolverá falso.
99
En realidad, puedes hacer if(true == f), que es lo mismo que if(f): la prueba pasará. Si también desea probar el tipo de f, utilice una comparación estricta: if(true === f)que fallará de hecho.
Alsciende
55
Sí, la evaluación de cortocircuito es común. Pero la distinción aquí radica en la forma en que JavaScript devuelve el último valor que detuvo la ejecución. La respuesta de @ Anurag explica mucho mejor esto.
Ben.12
No estoy seguro si esa es la mejor explicación para los principiantes. Yo recomendaría: javascript.info/logical-operators
csguy
198

Esto se hace para asignar un valor predeterminado , en este caso el valor de y, si la xvariable es falsa .

Los operadores booleanos en JavaScript pueden devolver un operando, y no siempre un resultado booleano como en otros idiomas.

El operador lógico OR ( ||) devuelve el valor de su segundo operando, si el primero es falso, de lo contrario se devuelve el valor del primer operando.

Por ejemplo:

"foo" || "bar"; // returns "foo"
false || "bar"; // returns "bar"

Falsy valores son los que coaccionar a falsecuando se utiliza en contexto booleano, y son 0, null, undefined, una cadena vacía, NaNy por supuesto false.

CMS
fuente
1
+1 ¿Hay otro operador así? O es ||exclusivo.
OscarRyz
99
@Support (@Oscar): el &&operador lógico tiene un comportamiento similar, devuelve el valor del primer operando si es falso y devuelve el valor del segundo operando, solo si el primero es verdadero , por ejemplo, ("foo" && "bar") == "bar"y(0 && "bar") == 0
CMS
12
Falsy es, de hecho, el término técnico.
ChaosPandion
8
Entonces aprendimos sobre ||, &&, "Falsy" y "Truly" en esta publicación. La mejor respuesta con regalos "ocultos".
Alex
44
@Alex NB: "Truthy" (! "Truly")
Bumpy
183

Javacript utiliza la evaluación de cortocircuito para operadores lógicos ||y &&. Sin embargo, es diferente a otros idiomas en que devuelve el resultado del último valor que detuvo la ejecución, en lugar de un valor trueo false.

Los siguientes valores se consideran falsos en JavaScript.

  • falso
  • nulo
  • "" (cuerda vacía)
  • 0 0
  • Yaya
  • indefinido

Ignorando las reglas de precedencia del operador y manteniendo las cosas simples, los siguientes ejemplos muestran qué valor detuvo la evaluación y se devuelve como resultado.

false || null || "" || 0 || NaN || "Hello" || undefined // "Hello"

Los primeros 5 valores hasta NaNson falsos, por lo que todos se evalúan de izquierda a derecha, hasta que cumpla con el primer valor "Hello"verdadero, lo que hace que toda la expresión sea verdadera, por lo que todo lo que esté más arriba no se evaluará y "Hello"se devolverá como resultado de la expresión . Del mismo modo, en este caso:

1 && [] && {} && true && "World" && null && 2010 // null

Los primeros 5 valores son todos verdaderos y se evalúan hasta que cumple con el primer valor falso ( null) que hace que la expresión sea falsa, por 2010lo que ya no se evalúa y nullse devuelve como resultado de la expresión.

El ejemplo que ha dado es hacer uso de esta propiedad de JavaScript para realizar una tarea. Se puede usar en cualquier lugar donde necesite obtener el primer valor verdadero o falso entre un conjunto de valores. Este código a continuación asignará el valor "Hello"a b, ya que facilita la asignación de un valor predeterminado, en lugar de hacer verificaciones if-else.

var a = false;
var b = a || "Hello";

Podría llamar al ejemplo siguiente una explotación de esta característica, y creo que hace que el código sea más difícil de leer.

var messages = 0;
var newMessagesText = "You have " + messages + " messages.";
var noNewMessagesText = "Sorry, you have no new messages.";
alert((messages && newMessagesText) || noNewMessagesText);

Dentro de la alerta, verificamos si messageses falso y, en caso afirmativo, evaluamos y devolvemos noNewMessagesText, de lo contrario, evaluamos y devolvemos newMessagesText. Como es falso en este ejemplo, nos detenemos en noNewMessagesText y alertamos "Sorry, you have no new messages.".

Anurag
fuente
36
Esta es la mejor respuesta en mi opinión debido a la siguiente explicación:However, it's different to other languages in that it returns the result of the last value that halted the execution, instead of a true, or false value.
mastazi
1
@mastazi Sí, debería ir en negrita en mi humilde opinión.
noviembre
66
Debería ser la respuesta, muestra los valores elegidos sobre los casos de prueba.
Dadan
De acuerdo, esta es mi respuesta favorita, ya que aborda específicamente las preocupaciones de asignación de variables de JavaScript. Además, si elige usar un ternario como una de las variables posteriores para probar la asignación (después del operador), debe ajustar el ternario entre paréntesis para que la evaluación de la asignación funcione correctamente.
Joey T
esta debería ser la respuesta aceptada
Alan
49

Las variables de Javascript no están escritas, por lo que a f se le puede asignar un valor entero aunque se haya asignado a través de operadores booleanos.

A f se le asigna el valor más cercano que no es equivalente a falso . Entonces 0, falso, nulo, indefinido, se pasan por alto:

alert(null || undefined || false || '' || 0 || 4 || 'bar'); // alerts '4'
Alsciende
fuente
13
No olvides ''también igual falso en este caso.
Brigante
Vota por señalar lo que f is assigned the NEAREST valuees un punto bastante importante aquí.
steviejay
3
"Más cercano" no es del todo cierto, aunque tiene esa apariencia. El ||operador booleano , al ser un operador booleano, tiene dos operandos: un lado izquierdo y un lado derecho. Si el lado izquierdo del ||es verdadero , la operación se resuelve hacia el lado izquierdo y se ignora el lado derecho. Si el lado izquierdo es falso , se resuelve hacia el lado derecho. Por null || undefined || 4 || 0lo tanto, en realidad se resuelve undefined || 4 || 0qué resuelve a 4 || 0qué se resuelve 4.
devios1
29

No hay ninguna magia en ello. Las expresiones booleanas como a || b || c || dse evalúan perezosamente. Interpeter busca el valor de a, es indefinido, por lo que es falso, por lo que sigue adelante, luego ve bcuál es nulo, lo que todavía da un resultado falso, por lo que sigue adelante, luego ve la cmisma historia. Finalmente ve dy dice 'eh, no es nulo, así que tengo mi resultado' y lo asigna a la variable final.

Este truco funcionará en todos los lenguajes dinámicos que hacen una evaluación de cortocircuito diferido de expresiones booleanas. En lenguajes estáticos no se compilará (error de tipo). En los lenguajes que están ansiosos por evaluar expresiones booleanas, devolverá un valor lógico (es decir, verdadero en este caso).

Marcin
fuente
66
En el lenguaje bastante estático, C # one puede usar el ?? operador á la: objeto f = a ?? b ?? C ?? d ?? mi;
herzmeister el
2
herzmeister - gracias! No sabia eso ?? El operador se puede encadenar en C # y usar en técnicas de evaluación perezosa
Marek
3
Como se mencionó en otra parte, ese último dse asignará si fue nulo / indefinido o no.
BlackVegetable
Una ligera corrección: el ||operador siempre resuelve todo el operando del lado derecho cuando el lado izquierdo es falso. Al ser un operador booleano, solo ve dos entradas: el lado izquierdo y el lado derecho. El analizador no los ve como una serie de términos, por lo que en realidad no se detiene cuando encuentra el primer valor verdadero a menos que ese valor sea también el operando de la mano izquierda de otro ||.
devios1
8

Esta pregunta ya ha recibido varias buenas respuestas.

En resumen, esta técnica aprovecha una característica de cómo se compila el lenguaje. Es decir, JavaScript "cortocircuita" la evaluación de los operadores booleanos y devolverá el valor asociado con el primer valor de la variable no falsa o lo que contenga la última variable. Vea la explicación de Anurag de esos valores que se evaluarán como falsos.

Usar esta técnica no es una buena práctica por varias razones; sin embargo.

  1. Legibilidad de código: Esto está utilizando operadores booleanos, y si no se entiende el comportamiento de cómo se compila, entonces el resultado esperado sería un valor booleano.
  2. Estabilidad: Esto está utilizando una característica de cómo se compila el idioma que es inconsistente en varios idiomas, y debido a esto es algo que podría ser objeto de cambios en el futuro.
  3. Características documentadas: existe una alternativa existente que satisface esta necesidad y es coherente en más idiomas. Este sería el operador ternario:

    ()? valor 1: valor 2.

El uso del operador ternario requiere un poco más de tipeo, pero distingue claramente entre la expresión booleana que se evalúa y el valor que se asigna. Además, se puede encadenar, por lo que se podrían recrear los tipos de tareas predeterminadas que se realizan anteriormente.

var a;
var b = null;
var c = undefined;
var d = 4;
var e = 'five';

var f =  ( a ) ? a : 
                ( b ) ? b :
                       ( c ) ? c :
                              ( d ) ? d :
                                      e;

alert(f); // 4
WSimpson
fuente
potentially be targeted for change in the future.Sí, pero no, eso se aplica a JavaScript.
Dadan
Vine aquí y vi todas las respuestas anteriores y estaba pensando para mí mismo que algo simplemente se desvió de la tarea. Acabo de leer Clean Code de Robert C Martin y este tipo de tarea definitivamente viola la regla "No tener efectos secundarios" ... mientras que el autor mismo afirma que su libro es solo una de las muchas técnicas para generar un buen código, yo Todavía estaba sorprendido de que nadie más se opusiera a este tipo de asignación. +1
Albert Rothman
Gracias por la respuesta. Creo que más personas deben tener en cuenta los efectos secundarios al escribir código, pero hasta que alguien haya pasado mucho tiempo manteniendo el código de otras personas. A menudo no lo consideran.
WSimpson
1
Realmente piensas que la monstruosidad es más clara que a || b || c || d || e ?
devios1
1
@AlbertRothman No veo ningún efecto secundario. Nada está siendo mutado. Es simplemente una abreviatura de fusión nula, que es una característica bastante común en muchos idiomas.
devios1
7

Devuelve el primer valor verdadero de salida .

Si todos son falsos, devuelva el último valor falso.

Ejemplo:-

  null || undefined || false || 0 || 'apple'  // Return apple
Arshid KV
fuente
4

Establece la nueva variable ( z) en el valor de xsi es "verdadero" (distinto de cero, un objeto / matriz / función válido / lo que sea) o de lo ycontrario. Es una forma relativamente común de proporcionar un valor predeterminado en caso de quex que no exista.

Por ejemplo, si tiene una función que toma un parámetro opcional de devolución de llamada, podría proporcionar una devolución de llamada predeterminada que no haga nada:

function doSomething(data, callback) {
    callback = callback || function() {};
    // do stuff with data
    callback(); // callback will always exist
}
Matthew Crumley
fuente
1

Significa que si xse establece, el valor zserá x, de lo contrario, si yse establece, su valor se establecerá como el zvalor de 's.

es lo mismo que

if(x)
  z = x;
else
  z = y;

Es posible porque los operadores lógicos en JavaScript no devuelven valores booleanos, pero el valor del último elemento necesario para completar la operación (en una oración OR sería el primer valor no falso, en una oración AND sería el último ) Si la operación falla, falsese devuelve.

Andris
fuente
55
¡esto está mal! si (x) {z = x; } else {z = y;} si el primer valor es falso, el segundo valor siempre se asigna sin depender del valor real.
evilpie
Excepto que creo que solo asigna y a z si x es falso . Así es como funciona para mí en FF, por supuesto, eso podría depender de la implementación también.
tvanfosson
77
La última parte acerca de devolver falso no es cierto (sin juego de palabras). Si el primer valor es falsey, el ||operador solo devuelve el segundo valor, independientemente de si es verdadero o no.
Matthew Crumley
-1. Su fragmento de código equivalente es preciso, pero el punto importante es que zse establece en el valor de xsi ese valor es verdadero . De lo contrario, se establece en el valor dey . Esto significa que si xse establece en, por ejemplo, 0o la cadena vacía "", esto no hace lo que usted dice, ya que esos valores son falsos .
Daniel Cassidy
1

Se llama operador de cortocircuito.

La evaluación de cortocircuito dice que el segundo argumento se ejecuta o evalúa solo si el primer argumento no es suficiente para determinar el valor de la expresión. cuando el primer argumento de la función OR (||) se evalúa como verdadero, el valor general debe ser verdadero.

También podría usarse para establecer un valor predeterminado para el argumento de la función.

function theSameOldFoo(name){ 
  name = name || 'Bar' ;
  console.log("My best friend's name is " + name);
}
theSameOldFoo();  // My best friend's name is Bar
theSameOldFoo('Bhaskar');  // My best friend's name is Bhaskar`
Vijay
fuente
0

Evaluará X y, si X no es nulo, la cadena vacía o 0 (falso lógico), entonces lo asignará a z. Si X es nulo, la cadena vacía o 0 (falso lógico), entonces asignará y a z.

var x = '';
var y = 'bob';
var z = x || y;
alert(z);

Producirá 'bob';

tvanfosson
fuente
Debe aclarar lo que quiere decir con "vacío". Las cadenas vacías obligan a false, pero las matrices u objetos vacíos obligan a true.
Daniel Cassidy
@Daniel "nulo, vacío o 0": se aplicaría nulo con respecto a las matrices y los objetos. Punto tomado, sin embargo.
tvanfosson
0

De acuerdo con la publicación del blog de Bill Higgins ; el lenguaje de asignación OR lógico de Javascript (febrero de 2007), este comportamiento es cierto a partir de v1.2 (al menos)

También sugiere otro uso para él (citado): " normalización ligera de las diferencias entre navegadores "

// determine upon which element a Javascript event (e) occurred
var target = /*w3c*/ e.target || /*IE*/ e.srcElement;
Alon Brontman
fuente