Atribuir variável na declaração da condição if, boa prática ou não? [fechadas]

113

Há um ano, mudei de linguagens OO clássicas, como Java, para JavaScript. O código a seguir definitivamente não é recomendado (ou mesmo incorreto) em 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;
}

Basicamente, acabei de descobrir que posso atribuir uma variável a um valor em uma instrução da condição if e verificar imediatamente o valor atribuído como se fosse booleano.

Para uma aposta mais segura, normalmente separo isso em duas linhas de código, atribuo primeiro e depois verifico a variável, mas agora que descobri isso, estou apenas me perguntando se é uma boa prática ou não aos olhos dos desenvolvedores de JavaScript experientes.

Michael Mao
fonte
"The following code is definitely not recommended (or event not correct) in Java..."Está correto em JavaScript? Porque, pelo que eu posso ver, você retorna um inteiro ( return parseInt(...)) se dayNumber != -1for verdadeiro, mas um booleano se for falso.
Daniel Kvist

Respostas:

117

Eu não recomendaria. O problema é que parece um erro comum quando você tenta comparar valores, mas usa um único em =vez de ==ou ===. Por exemplo, quando você vê isto:

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

você não sabe se é isso que eles pretendiam fazer ou se pretendiam escrever isto:

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

Se você realmente quiser fazer a atribuição no local, recomendo fazer uma comparação explícita também:

if ((value = someFunction()) === <whatever truthy value you are expecting>) {
    ...
}
Matthew Crumley
fonte
1
@Matthew Crumley: isso responde minha pergunta de forma clara. Não estou verificando atribuindo, mas verificando o que quer que o valor avalie como sendo após a atribuição. Este entendimento está correto?
Michael Mao,
1
@Michael: sim, correto. Adicionar a comparação basicamente torna suas intenções mais claras.
Matthew Crumley,
4
O último exemplo não funciona, entretanto, se você estiver testando a falha / sucesso de uma função que retorna um booleano. Em outras palavras, embora if (resultArr = myNeedle.exec(myHaystack)) {...}funcione, if ((resultArr = myNeedle.exec(myHaystack)) === true) {...}não funciona porque a atribuição a resultArr é sempre verdadeira, mesmo quando o resultado da função não é. Se alguém usar esta construção .., lembre-se de declarar a variável de resultado primeiro; 'var' não é válido na instrução da condição if.
Ville
3
Você pode usar if (!!(value = someFunction())), mas como disse, o problema é que você não pode usar vardentro, ifentão você acaba criando um global ou não consegue nada, pois você precisa declarar valueem uma linha separada de qualquer maneira. Pena, gostei muito dessa construção em C ++.
riv
1
@riv, você está certo; caso a função retorne um booleano - como eu disse no meu comentário acima - então a condicional funciona conforme o esperado. Mas se a função retornar um booleano (como no meu exemplo), então toda a construção é meio sem sentido; claramente minha linha de pensamento era - a julgar pelo exemplo - que a função retornaria um array. Um teste rápido indica que a condição é avaliada trueapenas quando a função retorna true, mas em todos os outros casos (incluindo quando uma matriz, string, número ou nulo é retornada) ela avalia como false.
Ville
28

Não vejo prova de que não seja uma boa prática. Sim, pode parecer um erro, mas isso é facilmente remediado por comentários criteriosos. Tomemos por exemplo:

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

Por que essa função deve ser executada uma segunda vez com:

alert(processorIntensiveFunction());

Porque a primeira versão PARECE ruim? Não posso concordar com essa lógica.

Adrian Bartholomew
fonte
32
Não é para desenterrar um comentário antigo, mas não concordo com seus argumentos. O código legível deve se explicar sem a necessidade de um comentário - adicionar um comentário a um código confuso não é uma solução. Quanto à segunda parte, que diz que a alternativa é chamar a função novamente, não acho que alguém tenha a intenção de fazer isso. Em vez disso, você fariax = processorItensiveFunction(); if(x) { alert(x); }
maksim
9
@maksim: Gosto de código legível, mas isso não significa necessariamente que o código deva ser simplificado ou muito prolixo. Espalhar coisas em várias linhas e fazer malabarismos com valores entre variáveis ​​pode realmente levar a um código pior. O código inserido pode ter efeitos colaterais imprevistos em uma linguagem de digitação fraca / flexível como JS. A atribuição em uma instrução condicional é válida em javascript, porque você apenas pergunta "se a atribuição é válida, faça algo que possivelmente inclui o resultado da atribuição". Mas, de fato, atribuir antes da condicional também é válido, não muito detalhado e mais comumente usado.
okdewit de
1
@maksim, por que você acha que if ( ! x = anyFunction() )não é legível? Não precisa de nenhum comentário.
JDrake
Se você corrigir o OPC, trabalhar com outros desenvolvedores de vários níveis de habilidade (em outras palavras, você é um profissional), vai odiar que isso seja possível.
davidjmcclelland
2
@maksim - também sua solução seria muito inconveniente em uma if-elsesituação. Considere- if (condition) {...} else if (x = processorIntensiveFunction()) {alert(x)} Seu anterior x = processorIntensiveFunction();seria um esforço perdido se o inicial conditionfosse verdadeiro.
Adrian Bartholomew
16

