Un booléen JS ayant des propriétés personnalisées est-il une mauvaise pratique?

41

Dans JS, vous pouvez renvoyer un booléen ayant des propriétés personnalisées. Par exemple. Lorsque Modernizr teste le support vidéo, il renvoie trueou falsele booléen renvoyé (Bool est le premier objet de classe dans JS) possède des propriétés spécifiant les formats pris en charge. Au début, cela m’a un peu surpris, puis j’ai commencé à aimer cette idée et à me demander pourquoi elle semble être utilisée avec parcimonie.

Cela ressemble à une manière élégante de traiter tous les scénarios dans lesquels vous voulez savoir en gros si quelque chose est vrai ou faux, mais vous pouvez être intéressé par des informations supplémentaires que vous pouvez définir sans définir d'objet de retour personnalisé ni utiliser une fonction de rappel prête à accepter plus de paramètres. Ainsi, vous conservez une signature de fonction très universelle sans compromettre la capacité de restitution de données plus complexes.

Il y a 3 arguments contre cela que je peux imaginer:

  1. C'est un peu inhabituel / inattendu quand il est probablement préférable que toute interface soit claire et non délicate.
  2. C’est peut-être un argument de paille, mais comme il s’agit d’un cas un peu tranchant, je peux imaginer qu’il se retourne discrètement dans certains optimiseurs JS, uglifier, ordinateurs virtuels ou après une modification mineure des spécifications du langage, etc.
  3. Il y a une meilleure façon - concise, claire et commune - de faire exactement la même chose.

Ma question est donc la suivante: existe-t-il de bonnes raisons d’éviter d’utiliser des booléens avec des propriétés supplémentaires? Sont-ils un tour ou un régal?


Avertissement de rebond de parcelle.

Ci-dessus se trouve la question initiale en pleine gloire. Comme Matthew Crumley et senevoldsen l'ont tous deux souligné, il est basé sur une fausse prémisse (falsie?). Conformément à la tradition JS, ce que Modernizr fait est un tour de langue malpropre. Cela revient à dire que JS a un booléen primitif qui, s'il est défini sur false, restera faux même après avoir essayé d'ajouter des accessoires (qui échoue en silence) et un objet booléen pouvant avoir des accessoires personnalisés, mais être un objet est toujours une vérité. Modernizr renvoie soit un objet booléen faux, soit un objet booléen de vérité.

Ma question initiale supposait que l'astuce fonctionnait différemment et que les réponses les plus courantes traitaient donc de l'aspect (parfaitement valide) des normes de codage. Cependant, je trouve que les réponses à cette question sont très utiles (ainsi que les arguments ultimes contre l’utilisation de la méthode), aussi j’en accepte un. Merci à tous les participants!

Konrad
la source
35
L'extension du type booléen est un wtf classique .
hlovdal
7
Notez que dans JS, ils pourraient simplement revenir nullsi ils ne sont pas pris en charge et, le cas échéant, un tableau de formats. Une liste est considérée comme une vérité dans JS et nullest une fausseté.
jpmc26
3
Avant de répondre à cette question: en javascript, il y a une grande différence entre "booléen" et "booléen". Ce ne sont pas la même chose et toute réponse qui ne capitalise pas le booléen est invalide.
Pieter B
2
Pour ceux d'entre vous qui sont abasourdis de voir quelque chose comme cela à l'état sauvage, dans une bibliothèque aussi populaire que Modernizr, voici le code pertinent de leur GitHub: github.com/Modernizr/Modernizr/blob/…
Chris Nielsen

Réponses:

38

Outre les principes de conception généraux, tels que la responsabilité unique et la moindre surprise, il existe une raison spécifique à JavaScript qui n'est pas une bonne idée: il existe une énorme différence entre un booleanet BooleanJavaScript qui l'empêche de fonctionner dans le cas général.

booleanest un type primitif, pas un objet et ne peut pas avoir de propriétés personnalisées. Expressions comme true.toString()travail parce que dans les coulisses, il se transforme en (new Boolean(true)).toString().

