¿Es un JS Boolean tener propiedades personalizadas una mala práctica?

41

En JS puede devolver un booleano que tenga propiedades personalizadas. P.ej. cuando Modernizr prueba el soporte de video, regresa trueo falseel Boolean devuelto (Bool es un objeto de primera clase en JS) tiene propiedades que especifican qué formatos son compatibles. Al principio me sorprendió un poco, pero luego me empezó a gustar la idea y me pregunté por qué parece ser utilizada con moderación.

Parece una forma elegante de lidiar con todos esos escenarios en los que básicamente desea saber si algo es verdadero o falso, pero puede estar interesado en alguna información adicional que pueda definir sin definir un objeto de devolución personalizado o usar una función de devolución de llamada preparada para aceptar más parámetros De esta manera, conserva una firma de función muy universal sin comprometer la capacidad para devolver datos más complejos.

Hay 3 argumentos en contra que puedo imaginar:

  1. Es un poco raro / inesperado cuando probablemente sea mejor para cualquier interfaz ser clara y no complicada.
  2. Esto puede ser un argumento de hombre de paja, pero al ser un caso marginal, me imagino que fracasa en algún optimizador JS, uglifier, VM o después de un pequeño cambio en la especificación del lenguaje de limpieza, etc.
  3. Hay una forma mejor, concisa, clara y común, de hacer exactamente lo mismo.

Entonces, mi pregunta es ¿hay alguna razón fuerte para evitar el uso de booleanos con propiedades adicionales? ¿Son un truco o un regalo?


La trama tuerce la advertencia.

Arriba está la pregunta original en toda su gloria. Como Matthew Crumley y senevoldsen señalaron, se basa en una premisa falsa (¿falsa?). En la tradición de JS, lo que Modernizr hace es un truco de lenguaje y uno sucio. Se reduce a JS que tiene un bool primitivo que, si se establece en falso, seguirá siendo falso incluso después de INTENTAR agregar accesorios (que falla en silencio) y un objeto booleano que puede tener accesorios personalizados pero ser un objeto siempre es verdadero. Modernizr devuelve un objeto booleano falso o un objeto booleano verdadero.

Mi pregunta original asumía que el truco funciona de manera diferente y, por lo tanto, las respuestas más populares tratan con el aspecto de los estándares de codificación (perfectamente válidos). Sin embargo, encuentro que las respuestas que desacreditan todo el truco son más útiles (y también los argumentos finales contra el uso del método), así que estoy aceptando una de ellas. ¡Gracias a todos los participantes!

Konrad
fuente
35
Extender el tipo booleano es un wtf clásico .
hlovdal
77
Tenga en cuenta que en JS, podrían haber regresado nullsi no son compatibles y una matriz de formatos si es así. Una lista se considera verdadera en JS, y nulles falsa.
jpmc26
3
Antes de responder esta pregunta: en javascript hay una gran diferencia entre "booleano" y "booleano". No son lo mismo y cualquier respuesta que no capitalice el booleano no es válida.
Pieter B
2
Para aquellos de ustedes que se sorprenden al ver algo como esto en la naturaleza, en una biblioteca tan popular como Modernizr, aquí está el código relevante de su GitHub: github.com/Modernizr/Modernizr/blob/…
Chris Nielsen

Respuestas:

38

Además de los principios generales de diseño, como única responsabilidad, y la menor sorpresa, hay una razón para JavaScript específico que no es una buena idea: hay una enorme diferencia entre una booleany Booleanen JavaScript que le impide trabajar en el caso general.

booleanes un tipo primitivo, no un objeto, y no puede tener propiedades personalizadas. Expresiones como true.toString()trabajo porque detrás de las escenas, se convierte en (new Boolean(true)).toString().

Boolean(con una B mayúscula) es un objeto, pero tiene muy pocos buenos usos, y ser utilizado como booleandefinitivamente no es uno de ellos. La razón de esto es que todo Booleanes "verdadero", independientemente de su valor , porque todos los objetos se convierten trueen un contexto booleano. Por ejemplo, intente esto:

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

Por lo tanto, en general, no hay forma de agregar propiedades a un booleano en JavaScript que aún permita que se comporte de manera lógica. Modernizr puede salirse con la suya porque solo agrega propiedades a los valores "verdaderos", que funcionan de la manera que usted esperaría (es decir, funcionan en declaraciones if). Si el video no es compatible, Modernizr.videoserá real boolean(con el valor false) y no se le pueden agregar propiedades.

