Explicação de atribuição de variável JavaScript OR (||)

351

Dado este trecho 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

Alguém pode me explicar como é chamada essa técnica (meu melhor palpite está no título desta pergunta!)? E como / por que funciona exatamente?

Meu entendimento é que a variável freceberá o valor mais próximo (da esquerda para a direita) da primeira variável que tem um valor que não é nulo ou indefinido, mas não consegui encontrar muito material de referência sobre essa técnica e visto que usava muito.

Além disso, essa técnica é específica para JavaScript? Eu sei que fazer algo semelhante no PHP resultaria em fter um verdadeiro valor booleano, e não o valor em dsi.

chattsm
fonte
4
Questão de idade, mas sobre PHP, há uma construção que você pode usar: $f=$a or $f=$b or $f=$c; // etc. O PHP possui o ||operador e o oroperador, que fazem o mesmo trabalho; no entanto, oré avaliado após a atribuição, enquanto ||é avaliado antes. Isso também lhe dá o estilo perlish de$a=getSomething() or die('oops');
Manngo 14/10
11
No PHP 5.3, você pode deixar de fora a parte do meio do operador ternário tão baseando disso ... Você também pode cortar que um pouco mais curto em algo assim: $f = $a ?: $b ?: $c;
Rei
11
A partir do PHP 7, você pode usar ??para isso. $a = $b ?? 'default'
Spencer Ruskin
@ SpencerRuskin, então $aserá atribuído o valor de $bse $bfor verdadeiro, outro 'default'?
Oldboy
Está certo. Veja a seção do operador de coalescência nula nesta página: php.net/manual/en/migration70.new-features.php
Spencer Ruskin

Respostas:

212

Consulte avaliação de curto-circuito para obter a explicação. É uma maneira comum de implementar esses operadores; não é exclusivo do JavaScript.

descontrair
fonte
57
Apenas lembre-se do 'pegadinha', que é o último sempre atribuído, mesmo que todos sejam indefinidos, nulos ou falsos. Definir algo que você sabe que não é falso, nulo ou indefinido no final da cadeia é uma boa maneira de sinalizar que nada foi encontrado.
Erik Reppen
Eu tenho visto essa técnica há anos, mas o que mais me impressionou quando eu queria usá-la é que o resultado da expressão não é convertido em booleano. Você não pode fazer mais tarde if( true == f ). Se um número inteiro foi armazenado em f, esse teste sempre retornará falso.
9
Na verdade, você pode fazer if(true == f)o mesmo if(f): o teste será aprovado. Se você também quiser testar o tipo de f, use comparação estrita:, o if(true === f)que realmente falhará.
Alsciende
5
Sim, a avaliação de curto-circuito é comum. Mas a distinção aqui reside na maneira como o JavaScript retorna o último valor que interrompeu a execução. A resposta de @ Anurag faz um trabalho muito melhor ao explicar isso.
Ben.12
não tenho certeza se essa é a melhor explicação para iniciantes. Eu recomendaria: javascript.info/logical-operators
csguy
198

Isso é feito para atribuir um valor padrão , neste caso o valor de y, se a xvariável for falsa .

Os operadores booleanos em JavaScript podem retornar um operando, e nem sempre um resultado booleano, como em outros idiomas.

O operador OR lógico ( ||) retorna o valor do seu segundo operando, se o primeiro for falso, caso contrário, o valor do primeiro operando será retornado.

Por exemplo:

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

Falsas valores são aqueles que coerce para falsequando usado em contexto booleano, e eles são 0, null, undefined, uma cadeia vazia, NaNe, é claro false.

CMS
fonte
11
+1 Existe outro operador assim? Ou é ||exclusivo.
OscarRyz
9
@Support (@Oscar): O &&operador lógico tem um comportamento semelhante; ele retorna o valor do primeiro operando se for falso e retorna o valor do segundo operando, apenas se o primeiro for verdadeiro , por exemplo, ("foo" && "bar") == "bar"e(0 && "bar") == 0
CMS
12
Falsy é de fato o termo técnico.
ChaosPandion
8
Então, aprendemos sobre ||, &&, "Falsy" e "Truly" neste post. Melhor resposta com presentes "ocultos".
Alex
4
@Alex NB: "Truthy" ( "Truly"!)
Bumpy
183

Javacript usa avaliação de curto-circuito para operadores lógicos ||e &&. No entanto, é diferente de outros idiomas, pois retorna o resultado do último valor que interrompeu a execução, em vez de a true, ou falsevalue.

Os seguintes valores são considerados falsos em JavaScript.

  • falso
  • nulo
  • "" (cadeia vazia)
  • 0 0
  • Nan
  • Indefinido

Ignorando as regras de precedência do operador e simplificando, os exemplos a seguir mostram qual valor interrompeu a avaliação e é retornado como resultado.

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

Os primeiros 5 valores até NaNsão falsos, portanto, todos são avaliados da esquerda para a direita, até atingir o primeiro valor de "Hello"verdade - o que torna toda a expressão verdadeira, para que qualquer coisa acima não seja avaliada e "Hello"retorne como resultado da expressão . Da mesma forma, neste caso:

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