Boolean(avec un majuscule B) est un objet, mais a très peu d'utilisations utiles, et son utilisation en booleanest certainement pas l'un d'entre eux. La raison en est que chaque propriété Booleanest "true", quelle que soit sa valeur , car tous les objets sont convertis trueen un contexte booléen. Par exemple, essayez ceci:

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

Donc, en général, il n’ya aucun moyen d’ajouter des propriétés à un booléen en JavaScript qui le laisse quand même se comporter de manière logique. Modernizr peut s'en tirer parce que les seuls ajoutent des propriétés aux "vraies" valeurs, qui fonctionnent comme vous le souhaitez (c'est-à-dire qu'elles fonctionnent dans des instructions if). Si la vidéo n'est pas du tout prise en charge, Modernizr.videoelle sera réelle boolean(avec la valeur false) et ne pourra pas avoir de propriétés ajoutées.

Matthew Crumley
la source
18
Je trouve l'approche de Modernizr plutôt étrange, considérant que le fait de n'avoir qu'un objet avec des propriétés est déjà évalué comme trueconditionnel. Pourquoi utiliser explicitement un Boolean?
Commentaires
Merci beaucoup pour un argument technique solide. Il semble que mon test rapide ait été défectueux: jsbin.com/hurebi/3/edit?js,console . Même après l'ajout d'accessoires personnalisés à falsela fois, il est strictement 'faux' et falsifié ( ===et ==fonctionne comme) MAIS en effet, vous ne pouvez réellement pas ajouter d'accessoires à bool primitive. La distinction entre Bool et bool m'a foudroyé apparemment.
Konrad
@ ArturoTorresSánchez parce que, comme ils le font, les valeurs de retour sont nominalement du même type.
Jared Smith
1
@ ArturoTorresSánchez c'est pourquoi j'ai ajouté le qualificatif "nominalement": P
Jared Smith
1
@konrad Pour référence, par défaut, JavaScript prétend pouvoir vous permettre d'ajouter des propriétés aux primitives en ne vous plaignant pas lorsque vous essayez de le faire. L'utilisation du mode strict corrigera ce problème et lancera un TypeErrorsi vous essayez d'ajouter une propriété (voir jsbin.com/yovasafibo/edit?js,console ).
Matthew Crumley
59

Félicitations, vous avez découvert des objets. La raison pour ne pas le faire s'appelle le principe du moindre étonnement . Être surpris par un design n'est pas une bonne chose.

Il n'y a rien de mal à regrouper ces informations, mais pourquoi voudriez-vous les cacher dans un Bool? Mettez-le dans quelque chose que vous vous attendez à avoir avec toutes ces informations. Bool inclus.

confits_orange
la source
1
LOL oui, j'ai mentionné l'objet comme une alternative évidente dans ma question. Le principe de moindre étonnement est un bon point même si, avec JS, les objets de typage faibles sont moins surprenants mais restent déroutants et vous devez quand même vérifier la documentation. En ce qui concerne l’utilisation, Modernizr en est un bon exemple: vous avez une grande série de tests avec un résultat vrai / faux uniforme et seulement de temps en temps vous avez besoin de passer plus d’informations. Apportez des modifications radicales à l'ensemble de la bibliothèque en créant des enveloppes principalement redondantes autour des booléens ou améliorez-les. L'OMI n'est pas si bête.
Konrad
1
Un peu plus sur l'utilisation. Si vous vous en tenez à renvoyer une valeur booléenne, vous pouvez passer toutes les fonctions à la liste des opérations telles que any (), all (), filter () facilement. Il contourne le manque de typage fort ou d'interfaces formelles dans JS au prix d'un peu anticonformiste - ce qui semble être jusqu'à présent le principal argument à son encontre.
Konrad
1
"Non conventionnel" ne me semble pas être un argument de poids.
Robert Harvey
J'ai édité la question. Voici le principe de moindre étonnement. :-)
Konrad
16

