Um JS Booleano com propriedades personalizadas é uma prática ruim?

41

Em JS, você pode retornar um booleano com propriedades customizadas. Por exemplo. quando o Modernizr testa o suporte a vídeo, ele retorna trueou falseo Booleano retornado (Bool é um objeto de primeira classe em JS) possui propriedades que especificam quais formatos são suportados. No começo, me surpreendeu um pouco, mas depois comecei a gostar da ideia e comecei a me perguntar por que ela parece ser usada com moderação?

Parece uma maneira elegante de lidar com todos os cenários em que você basicamente quer saber se algo é verdadeiro ou falso, mas pode estar interessado em algumas informações adicionais que você pode definir sem definir um objeto de retorno personalizado ou usar uma função de retorno de chamada preparada para aceite mais parâmetros. Dessa forma, você mantém uma assinatura de função muito universal sem comprometer a capacidade de retornar dados mais complexos.

Existem 3 argumentos contra isso que posso imaginar:

  1. É um pouco incomum / inesperado quando provavelmente é melhor para qualquer interface ser clara e não complicada.
  2. Este pode ser um argumento simples, mas, com um pouco de dificuldade, posso imaginar que ele sai pela culatra em algum otimizador de JS, uglifier, VM ou após uma pequena alteração na especificação da linguagem de limpeza etc.
  3. Existe uma maneira melhor - concisa, clara e comum - de fazer exatamente o mesmo.

Então, minha pergunta é: existem razões fortes para evitar o uso de booleanos com propriedades adicionais? Eles são um doce ou travessura?


A plotagem distorce o aviso.

Acima está a pergunta original em plena glória. Como Matthew Crumley e senevoldsen apontaram, é baseado em uma premissa falsa (falsidade?). Na tradição JS, o que o Modernizr faz é um truque de linguagem e um truque sujo. Tudo se resume a JS tendo um bool primitivo que, se definido como false, permanecerá falso, mesmo depois de TENTAR adicionar adereços (que falham silenciosamente) e um objeto booleano que pode ter adereços personalizados, mas ser um objeto é sempre verdadeiro. Modernizr retorna um objeto booleano booleano falso ou verdadeiro.

Minha pergunta original assumiu que o truque funciona de maneira diferente e, portanto, as respostas mais populares lidam com o aspecto (perfeitamente válido) dos padrões de codificação. No entanto, acho as respostas que desmembram todo o truque mais útil (e também os argumentos finais contra o uso do método), por isso estou aceitando uma delas. Obrigado a todos os participantes!

Konrad
fonte
35
Estender o tipo booleano é um wtf clássico .
precisa saber é o seguinte
7
Observe que, em JS, eles poderiam ter retornado nullse não forem suportados e uma matriz de formatos, se houver. Uma lista é considerada verdadeira em JS e nullé falsa.
Jpmc26
3
Antes de responder a esta pergunta: em javascript, há uma grande diferença entre "boolean" e "boolean". Eles não são a mesma coisa e qualquer resposta que não capitalize Boolean é inválida.
Pieter B
2
Para aqueles que ficam surpresos ao ver algo assim na natureza, em uma biblioteca tão popular como Modernizr, aqui está o código relevante do GitHub: github.com/Modernizr/Modernizr/blob/…
Chris Nielsen

Respostas:

38

Além dos princípios gerais de design, como a responsabilidade única e menor surpresa, há uma razão específica de JavaScript que não é uma boa idéia: há uma enorme diferença entre um booleane Booleanem JavaScript que o impede de trabalhar no caso geral.

booleané um tipo primitivo, não um objeto e não pode ter propriedades personalizadas. Expressões como true.toString()trabalho, porque nos bastidores, ele se transforma (new Boolean(true)).toString().

Boolean(com letra maiúscula B) é um objeto, mas tem muito poucos usos bons, e ser usado como um booleandefinitivamente não é um deles. A razão para isso é que tudo Booleané "verdadeiro", independentemente do seu valor , porque todos os objetos são convertidos trueem um contexto booleano. Por exemplo, tente o seguinte:

var answer = new Boolean(false);
if (answer) {
  console.log("That was unexpected.");
}

Portanto, em geral, não há como adicionar propriedades a um booleano em JavaScript que ainda permita que ele se comporte de maneira lógica. O Modernizr pode se safar disso, pois apenas adiciona propriedades a valores "verdadeiros", que tipo de trabalho como você esperaria (ou seja, eles funcionam nas instruções if). Se o vídeo não for suportado, Modernizr.videoserá um valor real boolean(com o valor false) e não poderá ter propriedades adicionadas a ele.