Os 5 primeiros valores são todos verdadeiros e são avaliados até atingir o primeiro valor falso ( null) que torna a expressão falsa, portanto, 2010não é mais avaliada e nullé retornada como resultado da expressão.

O exemplo que você deu está usando essa propriedade do JavaScript para executar uma atribuição. Pode ser usado em qualquer lugar em que você precise obter o primeiro valor de verdade ou falsidade entre um conjunto de valores. Este código abaixo irá atribuir o valor "Hello"para b, uma vez que torna mais fácil para atribuir um valor padrão, em vez de fazer verificações de if-else.

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

Você poderia chamar o exemplo abaixo de exploração desse recurso e acredito que torna o código mais difícil de ler.

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

Dentro do alerta, verificamos se messagesé falso e, se sim, avaliamos e retornamos noNewMessagesText, caso contrário, avaliamos e retornamos newMessagesText. Como é falso neste exemplo, paramos em noNewMessagesText e alertamos "Sorry, you have no new messages.".

Anurag
fonte
36
Esta é a melhor resposta na minha opinião por causa da seguinte explicação: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
11
@mastazi Sim, deve ir em negrito fonte IMHO.
precisa saber é
6
Deve ser a resposta, mostra os valores escolhidos sobre os casos de teste.
Dadan
Concordo, esta é a minha resposta favorita, pois aborda especificamente as preocupações de atribuição de variáveis ​​JavaScript. Além disso, se você optar por usar um ternário como uma das variáveis ​​subseqüentes para testar a atribuição (após o operador), deverá colocar o ternário entre parênteses para que a avaliação da atribuição funcione corretamente.
Joey T
esta deve ser a resposta aceita
Alan
49

As variáveis ​​Javascript não são digitadas; portanto, pode ser atribuído um valor inteiro a f, mesmo que ele tenha sido atribuído por meio de operadores booleanos.

f é atribuído ao valor mais próximo que não é equivalente a false . Portanto, 0, falso, nulo, indefinido, são todos ignorados:

alert(null || undefined || false || '' || 0 || 4 || 'bar'); // alerts '4'
Alsciende
fonte
13
Não se esqueça ''também igual false neste caso.
quer
Voto positivo por apontar o que f is assigned the NEAREST valueé um ponto muito importante aqui.
steviejay
3
"Mais próximo" não é bem verdade, embora tenha essa aparência. O ||operador booleano , sendo um operador booleano, tem dois operandos: um lado esquerdo e um lado direito. Se o lado esquerdo da tela|| for verdade , a operação será resolvida para o lado esquerdo e o lado direito será ignorado. Se o lado esquerdo for falso , ele será resolvido para o lado direito. Então, null || undefined || 4 || 0na verdade resolve para undefined || 4 || 0qual resolve para 4 || 0qual resolve 4.
devios1
29

Não há mágica nisso. Expressões booleanas como a || b || c || dsão avaliadas preguiçosamente. O intérprete procura o valor de a, é indefinido, então é falso, então segue em frente, depois vê o bque é nulo, o que ainda dá resultado falso, então segue em frente, depois vê c- a mesma história. Finalmente, ele vê de diz 'huh, não é nulo, então eu tenho o meu resultado' e o atribui à variável final.

Esse truque funcionará em todas as linguagens dinâmicas que fazem uma avaliação lenta de curto-circuito de expressões booleanas. Em linguagens estáticas, ele não é compilado (erro de tipo). Nas linguagens que estão ansiosas para avaliar expressões booleanas, ele retornará valor lógico (ou seja, verdadeiro neste caso).

Marcin
fonte
6
Na linguagem bastante estática C # pode-se usar o ?? operador la: object f = a ?? b ?? c ?? d ?? e;
precisa saber é o seguinte
2
herzmeister - obrigado! Eu não sabia disso ?? operador pode ser encadeado em C # e usadas em técnicas de avaliação lenta
Marek
3
Como mencionado em outro lugar, esse último dserá atribuído, independentemente de ter sido nulo / indefinido ou não.
precisa saber é o seguinte
Uma pequena correção: o ||operador sempre resolve todo o operando do lado direito quando o lado esquerdo é falso. Sendo um operador booleano, ele vê apenas duas entradas: o lado esquerdo e o lado direito. O analisador não os vê como uma série de termos, portanto, na verdade, não para quando encontra o primeiro valor de verdade, a menos que esse valor também seja o operando esquerdo de outro ||.
devios1
8

Esta pergunta já recebeu várias boas respostas.

Em resumo, essa técnica está aproveitando um recurso de como o idioma é compilado. Ou seja, o JavaScript "provoca um curto-circuito" na avaliação dos operadores booleanos e retornará o valor associado ao primeiro valor da variável não falsa ou ao que a última variável contiver. Veja a explicação de Anurag sobre esses valores que serão avaliados como falsos.

