Ist ein JS Boolean mit benutzerdefinierten Eigenschaften eine schlechte Praxis?

41

In JS können Sie einen Booleschen Wert mit benutzerdefinierten Eigenschaften zurückgeben. Z.B. Wenn Modernizr die Videounterstützung testet, gibt es zurück, trueoder falseaber der zurückgegebene Boolesche Wert (Bool ist ein erstklassiges Objekt in JS) verfügt über Eigenschaften, die angeben, welche Formate unterstützt werden. Zuerst überraschte es mich ein bisschen, aber dann fing ich an, die Idee zu mögen und mich zu fragen, warum sie eher sparsam verwendet zu werden scheint.

Es sieht aus wie eine elegante Art und Weise, mit all den Szenarien umzugehen, in denen Sie im Grunde wissen möchten, ob etwas wahr oder falsch ist, aber möglicherweise an zusätzlichen Informationen interessiert sind, die Sie definieren können, ohne ein benutzerdefiniertes Rückgabeobjekt zu definieren oder eine Rückruffunktion zu verwenden, für die Sie vorbereitet sind Akzeptieren Sie weitere Parameter. Auf diese Weise behalten Sie eine sehr universelle Funktionssignatur bei, ohne die Fähigkeit zur Rückgabe komplexerer Daten zu beeinträchtigen.

Es gibt drei Argumente dagegen, die ich mir vorstellen kann:

  1. Es ist etwas ungewöhnlich / unerwartet, wenn es wahrscheinlich besser ist, wenn eine Schnittstelle klar und nicht schwierig ist.
  2. Dies mag ein Strohmann-Argument sein, aber da es sich um einen Randfall handelt, kann ich mir vorstellen, dass es in einigen JS-Optimierern, Uglifizierern, VMs oder nach einer geringfügigen Änderung der Aufräum-Sprachspezifikationen usw. leise nach hinten losgeht.
  3. Es gibt eine präzisere, klarere und allgemeinere Methode, genau dasselbe zu tun.

Meine Frage ist also, ob es starke Gründe gibt, die Verwendung von Booleschen Werten mit zusätzlichen Eigenschaften zu vermeiden. Sind sie ein Trick oder ein Leckerbissen?


Plot Twists Warnung.

Oben ist die ursprüngliche Frage in voller Pracht. Wie Matthew Crumley und Senevoldsen beide betonten, basiert es auf einer falschen (falschen?) Prämisse. In feiner JS-Tradition ist Modernizr ein Sprachtrick und ein schmutziger. Es läuft darauf hinaus, dass JS einen primitiven Bool hat, der, wenn er auf false gesetzt ist, auch nach dem VERSUCH, Requisiten hinzuzufügen (was unbemerkt fehlschlägt), und ein boolesches Objekt, das benutzerdefinierte Requisiten haben kann, aber ein Objekt ist, immer wahr. Modernizr gibt entweder Boolean false oder ein wahrheitsgemäßes Boolean-Objekt zurück.

Meine ursprüngliche Frage ging davon aus, dass der Trick anders funktioniert, und daher beschäftigen sich die beliebtesten Antworten mit dem Aspekt der (perfekt gültigen) Codierungsstandards. Allerdings finde ich die Antworten, die den ganzen Trick entlarven, am hilfreichsten (und auch die ultimativen Argumente gegen die Verwendung der Methode), sodass ich eine davon akzeptiere. Vielen Dank an alle Teilnehmer!

konrad
quelle
35
Die Erweiterung des Booleschen Typs ist ein klassisches wtf .
hlovdal
7
Beachten Sie, dass sie in JS möglicherweise nur zurückgegeben wurden, nullwenn sie nicht unterstützt werden, und in diesem Fall eine Reihe von Formaten. Eine Liste wird in JS als wahr angesehen und nullist falsch.
jpmc26
1
Verwandte Frage: Meine
3
Bevor Sie diese Frage beantworten: In Javascript gibt es einen großen Unterschied zwischen "Boolean" und "Boolean". Sie sind nicht dasselbe und jede Antwort, bei der Boolean nicht groß geschrieben wird, ist ungültig.
Pieter B
2
Für diejenigen unter Ihnen, die verblüfft sind, so etwas in freier Wildbahn in einer so beliebten Bibliothek wie Modernizr zu sehen, ist hier der relevante Code von ihrem GitHub: github.com/Modernizr/Modernizr/blob/…
Chris Nielsen

Antworten:

38

Zusätzlich zu den allgemeinen Gestaltungsprinzipien, wie Einzelverantwortung und am wenigsten überraschend, gibt es einen JavaScript-spezifischen Grund, warum dies keine gute Idee ist: Es gibt einen großen Unterschied zwischen a booleanund Booleanin JavaScript, der verhindert, dass es im allgemeinen Fall funktioniert.

