Un booleano JS con proprietà personalizzate è una cattiva pratica?


41

In JS puoi restituire un valore booleano con proprietà personalizzate. Per esempio. quando Modernizr verifica il supporto video, ritorna trueo falsema il valore booleano restituito (Bool è un oggetto di prima classe in JS) ha proprietà che specificano quali formati sono supportati. All'inizio mi ha sorpreso un po ', ma poi ho iniziato a piacermi l'idea e ho iniziato a chiedermi perché sembra essere usato con parsimonia?

Sembra un modo elegante di gestire tutti quegli scenari in cui sostanzialmente vuoi sapere se qualcosa è vero o falso ma potresti essere interessato ad alcune informazioni aggiuntive che puoi definire senza definire un oggetto di ritorno personalizzato o utilizzando una funzione di callback preparata per accetta più parametri. In questo modo si conserva una firma di funzione molto universale senza compromettere la capacità di restituzione di dati più complessi.

Ci sono 3 argomenti contrari che posso immaginare:

  1. È un po 'insolito / inaspettato quando probabilmente è meglio che qualsiasi interfaccia sia chiara e non complicata.
  2. Questo potrebbe essere un argomento da uomo di paglia, ma essendo un po 'un caso limite, posso immaginarlo tranquillamente ritorcersi contro alcuni ottimizzatori, ugelli, VM JS o dopo una piccola modifica delle specifiche del linguaggio di pulizia ecc.
  3. Esiste un modo migliore, conciso, chiaro e comune di fare esattamente lo stesso.

Quindi la mia domanda è: ci sono delle valide ragioni per evitare di usare i booleani con proprietà aggiuntive? Sono uno scherzetto o dolcetto?


Trama avviso colpi di scena.

Sopra è la domanda originale in piena gloria. Come hanno sottolineato Matthew Crumley e Senevoldsen, si basa su una premessa falsa (falsa?). Nella raffinata tradizione di JS, ciò che Modernizr fa è un trucco linguistico e uno sporco. Si riduce a JS che ha un bool primitivo che se impostato su falso rimarrà falso anche dopo aver provato ad aggiungere oggetti di scena (che fallisce silenziosamente) e un oggetto booleano che può avere oggetti di scena personalizzati ma essere un oggetto è sempre vero. Modernizr restituisce un booleano falso o un vero oggetto booleano.

La mia domanda originale presupponeva che il trucco funzionasse diversamente e quindi le risposte più popolari riguardano l'aspetto degli standard di codifica (perfettamente validi). Comunque trovo le risposte debunking tutto il trucco più utili (e anche gli argomenti finali contro l'utilizzo del metodo), quindi ne accetto uno. Grazie a tutti i partecipanti!


35
L'estensione del tipo booleano è un classico wtf .
hlovdal

7
Si noti che in JS avrebbero potuto essere restituiti nullse non supportati e una serie di formati in tal caso. Una lista è considerata vera in JS ed nullè falsa.
jpmc26


3
Prima di rispondere a questa domanda: in javascript c'è una grande differenza tra "booleano" e "booleano". Non sono la stessa cosa e qualsiasi risposta che non capitalizza il valore booleano non è valida.
Pieter B,

2
Per quelli di voi che sono sorpresi di vedere qualcosa di simile in natura, in una libreria popolare come Modernizr, ecco il codice pertinente dal loro GitHub: github.com/Modernizr/Modernizr/blob/…
Chris Nielsen

Risposte:


38

Oltre ai principi generali di progettazione, come la responsabilità singola e la minima sorpresa, c'è un motivo specifico per JavaScript che non è una buona idea: c'è un'enorme differenza tra un booleane Booleanin JavaScript che gli impedisce di funzionare nel caso generale.

booleanè un tipo primitivo, non un oggetto e non può avere proprietà personalizzate. Alle espressioni piace true.toString()lavorare perché dietro le quinte si trasforma (new Boolean(true)).toString().

Boolean(con la B maiuscola) è un oggetto, ma ha pochissimi usi utili e essere usato come un non booleanè sicuramente uno di questi. Il motivo è che ogni cosa Booleanè "vera", indipendentemente dal suo valore , perché tutti gli oggetti vengono convertiti truein un contesto booleano. Ad esempio, prova questo:

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

Quindi, in generale, non c'è modo di aggiungere proprietà a un valore booleano in JavaScript che permetta comunque che si comporti in modo logico. Modernizr può cavarsela perché le uniche aggiungono proprietà ai valori "veri", che in qualche modo funzionano come ti aspetteresti (cioè se funzionano in istruzioni if). Se il video non è supportato, Modernizr.videosarà un valore reale boolean(con il valore false) e non potrà essere aggiunto proprietà.


18
Trovo l'approccio di Modernizr piuttosto strano, considerando che avere solo un oggetto con proprietà già valuta truein un condizionale. Perché usare esplicitamente un Boolean?
Arturo Torres Sánchez,