Matthew Crumley
fuente
18
Encuentro el enfoque de Modernizr bastante extraño, teniendo en cuenta que tener solo un objeto con propiedades ya se evalúa trueen condicional. ¿Por qué usar explícitamente a Boolean?
Arturo Torres Sánchez
Muchas gracias por un buen argumento técnico. Parece que mi prueba rápida fue defectuosa: jsbin.com/hurebi/3/edit?js,console . Incluso después de agregar accesorios personalizados false, es estrictamente 'falso' y falso ( ===y ==funciona como) PERO, de hecho, no puede agregar accesorios al bool primitivo. La distinción entre Bool y bool aparentemente me abandonó.
konrad
@ ArturoTorresSánchez porque la forma en que lo hacen los valores de retorno son nominalmente del mismo tipo.
Jared Smith
1
@ ArturoTorresSánchez por eso agregué el calificador "nominalmente": P
Jared Smith
1
@konrad Como referencia, JavaScript de forma predeterminada fingirá que le permite agregar propiedades a las primitivas al no quejarse cuando intente hacerlo. El uso del modo estricto lo solucionará y arrojará un TypeErrorsi intenta agregar una propiedad (consulte jsbin.com/yovasafibo/edit?js,console ).
Matthew Crumley el
59

Felicidades, has descubierto objetos. La razón para no hacer esto se llama principio de mínimo asombro . Ser sorprendido por un diseño no es algo bueno.

No hay nada de malo en agrupar esta información, pero ¿por qué querrías ocultarla en un Bool? Ponlo en algo que esperarías tener toda esta información. Bool incluido.

naranja confitada
fuente
1
LOL sí, mencioné el objeto como una alternativa obvia en mi pregunta. El principio de menor asombro es un buen punto, aunque con JS los objetos de escritura débiles son menos sorprendentes pero confusos y de todos modos debe verificar los documentos. En cuanto al uso, Modernizr es un buen ejemplo: tiene un gran conjunto de pruebas con un resultado uniforme verdadero / falso y solo de vez en cuando necesita pasar más información, tal vez incluso la necesidad de eso llegó más tarde, así que cuando lo haga, puede realice cambios importantes en toda la biblioteca creando envoltorios en su mayoría redundantes alrededor de booleanos o mejore el bool. OMI no es tan tonto.
konrad
1
Un poco más sobre el uso. Si se apega a devolver un valor booleano, puede pasar todas las funciones para enumerar operaciones como any (), all (), filter () fácilmente. Evita la falta de tipeo fuerte o interfaces formales en JS a costa de ser un poco poco convencional, lo que hasta ahora parece ser el argumento principal en su contra.
konrad
1
"No convencional" no me parece un argumento fuerte.
Robert Harvey
Edité la pregunta. He aquí el principio del menor asombro. :-)
konrad
16

El argumento principal que sostengo en su contra es, el principio de responsabilidad única , un booleano solo debería decir si algo es trueo falseno, por qué o cómo o cualquier otra cosa. Creo y practico firmemente que se deben usar otros objetos para comunicar esa o cualquier otra información.

J. Pichardo
fuente
¿te refieres a booleano o booleano (con B mayúscula) en javascript? hay una diferencia.
Pieter B
Pero si tiene un objeto que define esto y algunas otras cosas, ¿no podría violar los mismos principios?
Casey
1
@Casey No ya que el propósito de ese objeto sería diferente del booleano original. Y tendría una sola responsabilidad, como ejemplo, comunicar el estado y el motivo de una transacción.
J. Pichardo
1
@Casey el problema no es que contener esta información en sí mismo es una violación de SRP. Solo se convierte en un problema cuando se combina con la responsabilidad que ya tiene un booleano: representar verdadero o falso. A Booleanpesar de las travesuras de JavaScript .
Jacob Raihle
10

Dado que la razón por la que se llama valor booleano es el hecho de que es verdadero o falso, no me gusta la idea, ya que prácticamente socava todo su propósito de wikipedia

En informática, el tipo de datos booleanos es un tipo de datos, que tiene dos valores (generalmente denotados verdadero y falso), destinados a representar los valores de verdad de la lógica y el álgebra booleana .

(mi negrita)

Nombre para mostrar
fuente
1
correcta, pero la necesidad puede dictar lo contrario
svarog
8
@svarog, la única necesidad que puedo ver es el diseñador no calificado del código. Si necesita devolver bool y algo más, conviértalo en una clase, tupla, lista o cualquier otra cosa que encaje en el código particular.
MatthewRock
si vas a regresar sí
svarog
Creo que la pregunta es sobre el objeto booleano, no la primitiva booleana. El objeto no es un valor.
Pieter B
1
Si puede tomar un valor que no sea verdadero o falso, tampoco es un valor booleano. Lo cual, dado el nombre, es una tontería.
Inútil el
2

El hecho de que pueda no significa que deba hacerlo, en JS puede adjuntar propiedades a cualquier objeto (booleanos incluidos)

¿Cómo es eso algo malo?

Por un lado, puede adjuntar más datos irrelevantes a un booleano (según su ejemplo), algo que otro desarrollador no esperará porque ¿por qué debería estar allí? los bools son solo para verdadero y falso.