booleanist ein primitiver Typ, kein Objekt und kann keine benutzerdefinierten Eigenschaften haben. Ausdrücke mögen true.toString()Arbeit, denn hinter den Kulissen verwandelt sie sich in (new Boolean(true)).toString().

Boolean(mit einem Großbuchstaben B) ist ein Objekt, hat aber nur sehr wenige gute Verwendungszwecke, und es booleangehört definitiv nicht dazu , als verwendet zu werden. Der Grund dafür ist, dass jeder Boolean"wahr" ist, unabhängig von seinem Wert , da alle Objekte truein einen booleschen Kontext konvertiert werden . Versuchen Sie zum Beispiel Folgendes:

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

Im Allgemeinen gibt es keine Möglichkeit, einem Booleschen Wert in JavaScript Eigenschaften hinzuzufügen, mit denen sich das Verhalten dennoch logisch verhält. Modernizr kann damit durchkommen, weil die einzigen Eigenschaften zu "wahren" Werten hinzufügen, welche Art von Arbeit Sie erwarten würden (dh sie funktionieren in if-Anweisungen). Wenn Video überhaupt nicht unterstützt Modernizr.videowird, ist es ein tatsächliches boolean(mit dem Wert false) und es können keine Eigenschaften hinzugefügt werden.

Matthew Crumley
quelle
18
Ich finde den Ansatz von Modernizr ziemlich seltsam, wenn man bedenkt, dass nur ein Objekt mit Eigenschaften bereits trueals bedingt ausgewertet wird. Warum explizit a verwenden Boolean?
Arturo Torres Sánchez
Vielen Dank für ein fundiertes technisches Argument. Anscheinend war mein Schnelltest fehlerhaft: jsbin.com/hurebi/3/edit?js,console . Sogar nach dem Hinzufügen von benutzerdefinierten Requisiten ist falsees streng 'falsch' und falsch ( ===und ==funktioniert wie), ABER in der Tat können Sie primitiven Narren keine Requisiten hinzufügen. Die Unterscheidung zwischen Bool und Bool hat mich offensichtlich verunsichert.
Konrad
@ ArturoTorresSánchez, weil die Rückgabewerte nominell vom gleichen Typ sind.
Jared Smith
1
@ ArturoTorresSánchez deshalb habe ich den Qualifier "nominal" hinzugefügt: P
Jared Smith
1
@konrad Als Referenz gibt JavaScript standardmäßig vor, dass Sie den Grundelementen Eigenschaften hinzufügen können, indem Sie sich nicht beschweren, wenn Sie dies versuchen. Die Verwendung des strikten Modus behebt dies und wirft ein, TypeErrorwenn Sie versuchen, eine Eigenschaft hinzuzufügen (siehe jsbin.com/yovasafibo/edit?js,console ).
Matthew Crumley
59

Herzlichen Glückwunsch, Sie haben Objekte entdeckt. Den Grund, dies nicht zu tun, nennt man das Prinzip des geringsten Erstaunens . Von einem Design überrascht zu sein, ist keine gute Sache.

Es ist nichts Falsches daran, diese Informationen zu bündeln, aber warum sollten Sie sie in einem Bool verstecken? Schreiben Sie es in etwas, von dem Sie erwarten, dass es all diese Informationen enthält. Bool enthalten.

kandierte_orange
quelle
1
LOL ja ich erwähnte Objekt als offensichtliche Alternative in meiner Frage. Das Prinzip des geringsten Erstaunens ist ein guter Punkt, auch wenn mit JS schwache Typisierungsobjekte weniger überraschend, aber immer noch verwirrend sind und Sie trotzdem Dokumente überprüfen müssen. Was die Verwendung betrifft, ist Modernizr ein gutes Beispiel: Sie haben eine große Reihe von Tests mit einheitlichem Richtig / Falsch-Ergebnis und müssen nur ab und zu weitere Informationen weitergeben - vielleicht sogar die, die später benötigt werden, wenn dies möglich ist Nehmen Sie wichtige Änderungen an der gesamten Bibliothek vor, indem Sie meist redundante Wrapper um Boolesche Werte erstellen, oder erweitern Sie den Bool. IMO nicht so dumm.
KONRAD
1
Ein bisschen mehr über die Nutzung. Wenn Sie sich an die Rückgabe eines Booleschen Werts halten, können Sie alle Funktionen einfach an Listenoperationen wie any (), all (), filter () übergeben. Es umgeht das Fehlen starker Typisierung oder formaler Schnittstellen in JS und ist ein wenig unkonventionell - was bislang das Hauptargument dafür zu sein scheint.
Konrad
1
"Unkonventionell" scheint mir kein starkes Argument zu sein.
Robert Harvey
Ich habe die Frage bearbeitet. Hier ist das Prinzip des geringsten Erstaunens. :-)
konrad
16