L'argument principal contre lequel je m'oppose est le principe de responsabilité unique , un booléen ne devrait dire que si quelque chose est trueou falsenon, pas pourquoi, comment ou quoi que ce soit d'autre. Je suis fermement convaincu et pratique que d’autres objets devraient être utilisés pour communiquer cette information ou toute autre information.

J. Pichardo
la source
voulez-vous dire booléen ou booléen (avec B majuscule) en javascript, il y a une différence.
Pieter B
Mais si vous avez un objet définissant ceci et d'autres choses, ne violez-vous pas les mêmes principes?
Casey
1
@ Casey Non, puisque l'objet de cet objet serait différent de celui du booléen d'origine. Et il n'aurait qu'une responsabilité, à titre d'exemple, de communiquer l'état et la raison d'une transaction.
J. Pichardo
1
@ Casey le problème n'est pas que le fait de contenir cette information en soi est une violation du SRP. Cela ne devient un problème que lorsqu'il est associé à la responsabilité déjà assumée par un booléen: représenter le vrai ou le faux. JavaScript Booleanmalgré les manigances.
Jacob Raihle
10

Puisque toute la raison pour laquelle on l'appelle une valeur booléenne est le fait que ce soit vrai ou faux, je n'aime pas l'idée, car vous minimisez à peu près tout son objectif de wikipedia

En informatique, le type de données booléen est un type de données ayant deux valeurs (généralement notées true et false), destiné à représenter les valeurs de vérité de la logique et de l'algèbre booléenne .

(mon gras)

Afficher un nom
la source
1
correct, mais la nécessité peut en dicter autrement
svarog
8
@svarog la seule nécessité que je connaisse est un concepteur non qualifié du code. Si vous devez renvoyer bool et quelque chose d'autre, faites-en une classe, un tuple, ou une liste, ou tout autre élément qui rentre dans le code en question.
MatthewRock le
si vous
revenez
Je pense que la question concerne l'objet booléen et non la primitive booléenne. L'objet n'est pas une valeur.
Pieter B
1
Si cela peut prendre une valeur autre que vrai ou faux, il aussi est pas une valeur booléenne. Qui, vu son nom, est idiot.
Inutile
2

Ce n’est pas parce que vous le pouvez que vous devriez, vous pouvez très bien attacher des propriétés à n’importe quel objet dans JS (Booléens inclus)

Comment est-ce une mauvaise chose?

D'une part, vous pouvez attacher plus de données non pertinentes à un booléen (selon votre exemple), ce qu'un autre développeur ne s'attendra pas à recevoir, car pourquoi devrait-il être là?! Les bool sont juste pour le vrai et le faux.

Un contre-point consiste à attacher des propriétés utiles pouvant vous aider à gérer la valeur booléenne ( c'est ce que fait Java ).

Par exemple, vous pouvez attacher une fonction qui convertit la valeur booléenne en une chaîne, un dirtyindicateur devenu vrai si la valeur a été modifiée, des observateurs et des rappels d'événements pouvant se déclencher lorsque la valeur est modifiée, etc.

mais le booléen renvoyé (Bool est un objet de première classe dans JS) possède des propriétés spécifiant les formats pris en charge.

Cela ressemble à quelque chose qui ne devrait pas être stocké dans un booléen, mais plutôt à l'aide d'un ensemble de booléens ou d'un ensemble de propriétés contenant des booléens. Je pense qu'une meilleure approche serait de renvoyer un objet avec tous les formats et détails de support.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}
svarog
la source
1
voulez-vous dire booléen ou booléen (avec majuscule B)?
Pieter B
1

Vous ne pouvez pas créer de booléens avec des propriétés personnalisées en JavaScript. Les échecs suivants (au moins en FF):

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

Vous pouvez utiliser ou hériter de Boolean, mais comme le dit Matthew Crumley, cela donne des résultats différents. Booleanest de type Object . Lorsque JS a besoin de la valeur booléenne d'une expression, il convertit à l'aide de la fonction de spécification ToBoolean, ce qui indique que, pour Objects, le résultat est toujours true. Ainsi, la valeur est new Boolean(false)évaluée à true! Vous pouvez le vérifier dans cet exemple: https://jsfiddle.net/md7abx5z/3/ .