O uso dessa técnica não é uma boa prática por vários motivos; Contudo.

  1. Legibilidade do código: está usando operadores booleanos e, se o comportamento de como isso é compilado não for compreendido, o resultado esperado será um valor booleano.
  2. Estabilidade: está usando um recurso de como o idioma é compilado que é inconsistente em vários idiomas e, devido a isso, é algo que pode ser direcionado para mudanças no futuro.
  3. Recursos documentados: Existe uma alternativa existente que atende a essa necessidade e é consistente em mais idiomas. Este seria o operador ternário:

    ()? valor 1: valor 2.

O uso do operador ternário exige um pouco mais de digitação, mas distingue claramente entre a expressão booleana que está sendo avaliada e o valor que está sendo atribuído. Além disso, ele pode ser encadeado, para que os tipos de atribuições padrão executadas acima possam ser recriados.

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
fonte
potentially be targeted for change in the future.sim, mas eu não se aplica a javascript.
Dadan
Vim aqui e vi todas as respostas acima e estava pensando comigo mesmo que algo parecia errado na tarefa. Acabei de ler o Código Limpo de Robert C Martin e esse tipo de tarefa definitivamente viola a regra "Não tenho efeitos colaterais" ... enquanto o próprio autor afirma que seu livro é apenas uma das muitas técnicas para gerar um bom código, eu Ainda estava surpreso que ninguém mais se opusesse a esse tipo de tarefa. +1
Albert Rothman
Obrigado pela resposta. Acho que mais pessoas precisam considerar efeitos colaterais ao escrever código, mas até que alguém gaste muito tempo mantendo o código de outras pessoas. Eles muitas vezes não consideram isso.
precisa saber é o seguinte
11
Você realmente acha que a monstruosidade é mais clara do que a || b || c || d || e?
devios1
11
@AlbertRothman Não vejo efeitos colaterais. Nada está sendo modificado. É simplesmente uma abreviação para coalescência nula, que é um recurso bastante comum em muitos idiomas.
devios1
7

Retorne o primeiro valor verdadeiro da saída .

Se todos forem falsos, retorne o último valor falso.

Exemplo:-

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

Ele está configurando a nova variável ( z) para o valor de xse for "verdade" (diferente de zero, um objeto / matriz / função válido / qualquer que seja) ou de youtra forma. É uma maneira relativamente comum de fornecer um valor padrão, caso xnão exista.

Por exemplo, se você tiver uma função que aceita um parâmetro de retorno de chamada opcional, poderá fornecer um retorno de chamada padrão que não faça nada:

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

Isso significa que se xfor definido, o valor para zserá x, caso contrário, se yfor definido, seu valor será definido como o zvalor de.

é o mesmo que

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

É possível porque os operadores lógicos no JavaScript não retornam valores booleanos, mas o valor do último elemento necessário para concluir a operação (em uma sentença OR seria o primeiro valor não falso, em uma sentença AND seria o último ) Se a operação falhar, falseserá retornado.

Andris
fonte
5
isto está errado! se (x) {z = x; } else {z = y;} se o primeiro valor for falso, o segundo valor será sempre atribuído, não dependendo de qual seja realmente o valor.
evilpie
Só que eu acho que apenas atribui y a z se x for falso . É assim que funciona para mim no FF, é claro, que também pode ser dependente da implementação.
precisa saber é o seguinte
7
A última parte sobre retornar falso não é verdadeira (sem trocadilhos). Se o primeiro valor for falsey, o ||operador retornará o segundo valor, independentemente de ser verdade ou não.
Matthew Crumley
-1. Seu snippet de código equivalente é preciso, mas o ponto importante é que ele zseja definido como o valor de xse esse valor for verdadeiro . Caso contrário, ele será definido como o valor de y. Isso significa que, se xfor definido como, por exemplo, 0ou a sequência vazia "", isso não fará o que você diz, pois esses valores são falsos .
Daniel Cassidy
1

É o chamado operador de curto-circuito.

A avaliação de curto-circuito diz que o segundo argumento é executado ou avaliado apenas se o primeiro argumento não for suficiente para determinar o valor da expressão. quando o primeiro argumento da função OR (||) é avaliado como verdadeiro, o valor geral deve ser verdadeiro.

Também pode ser usado para definir um valor padrão para o argumento da função.`

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
fonte
0

Ele avaliará X e, se X não for nulo, a cadeia vazia ou 0 (falso lógico), será atribuído a z. Se X for nulo, a cadeia vazia ou 0 (falso lógico), ele atribuirá y a z.

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

Produzirá 'bob';

tvanfosson
fonte
Você deve esclarecer o que você quer dizer com 'vazio'. Seqüências de caracteres vazias são coerentes com false, mas matrizes ou objetos vazios são coerentes com true.
Daniel Cassidy
@Daniel "nulo, vazio ou 0" - nulo se aplica a matrizes e objetos. Ponto tomado, no entanto.
tvanfosson 27/09/10
0

De acordo com a publicação no blog de Bill Higgins ; o idioma lógico de atribuição OU do Javascript (fevereiro de 2007), esse comportamento é verdadeiro a partir da v1.2 (pelo menos)

Ele também sugere outro uso para ele (citado): " leve normalização das diferenças entre navegadores "

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