Eu fiz isso muitas vezes. Para ignorar o aviso de JavaScript, adiciono dois parênteses:

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

Você deve evitá-lo, se você realmente deseja usá-lo, escreva um comentário acima dele dizendo o que está fazendo.

Ming-Tang
fonte
1
@SHiNKiROU: como posso ver os avisos de javascript? Existe um compilador Javascript? ou o intérprete vai gerar algum tipo de aviso? Estou usando o console do Firefox como na depuração de javascript o tempo todo, mas nunca vejo resultados semelhantes. Desculpe pela minha experiência limitada.
Michael Mao,
5
@Michael: JSLint ( jslint.com ) é um programa / biblioteca popular que verifica os programas JavaScript em busca de possíveis erros ou código inválido .
Matthew Crumley,
Use o Mozilla Firefox com Firebug e / ou extensão Web Developer para verificar os avisos.
Ming-Tang,
Eu apenas tentei com if ((a = [1, 2]).length > 0) { console.log(a); }where anão foi inicializado em nenhum lugar ainda e realmente funcionou (bom! Torna o uso de regex muito mais fácil). É correto que eu não preciso de nada var|const|letaqui? Por acaso você sabe onde eu poderia ler mais sobre esse truque ?
t3chb0t
4

Você também pode fazer isso em Java. E não, não é uma boa prática. :)

(E use o ===em Javascript para a igualdade de tipos. Leia o livro The Good Parts de Crockford sobre JS.)

Ben Zotto
fonte
@quixoto: Posso fazer esse truque em Java? Eu me pergunto ... Não tenho o jdk à mão atm, então não consigo obter um código de amostra em Java. Da minha memória fraca, o Java só vai gerar um erro de Runtime se o valor de retorno avaliar algo não booleano como em uma instrução condicional, certo?
Michael Mao,
1
Ah, sim, em Java é verificado o tipo para ser um tipo booleano. Mas você pode fazerif (foo = getSomeBoolValue()) { }
Ben Zotto,
sim, está certo. uma variável booleana para testar se algo deu certo e outra variável para armazenar o valor retornado. É assim que o Java faz seu trabalho, estou muito familiarizado com isso, então me sinto estranho em ver que o Javascript pode fazer duas coisas em uma mera linha :)
Michael Mao
@BenZotto não é uma boa prática por quê? "Para evitar o uso indevido acidental de uma variável, geralmente é uma boa ideia introduzir a variável no menor escopo possível. Em particular, é geralmente melhor atrasar a definição de uma variável até que se possa dar a ela um valor inicial ... Uma das aplicações mais elegantes desses dois princípios é declarar uma variável em uma condicional. " - Stroustrup, "A linguagem de programação C ++."
JDrake
1
Olá, vim aqui como usuário de javascript node.js. Por que não é uma boa prática em um caso que tive dor com: if (myvar = 'apenas um teste') Cria uma variável GLOBAL node.js myvar ( nodejs.org/docs/latest-v12.x/api/globals.html #globals_global ). Portanto, se você for como eu e usar essa variável no tratamento de solicitações do servidor (voltando a ela depois de alguns segundos, quando outras solicitações podem ter aparecido e outras coisas), você pode se surpreender com os resultados que obtém. Portanto, a recomendação é: Esteja ciente de que esse padrão cria uma variável global em node.js.
pein-consulting.de
4

Há um caso em que você faz isso, com while-loops.
Ao ler arquivos, você geralmente faz assim:

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
        }
    }
}

Observe o while-loop na linha 9. Lá, uma nova linha é lida e armazenada em uma variável e, em seguida, o conteúdo do loop é executado. Eu sei que isso não é umif afirmação, mas acho que um loop while também pode ser incluído em sua pergunta.