Matthew Crumley
fonte
18
Acho a abordagem de Modernizr bastante estranha, considerando que ter apenas um objeto com propriedades já é avaliado como truecondicional. Por que usar explicitamente um Boolean?
Arturo Torres Sánchez
Muito obrigado por um sólido argumento técnico. Parece que meu teste rápido falhou: jsbin.com/hurebi/3/edit?js,console . Mesmo depois de adicionar adereços personalizados a falseele, é estritamente 'falso' e falso ( ===e ==funciona como), mas, na verdade, você não pode adicionar adereços ao bool primitivo. A distinção entre Bool e bool fugiu de mim, aparentemente.
Konrad
@ ArturoTorresSánchez porque, da maneira como fazem, os valores de retorno são nominalmente do mesmo tipo.
Jared Smith
1
@ ArturoTorresSánchez é por isso que adicionei o qualificador "nominalmente": P
Jared Smith
1
@konrad Para referência, por padrão, o JavaScript fingirá que está permitindo adicionar propriedades às primitivas, não reclamando quando você tenta fazê-lo. O uso do modo estrito corrigirá isso e lançará um TypeErrorse você tentar adicionar uma propriedade (consulte jsbin.com/yovasafibo/edit?js,console ).
Matthew Crumley
59

Parabéns, você descobriu objetos. A razão para não fazer isso é chamada de princípio de menor espanto . Ser surpreendido por um design não é uma coisa boa.

Não há nada errado em agrupar essas informações, mas por que você deseja ocultá-las em um Bool? Coloque-o em algo que você esperaria ter todas essas informações. Bool incluído.

candied_orange
fonte
1
LOL sim, eu mencionei o objeto como uma alternativa óbvia na minha pergunta. O princípio de menor espanto é um bom argumento, embora, com o JS, os objetos de digitação fracos sejam menos surpreendentes, mas ainda sejam confusos e você precise verificar os documentos de qualquer maneira. Quanto ao uso do Modernizr, é um bom exemplo: você tem um grande conjunto de testes com resultados uniformes verdadeiro / falso e, de vez em quando, precisa passar mais informações - talvez até a necessidade desses veio mais tarde; faça alterações recentes em toda a biblioteca criando invólucros principalmente redundantes em torno dos booleanos ou aprimore o booleano. OMI não é tão burro.
Konrad
1
Um pouco mais sobre o uso. Se você continuar retornando um valor booleano, poderá passar todas as funções para listar operações como any (), all (), filter () facilmente. Isso evita a falta de digitação forte ou interfaces formais em JS, ao custo de ser um pouco convencional - o que parece até agora ser o principal argumento contra isso.
Konrad
1
"Não convencional" não parece ser um argumento forte para mim.
Robert Harvey
Eu editei a pergunta. Aqui está o princípio do mínimo espanto. :-)
konrad
16

O principal argumento que sustento é que, o princípio da responsabilidade única , um booleano só deve dizer se algo é trueou falsenão, por que ou como ou qualquer outra coisa. É minha firme convicção e prática que outros objetos sejam usados ​​para comunicar essa ou qualquer outra informação.

J. Pichardo
fonte
quer dizer booleano ou booleano (com maiúsculas B) em javascript, há uma diferença.
Pieter B
Mas se você tem um objeto que define isso e algumas outras coisas, isso não viola os mesmos princípios?
Casey
1
@ Casey Não, pois o objetivo desse objeto seria diferente do booleano original. E teria apenas uma responsabilidade: comunicar o estado e o motivo de uma transação, como exemplo.
J. Pichardo
1
@ Casey, o problema não é que conter essas informações por si só seja uma violação do SRP. Isso só se torna um problema quando combinado com a responsabilidade que um booleano já possui - representar verdadeiro ou falso. BooleanApesar das travessuras do JavaScript .
Jacob Raihle
10

Como todo o motivo pelo qual ele é chamado de valor booleano é verdadeiro ou falso, não gosto da ideia, pois você prejudica todo o seu propósito na wikipedia

Na ciência da computação, o tipo de dados booleano é um tipo de dados, com dois valores (geralmente denotados verdadeiro e falso), destinado a representar os valores verdadeiros da lógica e da álgebra booleana .

(meu negrito)

Mostrar nome
fonte
1
correta, mas a necessidade pode ditar o contrário
svarog
8
@ varvarog a única necessidade que posso ver é designer não qualificado do código. Se você precisar retornar bool e qualquer outra coisa, torne-a uma classe, tupla ou lista, ou qualquer outra coisa que se encaixe no código específico.
MatthewRock
se você estiver retornando sim
svarog
Eu acho que a pergunta é sobre o objeto booleano e não o primitivo booleano. O objeto não é um valor.
Pieter B
1
Se ele pode assumir um valor diferente de verdadeiro ou falso, também não é um booleano. Qual, dado o nome, é bobo.
inútil
2

Só porque você pode, não significa que deveria, em JS você pode praticamente anexar propriedades a qualquer objeto (incluindo booleanos)

Como pode ser uma coisa ruim?

Por um lado, você pode anexar dados mais irrelevantes a um booleano (por exemplo), algo que outro desenvolvedor não espera, porque por que deveria estar lá ?! bools são apenas para verdadeiro e falso.