La seule raison pour laquelle cela fonctionne pour Modernizr est fortuite. Ils créent uniquement un Booleanobjet lorsque la condition est vraie. Quand ils évaluent faux, ils retournent simplement à l'ordinaire false. Donc, cela fonctionne car ils ne renvoient des Boolean objets que lorsque le résultat est vrai, de toute façon, et jamais lorsqu'il est faux.

Senevoldsen
la source
1

Je comprends que le contexte a changé, mais je voudrais répondre à la question initiale que j’avais lue en utilisant JS comme exemple, mais pas uniquement JS.

Ajouter des propriétés à un booléen ne poserait pas de problème SI les propriétés avaient quelque chose à voir avec true / false, pas avec la variable contenant la valeur. Par exemple, ajouter une méthode toYesNoString serait bien, ajouter un numberOfChildren à une valeur hasChildren ne le serait pas, pas plus que questionsMassed studentPassed. Il n'y a pas grand-chose que vous puissiez ajouter à un booléen, mis à part diverses représentations de chaîne, la seule propriété à laquelle cela puisse donner un sens est originalExpression. Mais y ajouter n'est pas nécessairement une mauvaise idée en théorie.

Jmoreno
la source
0

Si je regarde le code, il ne s'agit pas vraiment de donner des propriétés booléennes personnalisées, mais d'une méthode ayant des méthodes de retour avec des signatures différentes.

Si elle est fausse, vous obtenez une primitive fausse et, si elle est vraie, elle retourne un objet qui, par définition, est interprété comme étant vrai en javascript.

Donc, à mon avis, votre question ne devrait pas être de savoir si c’est une bonne idée de donner des propriétés personnalisées à un booléen, mais bien d’avoir des méthodes avec plusieurs signatures de retour.

Pieter B
la source
0

Dans l'exemple donné, ne pouvez - vous pas simplement renvoyer un tableau de tous les formats vidéo pris en charge?

  • Un tableau vide signifie "aucun format vidéo pris en charge", ce qui signifie "aucune vidéo prise en charge".
  • Sinon, si un format vidéo est pris en charge, la vidéo en général est évidemment prise en charge.

Je dirais au moins que l'utilisation de array est un peu moins surprenante que d'avoir des "booleans personnalisés".

En Javascript, un tableau vide est même considéré comme de la fausseté , alors qu'un tableau non vide est une vérité , alors avec un peu de chance, vous pouvez simplement passer à des tableaux et tout fonctionnerait comme avant Edit Nope, idiot moi de penser que je pourrais me souvenir de la vérité d'objets en JavaScript: P

Daniero
la source
Un tableau vide n'est pas évalué comme false dans une instruction if. if ([]) console.log ('not false'); affiche 'not false' dans Chrome par exemple. typeof [] === 'objet' donc aucun tableau vide n'est faux. Voir developer.mozilla.org/en-US/docs/Glossary/Truthy pour référence.
Joshp
@ joshp oups, vous avez raison, mon mauvais. Fixé
daniero
Bien que, je ne suis pas sûr que le type de chose prouve quelque chose; ""a typeof "string"mais c'est en réalité de la fausseté
daniero
1
D’un autre côté, [].lengthfalsey si longueur est 0, n’est pas beaucoup plus typé et, à mon avis, est une meilleure indication de l’intention que ne le if([])serait même si cela fonctionnait.
IllusiveBrian
@daniero Le point à propos de typeof [] === 'objet' est que tout objet est une vérité, même un nouveau booléen (faux). Certains types primitifs (par exemple, chaîne "" et nombre 0) sont faux, mais ils ne sont pas des objets en ce qui concerne typeof. C'est juste une autre façon de se souvenir. Il ne s'agit pas de prouver quoi que ce soit. La preuve est dans la spécification ou les tests, selon votre préférence.
Joshp