Grazie mille per un valido argomento tecnico. Sembra che il mio test rapido sia stato imperfetto: jsbin.com/hurebi/3/edit?js,console . Anche dopo aver aggiunto oggetti di scena personalizzati falsesia rigorosamente "falsi" che falsi ( ===e ==funziona come) MA in realtà non è possibile aggiungere oggetti di scena al bool primitivo. La distinzione tra Bool e bool mi apparentemente fuggì.
Konrad

@ ArturoTorresSánchez perché il modo in cui lo fanno i valori di ritorno sono nominalmente dello stesso tipo.
Jared Smith,

1
@ ArturoTorresSánchez ecco perché ho aggiunto la qualifica "nominalmente": P
Jared Smith

1
@konrad Per riferimento, per impostazione predefinita JavaScript farà finta che ti permetta di aggiungere proprietà ai primitivi senza lamentarti quando provi a farlo. L'uso della modalità rigorosa risolverà questo problema e genererà un erroreTypeError se provi ad aggiungere una proprietà (vedi jsbin.com/yovasafibo/edit?js,console ).
Matthew Crumley,

59

Congratulazioni, hai scoperto oggetti. Il motivo per non farlo è chiamato il principio del minimo stupore . Essere sorpresi da un design non è una buona cosa.

Non c'è niente di sbagliato nel raggruppare queste informazioni, ma perché dovresti nasconderle in un Bool? Inseriscilo in qualcosa che ti aspetti di avere tutte queste informazioni. Bool incluso.


1
LOL sì, ho citato l'oggetto come ovvia alternativa alla mia domanda. Il principio del minimo stupore è un buon punto, anche se con JS gli oggetti di digitazione deboli sono meno sorprendenti ma comunque confusi e devi comunque controllare i documenti. Per quanto riguarda l'uso, Modernizr è un buon esempio: hai una vasta gamma di test con risultati uniformi vero / falso e solo di tanto in tanto devi passare più informazioni - forse anche la necessità di quelli è arrivata più tardi, quindi quando puoi apporta modifiche sostanziali all'intera libreria creando involucri prevalentemente ridondanti attorno ai booleani o aumenti il ​​valore booleano. IMO non così stupido.
Konrad

1
Un po 'di più sull'utilizzo. Se continui a restituire un valore booleano, puoi passare tutte le funzioni per elencare le operazioni come any (), all (), filter () facilmente. Evita la mancanza di una tipizzazione forte o interfacce formali in JS al costo di essere un po 'non convenzionale - il che sembra finora essere l'argomento principale contro di esso.
Konrad

1
"Unconventional" non mi sembra un argomento forte.
Robert Harvey,

Ho modificato la domanda. Ecco il principio del minimo stupore. :-)
Konrad

16

L'argomento principale a cui mi oppongo è il principio della responsabilità singola , un booleano dovrebbe dire solo se qualcosa è trueo false, non perché o come o qualcos'altro . Sono fermamente convinto e pratico che altri oggetti debbano essere utilizzati per comunicare quella o qualsiasi altra informazione.


vuoi dire booleano o booleano (con la B maiuscola) in JavaScript c'è una differenza.
Pieter B,

Ma se hai un oggetto che definisce questo e alcune altre cose, non viola probabilmente gli stessi principi?
Casey,

1
@Casey No poiché lo scopo di quell'oggetto sarebbe diverso dal booleano originale. E avrebbe solo una responsabilità, come esempio, comunicare lo stato e la ragione di una transazione.
J. Pichardo,

1
@Casey il problema non è che contenere queste informazioni di per sé è una violazione di SRP. Diventa un problema solo se combinato con la responsabilità che ha già un booleano: rappresentare vero o falso. BooleanNonostante gli shenanigans di JavaScript .
Jacob Raihle,

10

Dal momento che l'intero motivo per cui viene chiamato valore booleano è il fatto che sia vero o falso, non mi piace l'idea, poiché praticamente minacci il suo intero scopo da Wikipedia

Nell'informatica, il tipo di dati booleani è un tipo di dati, con due valori (generalmente indicato come vero e falso), intesi a rappresentare i valori di verità della logica e dell'algebra booleana .

(il mio coraggio)


1
corretto, ma la necessità può dettare diversamente
svarog

8
@svarog l'unica necessità che posso vedere è il progettista non specializzato del codice. Se devi restituire bool e qualcos'altro, rendilo una classe, una tupla, una lista o qualsiasi altra cosa che si adatti al particolare codice.
Matthew Rock

se stai tornando sì
svarog

Penso che la domanda riguardi l'oggetto booleano e non la primitiva booleana. L'oggetto non è un valore.
Pieter B,

1
Se si può prendere un valore diverso da vero o falso, ma anche non è un valore booleano. Che, dato il nome, è sciocco.
Inutile

2

Solo perché non puoi significare che dovresti, in JS puoi praticamente associare proprietà a qualsiasi oggetto (inclusi i booleani)

Come è una cosa negativa?

Da un lato, puoi allegare dati più irrilevanti a un booleano (per il tuo esempio), qualcosa che un altro sviluppatore non si aspetta perché perché dovrebbe essere lì ?! i bool sono veri e falsi.