Das Hauptargument, das ich dagegen halte, ist das Prinzip der Einzelverantwortung . Ein Boolescher sollte nur sagen, ob etwas ist trueoder falsenicht, warum oder wie oder irgendetwas anderes. Ich bin der festen Überzeugung und Praxis, dass andere Objekte verwendet werden sollten, um diese oder andere Informationen zu kommunizieren.

J. Pichardo
quelle
Meinst du Boolean oder Boolean (mit Großbuchstaben B) in Javascript gibt es einen Unterschied.
Pieter B
Aber wenn Sie ein Objekt haben, das dieses und einige andere Dinge definiert, verstößt es möglicherweise gegen dieselben Prinzipien?
Casey
1
@Casey Nein, da der Zweck dieses Objekts vom ursprünglichen Booleschen Wert abweicht. Und es hätte nur eine Verantwortung, den Status und den Grund einer Transaktion als Beispiel mitzuteilen.
J. Pichardo
1
@Casey das Problem ist nicht, dass das Enthalten dieser Informationen für sich genommen eine Verletzung der SRP ist. In Verbindung mit der Verantwortung, die ein Boolescher bereits hat, wird es nur zu einem Problem - wahr oder falsch zu repräsentieren. BooleanUngeachtet der Spielereien von JavaScript .
Jacob Raihle
10

Da der ganze Grund, warum es ein Boolescher Wert genannt wird, die Tatsache ist, dass es wahr oder falsch ist, mag ich die Idee nicht, da Sie so ziemlich den gesamten Zweck von Wikipedia untergraben

In der Informatik ist der Boolesche Datentyp ein Datentyp mit zwei Werten (normalerweise als wahr und falsch bezeichnet), die die Wahrheitswerte der Logik und der Booleschen Algebra darstellen sollen .

(meine Kühnheit)

Anzeigename
quelle
1
richtig, aber die notwendigkeit kann etwas anderes vorschreiben
svarog
8
@svarog Die einzige Notwendigkeit, die ich sehen kann, ist der unerfahrene Designer des Codes. Wenn Sie bool und etwas anderes zurückgeben müssen, machen Sie es zu einer Klasse, einem Tupel, einer Liste oder zu etwas anderem, das in den bestimmten Code passt.
MatthewRock
wenn Sie ja zurückkehren
svarog
Ich denke, die Frage betrifft das Boolesche Objekt, nicht das Boolesche Primitiv. Das Objekt ist kein Wert.
Pieter B
1
Wenn es einen anderen Wert als true oder false annehmen kann, ist es auch kein Boolescher Wert . Was angesichts des Namens albern ist.
Nutzlos
2

Nur weil Sie das nicht können, können Sie in JS praktisch jedem Objekt Eigenschaften zuweisen (einschließlich Boolescher Werte).

Wie ist das eine schlechte Sache?

Einerseits können Sie einem Booleschen Wert (gemäß Ihrem Beispiel) irrelevantere Daten hinzufügen, was ein anderer Entwickler nicht erwartet, denn warum sollte es dort sein ?! Narren sind nur für wahr und falsch.

Ein Gegenpol dazu sind einige relevante nützliche Eigenschaften, die Ihnen beim Umgang mit dem Booleschen Wert helfen können ( Java macht das ).

Sie können beispielsweise eine Funktion anfügen, die den Booleschen Wert in einen String konvertiert, ein dirtyFlag, das bei Änderung des Werts auf true gesetzt wurde, Watcher und Event-Callbacks, die bei Änderung des Werts ausgelöst werden können usw.

Der zurückgegebene Boolesche Wert (Bool ist ein erstklassiges Objekt in JS) verfügt jedoch über Eigenschaften, die angeben, welche Formate unterstützt werden

Es klingt nach etwas, das nicht in einem Booleschen Wert gespeichert werden sollte, sondern eine Reihe von Booleschen Werten oder eine Reihe von Eigenschaften mit darin enthaltenen Booleschen Werten. Ich denke, ein besserer Ansatz wäre, ein Objekt mit allen Formaten und Unterstützungsdetails zurückzugeben.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}
svarog
quelle
1
Meinst du Boolean oder Boolean (mit dem Großbuchstaben B)?
Pieter B
1

Sie können keine Booleschen Werte mit benutzerdefinierten Eigenschaften in JavaScript erstellen. Folgendes schlägt fehl (zumindest in FF):

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

Sie können Boolean verwenden oder von Boolean erben, aber wie Matthew Crumley sagt, führt dies zu unterschiedlichen Ergebnissen. Booleanist vom Typ Object . Wenn JS den Booleschen Wert eines Ausdrucks benötigt, konvertiert es ihn mit der Spezifikationsfunktion ToBoolean, die vorschreibt, dass für Objects immer das Ergebnis ist true. So ist der Wert new Boolean(false)ausgewertet true! Sie können dies in folgendem Beispiel überprüfen: https://jsfiddle.net/md7abx5z/3/ .