A razão para isso é que, ao usar um FileInputStream, toda vez que você chama FileInputStream.readLine(), ele lê a próxima linha no arquivo, então se você o tivesse chamado do loop apenas fileIn.readLine() != nullsem atribuir a variável, em vez de chamar(currentLine = fileIn.readLine()) != null , e depois chamado de dentro do loop também, você obteria apenas cada segunda linha.

Espero que você entenda e boa sorte!

Daniel Kvist
fonte
3

Você também pode fazer atribuições em instruções if em Java. Um bom exemplo seria ler e escrever algo:

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

O 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
fonte
@Nitrodist: obrigado por este exemplo. Eu realmente não sou profissional em Java ou javascript ... É bom saber que essa abordagem também é viável em Java :)
Michael Mao
Eu não vejo o sentido disso. Você pode fazer isso em Java, PHP e muitas outras linguagens. A pergunta era sobre Javascript.
pmrotule
Não, não é necessariamente, você precisa reler a pergunta com atenção.
Nitrodista
3

Se você fosse consultar o livro de Martin Fowlers Refactoring, melhorando o design do código existente ! Então, há vários casos em que seria uma boa prática, por exemplo. condicionais complexos longos para usar uma chamada de função ou método para afirmar seu caso:

"Motivação

Uma das áreas mais comuns de complexidade em um programa reside na lógica condicional complexa. Conforme você escreve código para testar condições e fazer várias coisas dependendo de várias condições, você rapidamente acaba com um método muito longo. O comprimento de um método é em si um fator que o torna mais difícil de ler, mas as condições aumentam a dificuldade. O problema geralmente reside no fato de que o código, tanto nas verificações de condição quanto nas ações, diz a você o que acontece, mas pode facilmente obscurecer por que isso acontece.

Como acontece com qualquer bloco grande de código, você pode tornar sua intenção mais clara decompondo-a e substituindo pedaços de código por uma chamada de método com o nome da intenção desse bloco de código. > Com as condições, você pode receber mais benefícios ao fazer isso para a parte condicional e cada uma das alternativas. Desta forma, você destaca a condição e torna claro o que você está ramificando. Você também destaca o motivo da ramificação. "

E sim, sua resposta também é válida para implementações Java. Ele não atribui a função condicional a uma variável nos exemplos.

Allan
fonte
1

Não é uma boa prática. Você logo ficará confuso sobre isso. Parece semelhante a um erro comum: uso indevido dos operadores "=" e "==".

Você deve dividi-lo em 2 linhas de códigos. Isso não apenas ajuda a tornar o código mais claro, mas também facilita a refatoração no futuro. Imagine que você muda a condição IF? Você pode acidentalmente remover a linha e sua variável não obter mais o valor atribuído a ela.

thethanghn
fonte
@thethanghn: é exatamente disso que tenho medo. quando eu ficar mais velho e preguiçoso, não quero digitar mais no código se menos teclas forem suficientes :)
Michael Mao
1
Não, eu não fico confuso e faço isso o tempo todo. Existem benefícios nisso.
JDrake
Mas realmente depende, não é? Se você vem de um background 'C' (e outras linguagens baseadas em C), então a construção é muito familiar e as alternativas são muito estranhas. IMO, é algo que se aprende uma vez e então você sabe. Não é algo em que você tropeçará mais de uma vez.
Max Waterman
0

Eu consideraria isso mais um estilo C da velha escola; não é uma prática muito boa em JavaScript, portanto, você deve evitá-la.

Justin Ethier
fonte
8
Também não considero uma boa prática em C.
Matthew Crumley,
1
Eu considero uma boa prática em muitos idiomas.
JDrake
Apenas dizer 'não é uma boa prática' não é suficiente, imo. É realmente apenas sobre educação - é aprendido uma vez, e é isso.
Max Waterman
0

você poderia fazer algo assim:

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

Eu vim de Golang, onde é comum ver algo como

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

Em que errtem como escopo ifapenas aquele bloco. Como tal, aqui está o que estou fazendo no es6, o que parece muito feio, mas não faz com que minhas regras bastante rígidas do eslint passem despercebidas e alcança o mesmo.

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

As chaves extras definem um novo, uh, "escopo léxico"? O que significa que posso usar conste errnão está disponível para o bloco externo.

Adam Barnes
fonte