Un contrapunto para esto es adjuntar algunas propiedades útiles relevantes que pueden ayudarlo a manejar el valor booleano ( Java lo hace ).

Por ejemplo, puede adjuntar una función que convierta el valor booleano en una cadena, un dirtyindicador que se convirtió en verdadero si se cambió el valor, observadores y devoluciones de llamadas de eventos que pueden activarse cuando se cambia el valor, etc.

pero el Booleano devuelto (Bool es un objeto de primera clase en JS) tiene propiedades que especifican qué formatos son compatibles

Suena como algo que no debería almacenarse en un booleano, sino más bien usando un conjunto de booleanos, o un conjunto de propiedades con booleanos dentro de ellos. Creo que un mejor enfoque sería devolver un objeto con todos los formatos y detalles de soporte.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}
svarog
fuente
1
¿te refieres a booleano o booleano (con B mayúscula)?
Pieter B
1

No puede crear booleanos con propiedades personalizadas en JavaScript. El siguiente error (al menos en FF):

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

Puede usar o heredar de Boolean, pero como dice Matthew Crumley, da resultados diferentes. Booleanes de tipo Object . Cuando JS necesita el valor booleano de una expresión, convierte utilizando la función de especificación ToBoolean, que exige que para Objects el resultado sea siempre true. Por lo tanto, el valor se new Boolean(false)evalúa como true! Puede verificar esto en este ejemplo: https://jsfiddle.net/md7abx5z/3/ .

La única razón por la que funciona para Modernizr es incidental. Solo crean un Booleanobjeto cuando la condición es verdadera. Cuando evalúan falso, simplemente regresan ordinario false. Por lo tanto, funciona porque solo devuelven Boolean objetos cuando el resultado fue verdadero de todos modos, y nunca cuando es falso.

senevoldsen
fuente
1

Entiendo que el contexto ha cambiado, pero me gustaría responder a la pregunta original que leí usando JS como ejemplo, pero no limitado a solo JS.

Agregar propiedades a un valor booleano no sería un problema SI las propiedades tuvieran algo que ver con lo verdadero / falso, no con la variable que contiene el valor. Por ejemplo, agregar un método toYesNoString estaría bien, agregar un numberOfChildren a un valor hasChildren no lo está, y tampoco lo haría preguntasMissed studentPassed. No hay mucho que pueda agregar a un booleano, aparte de varias repeticiones de cadenas, la única propiedad que puedo pensar que tendría sentido es originalExpression. Pero agregarlo no es necesariamente una mala idea en teoría.

jmoreno
fuente
0

Si miro el código, no se trata realmente de dar propiedades personalizadas booleanas, sino de un método que tiene métodos de retorno con diferentes firmas.

Si es falso, obtiene un falso primitivo y si es verdadero, devuelve un objeto que, por definición, se interpreta como verdadero en javascript.

Entonces, en mi opinión, su pregunta no debería ser si es una buena idea dar propiedades personalizadas booleanas, sino si es una buena idea tener métodos con múltiples firmas de retorno.

Pieter B
fuente
0

En el ejemplo dado, ¿no podría simplemente devolver una matriz de todos los formatos de video compatibles?

  • Una matriz vacía significa "no se admiten formatos de video ", que en orden significa "no se admiten videos ".
  • De lo contrario, si se admite cualquier formato de video , entonces obviamente se admite el video en general.

Al menos diría que usar array es algo menos sorprendente que tener "booleanos personalizados".

En Javascript, una matriz vacía incluso se considera falsa , mientras que una matriz no vacía es verdadera , por lo que con un poco de suerte podría cambiar a matrices y todo estaría funcionando como antes de editar No, tonto por pensar que podría recordar la verdad de objetos en JavaScript: P

daniero
fuente
Una matriz vacía no se evalúa como falsa en una declaración if. if ([]) console.log ('no es falso'); imprime 'no falso' en Chrome, por ejemplo. typeof [] === 'objeto', por lo que no una matriz vacía no es falsa. Ver developer.mozilla.org/en-US/docs/Glossary/Truthy para referencia.
joshp
@joshp oops, tienes razón, mi mal. Corregido
daniero
Aunque, no estoy seguro de que el tipo de cosa pruebe algo; ""tiene typeof "string"pero en realidad es falso
daniero
1
Por otro lado, [].lengthes falsey si la longitud es 0, no es mucho más mecanografía, y en mi opinión es una mejor indicación de la intención de lo if([])que sería incluso si eso funcionara.
IllusiveBrian
@daniero El punto sobre typeof [] === 'objeto' es que cualquier Objeto es verdadero, incluso nuevo Booleano (falso). Algunos tipos primitivos (por ejemplo, la cadena "" y el número 0) son falsos, pero no son objetos en lo que respecta al tipo de. Es solo otra forma de recordar. No se trata de probar nada. La prueba está en la especificación o en las pruebas, lo que prefiera.
joshp