Der einzige Grund, warum es für Modernizr funktioniert, ist zufällig. Sie erstellen nur dann ein BooleanObjekt, wenn die Bedingung erfüllt ist. Wenn sie false auswerten, kehren sie einfach zu normal zurück false. Es funktioniert also, weil sie Boolean Objekte nur zurückgeben , wenn das Ergebnis ohnehin wahr ist, und niemals, wenn es falsch ist.

senevoldsen
quelle
1

Ich verstehe, dass sich der Kontext geändert hat, aber ich möchte die ursprüngliche Frage, die ich als Beispiel für JS gelesen habe, beantworten, aber nicht nur auf JS beschränkt.

Das Hinzufügen von Eigenschaften zu einem Booleschen Wert wäre kein Problem, wenn die Eigenschaften etwas mit true / false zu tun hätten, nicht mit Variablen, die den Wert enthalten. Zum Beispiel wäre das Hinzufügen einer toYesNoString-Methode in Ordnung, das Hinzufügen einer numberOfChildren zu einem hasChildren-Wert nicht, und auch questionsMissed studentPassed nicht. Es gibt nicht viel, was Sie zu einem Booleschen Wert hinzufügen könnten, außer verschiedenen Zeichenfolgenrepräsentationen. Die einzige Eigenschaft, an die ich denken kann, die Sinn macht, ist originalExpression. Aber es ist theoretisch nicht unbedingt eine schlechte Idee, etwas hinzuzufügen.

jmoreno
quelle
0

Wenn ich mir den Code anschaue, geht es nicht wirklich darum, boolesche benutzerdefinierte Eigenschaften anzugeben, sondern um eine Methode mit Rückgabemethoden mit unterschiedlichen Signaturen.

Wenn es falsch ist, erhalten Sie ein primitives false und wenn es wahr ist, gibt es ein Objekt zurück, das per Definition in Javascript als wahr interpretiert wird.

Daher sollte Ihre Frage meiner Meinung nach nicht lauten, ob es eine gute Idee ist, boolesche benutzerdefinierte Eigenschaften zu vergeben, sondern ob es eine gute Idee ist, Methoden mit mehreren Rückgabesignaturen zu verwenden.

Pieter B
quelle
0

Können Sie im Beispiel nicht einfach ein Array aller unterstützten Videoformate zurückgeben?

  • Ein leeres Array bedeutet "keine Videoformate unterstützt", was in der Reihenfolge "kein Video unterstützt" bedeutet.
  • Andernfalls wird Video im Allgemeinen unterstützt , wenn ein Videoformat unterstützt wird.

Ich würde zumindest sagen, dass die Verwendung von Array etwas weniger überraschend ist als die Verwendung von "benutzerdefinierten Booleschen".

In Javascript, wird ein leeres Array sogar als falsy , während ein nicht-leeres Array ist truthy , so mit etwas Glück könnten Sie einfach wechseln zu Arrays und alles so , wie vor der Arbeit wäre bearbeiten Nein, mir dumm zu denken , ich die Truthiness erinnern konnte , von Objekten in JavaScript: P

daniero
quelle
Ein leeres Array wird in einer if-Anweisung nicht als falsch ausgewertet. if ([]) console.log ('nicht falsch'); gibt beispielsweise in Chrome "nicht falsch" aus. typeof [] === 'object' also nein, ein leeres Array ist nicht falsch. Siehe developer.mozilla.org/en-US/docs/Glossary/Truthy als Referenz.
Joshp
@joshp hoppla, du hast recht, mein böser. Feste
daniero
Obwohl ich nicht sicher bin, ob die Art der Sache irgendetwas beweist; ""hat Typof, "string"aber es ist tatsächlich falsch
Daniero
1
Auf der anderen Seite [].lengthist Falsch, wenn Länge ist 0, ist nicht viel mehr Typisierung, und meiner Meinung nach ist dies ein besseres Indiz für Absicht, als es einfach if([])wäre, selbst wenn dies funktioniert hätte.
IllusiveBrian
@daniero Der Punkt über typeof [] === 'object' ist, dass jedes Objekt wahr ist, sogar das neue Boolesche (false). Einige primitive Typen (z. B. Zeichenfolge "" und Nummer 0) sind falsch, für den Typ jedoch keine Objekte. Es ist nur eine andere Art, sich zu erinnern. Es geht nicht darum, etwas zu beweisen. Der Beweis ist in der Spezifikation oder den Tests, je nachdem, was Sie bevorzugen.
Joshp