Um contraponto é anexar algumas propriedades úteis relevantes que podem ajudá-lo a lidar com o valor booleano ( Java faz isso ).

Por exemplo, você pode anexar uma função que converte o valor booleano em uma string, um dirtysinalizador que se tornará verdadeiro se o valor for alterado, observadores e retornos de chamada de eventos que podem disparar quando o valor for alterado etc.

mas o Booleano retornado (Bool é um objeto de primeira classe em JS) possui propriedades que especificam quais formatos são suportados

Parece algo que não deve ser armazenado em um booleano, mas usando um conjunto de booleanos ou um conjunto de propriedades com booleanos dentro deles. Eu acho que uma abordagem melhor seria retornar um objeto com todos os formatos e detalhes de suporte.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}
svarog
fonte
1
você quer dizer booleano ou booleano (com capital B)?
Pieter B
1

Você não pode criar booleanos com propriedades personalizadas em JavaScript. A seguinte falha (pelo menos em FF):

    var x = false;
    x.foo = "bar";
    console.log(x.foo);

Você pode usar ou herdar do booleano, mas como Matthew Crumley diz, ele fornece resultados diferentes. Booleané do tipo Object . Quando o JS precisa do valor booleano de uma expressão, ele é convertido usando a função de especificação ToBoolean, que exige que, para Objects, o resultado seja sempre true. Assim, o valor é new Boolean(false)avaliado como true! Você pode verificar isso neste exemplo: https://jsfiddle.net/md7abx5z/3/ .

O único motivo pelo qual o Modernizr funciona é incidental. Eles só criam um Booleanobjeto quando a condição é verdadeira. Quando avaliam falso, apenas retornam comuns false. Portanto, funciona porque eles retornam Boolean objetos apenas quando o resultado é verdadeiro, e nunca quando é falso.

senevoldsen
fonte
1

Entendo que o contexto mudou, mas gostaria de responder à pergunta original, que li como JS como exemplo, mas não apenas a JS.

Adicionar propriedades a um booleano não seria um problema se as propriedades tivessem algo a ver com true / false, não com a variável que mantinha o valor. Por exemplo, adicionar um método toYesNoString seria bom, adicionar um numberOfChildren a um valor hasChildren não é e nem o questionMissed studentPassed. Não há muito que você possa adicionar a um booleano, além de várias representações de string, a única propriedade em que consigo pensar que faria sentido é originalExpression. Mas adicionar a isso não é necessariamente uma má ideia em teoria.

jmoreno
fonte
0

Se eu olhar o código, não se trata realmente de fornecer propriedades personalizadas booleanas, mas de um método que possui métodos de retorno com assinaturas diferentes.

Se for falso, você obtém um falso primitivo e, se for verdadeiro, retorna um objeto que, por definição, é interpretado como verdadeiro em javascript.

Portanto, na minha opinião, sua pergunta não deve ser se é uma boa ideia fornecer propriedades personalizadas booleanas, mas se é uma boa ideia ter métodos com várias assinaturas de retorno.

Pieter B
fonte
0

No exemplo dado, você não poderia simplesmente retornar uma matriz de todos os formatos de vídeo suportados?

  • Uma matriz vazia significa "nenhum formato de vídeo suportado", que em ordem significa "nenhum vídeo suportado".
  • Caso contrário, se qualquer formato de vídeo for suportado, obviamente o vídeo em geral será suportado.

Eu diria pelo menos que usar array é um pouco menos surpreendente do que ter "booleanos personalizados".

Em Javascript, um array vazio é mesmo considerado Falsas , enquanto uma matriz não-vazia é truthy , então com alguma sorte você pode simplesmente mudar para matrizes e tudo estaria trabalhando como antes editar Não, bobo me para pensar que eu poderia lembrar o truthiness de objetos em JavaScript: P

daniero
fonte
Uma matriz vazia não é avaliada como falsa em uma instrução if. if ([]) console.log ('não falso'); imprime 'não falso' no Chrome, por exemplo. typeof [] === 'objeto' para que nenhuma matriz vazia não seja falsa. Consulte developer.mozilla.org/en-US/docs/Glossary/Truthy para obter referência.
joshp
@joshp oops, você está certo, meu mal. Corrigido
daniero 9/01/17
Embora não tenha certeza de que o tipo de coisa prove alguma coisa; ""tem typeof, "string"mas na verdade é falso
daniero
1
Por outro lado, [].lengthé falsey se o comprimento é 0, não é muito mais digitado e, na minha opinião, é uma indicação melhor da intenção do que if([])seria mesmo se isso funcionasse.
precisa saber é o seguinte
@daniero O ponto sobre typeof [] === 'objeto' é que qualquer Objeto é verdadeiro, até mesmo novo Booleano (falso). Alguns tipos primitivos (por exemplo, string "" e número 0) são falsos, mas não são objetos no que diz respeito ao typeof. É apenas outra maneira de lembrar. Não se trata de provar nada. A prova está na especificação ou nos testes, o que você preferir.
joshp