Un contrappunto per esso è quello di allegare alcune proprietà utili rilevanti che potrebbero aiutarti a gestire il valore booleano ( Java lo fa ).

Ad esempio, è possibile associare una funzione che converte il valore booleano in una stringa, un dirtyflag che diventa vero se il valore è stato modificato, watcher e callback di eventi che possono attivarsi quando il valore viene modificato, ecc.

ma il booleano restituito (Bool è un oggetto di prima classe in JS) ha proprietà che specificano quali formati sono supportati

Sembra qualcosa che non dovrebbe essere archiviato in un valore booleano, ma piuttosto usando un insieme di valori booleani o un insieme di proprietà con valori booleani al loro interno. Penso che un approccio migliore sarebbe quello di restituire un oggetto con tutti i formati e i dettagli di supporto.

{
    isSomethingSupported: true,
    isSomethingElseSupported: false,
    ....
}

1
vuoi dire booleano o booleano (con la maiuscola B)?
Pieter B,

1

Non puoi creare booleani con proprietà personalizzate in JavaScript. Il seguente errore (almeno in FF):

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

È possibile utilizzare o ereditare da Boolean, ma come dice Matthew Crumley, dà risultati diversi. Booleanè di tipo Object . Quando JS necessita del valore booleano di un'espressione, converte utilizzando la funzione di specifica ToBoolean, che impone che per Objects il risultato sia sempre true. Quindi il valore new Boolean(false)valuta true! Puoi verificarlo in questo esempio: https://jsfiddle.net/md7abx5z/3/ .

L'unica ragione per cui funziona per Modernizr è casuale. Creano un Booleanoggetto solo quando la condizione è vera. Quando valutano falso, ritornano semplicemente ordinari false. Quindi funziona perché restituiscono Boolean oggetti solo quando il risultato era vero comunque e mai quando è falso.


1

Capisco che il contesto è cambiato, ma mi piacerebbe rispondere alla domanda originale che ho letto usando come esempio JS, ma non solo JS.

Aggiungere proprietà a un valore booleano non sarebbe un problema SE le proprietà avevano qualcosa a che fare con il vero / falso, non con la variabile che conteneva il valore. Ad esempio, l'aggiunta di un metodo toYesNoString andrebbe bene, l'aggiunta di un numeroOfChildren a un valore hasChildren non lo è, e neanche domandeMissed studentPassed. Non c'è molto che potresti aggiungere a un valore booleano, a parte le varie rappresentazioni di stringhe, l'unica proprietà a cui riesco a pensare che avrebbe senso è originalExpression. Ma aggiungerlo non è necessariamente una cattiva idea in teoria.


0

Se guardo il codice, non si tratta in realtà di fornire proprietà personalizzate booleane, ma di un metodo con metodi di restituzione con firme diverse.

Se è falso allora ottieni un falso primitivo e se è vero restituisce un oggetto che per definizione viene interpretato come vero in javascript.

Quindi, a mio avviso, la tua domanda non dovrebbe essere se sia una buona idea dare proprietà personalizzate booleane, ma se è una buona idea avere metodi con più firme di ritorno.


0

Nell'esempio dato, non potresti semplicemente restituire un array di tutti i formati video supportati?

  • Un array vuoto significa "nessun formato video supportato", che in ordine significa "nessun video supportato".
  • Altrimenti, se è supportato qualsiasi formato video , ovviamente è supportato il video in generale.

Direi almeno che l'uso dell'array è in qualche modo meno sorprendente che avere "booleani personalizzati".

In Javascript, una matrice vuota è persino considerata falsa , mentre una matrice non vuota è vera , quindi con un po 'di fortuna potresti semplicemente passare alle matrici e tutto funzionerebbe proprio come prima di modificare No, sciocca me per pensare che potrei ricordare la verità di oggetti in JavaScript: P


Un array vuoto non viene valutato come falso in un'istruzione if. if ([]) console.log ('not false'); stampa "non falso" in Chrome, ad esempio. typeof [] === 'oggetto' quindi nessun array vuoto non è falso. Vedi developer.mozilla.org/en-US/docs/Glossary/Truthy per riferimento.
joshp

@joshp oops, hai ragione, mia cattiva. Risolto
daniero

Sebbene, non sono sicuro che il tipo di cosa provi qualcosa; ""ha typeof "string"ma in realtà è falso
daniero

1
D'altra parte, [].lengthè falso se la lunghezza è 0, non è molto più dattiloscritto, e secondo me è una migliore indicazione di intenti di quanto lo if([])sarebbe anche se funzionasse.
IllusiveBrian

@daniero Il punto su typeof [] === 'oggetto' è che ogni Oggetto è veritiero, persino nuovo booleano (falso). Alcuni tipi primitivi (es. Stringa "" e numero 0) sono falsi, ma non sono oggetti per quanto riguarda typeof. È solo un altro modo per ricordare. Non si tratta di provare nulla. La prova è nelle specifiche o nei test, a seconda delle preferenze.
joshp
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.