Leere
Ich empfehle nicht, eine Funktion zu definieren oder zu verwenden, die berechnet, ob ein Wert auf der ganzen Welt leer ist. Was bedeutet es wirklich, "leer" zu sein? Wenn ja let human = { name: 'bob', stomach: 'empty' }
, sollte ich isEmpty(human)
zurückkehren true
? Wenn ja let reg = new RegExp('');
, sollte ich isEmpty(reg)
zurückkehren true
? Was ist mit isEmpty([ null, null, null, null ])
- diese Liste enthält nur Leere, ist die Liste selbst also leer? Ich möchte hier einige Anmerkungen zur "Leere" (ein absichtlich undurchsichtiges Wort, um bereits bestehende Assoziationen zu vermeiden) in Javascript machen - und ich möchte argumentieren, dass "Leere" in Javascript-Werten niemals generisch behandelt werden sollte.
Wahrhaftigkeit / Falschheit
Um zu entscheiden, wie die "Leere" von Werten bestimmt werden soll, müssen wir dem in Javascript eingebauten, inhärenten Gefühl Rechnung tragen, ob Werte "wahr" oder "falsch" sind. Natürlich null
und undefined
sind beide "falsch". Weniger natürlich ist die Nummer 0
(und keine andere Nummer außer NaN
) auch "falsch". Am wenigsten natürlich: ''
ist falsch, aber []
und {}
(und new Set()
, und new Map()
) sind wahr - obwohl sie alle gleich leer scheinen!
Null vs Undefiniert
Es gibt auch einige Diskussionen über null
vs undefined
- brauchen wir wirklich beides, um Leere in unseren Programmen auszudrücken? Ich persönlich vermeide es, dass die Buchstaben u, n, d, e, f, i, n, e, d in dieser Reihenfolge in meinem Code erscheinen. Ich benutze immer null
"Leere". Aber auch hier müssen wir dem inhärenten Sinn null
und undefined
Unterschied von Javascript Rechnung tragen :
- Der Versuch, auf eine nicht vorhandene Eigenschaft zuzugreifen, gibt
undefined
- Das Weglassen eines Parameters beim Aufrufen einer Funktion führt dazu, dass dieser Parameter Folgendes erhält
undefined
:
let f = a => a;
console.log(f('hi'));
console.log(f());
- Parameter mit Standardwerten erhalten den Standard nur, wenn sie angegeben sind
undefined
, nicht null
:
let f = (v='hello') => v;
console.log(f(null));
console.log(f(undefined));
Nicht generische Leere
Ich glaube, dass Leere niemals generisch behandelt werden sollte. Wir sollten stattdessen immer die Sorgfalt haben, mehr Informationen über unsere Daten zu erhalten, bevor wir feststellen, ob sie leer sind. Dazu überprüfe ich hauptsächlich, mit welcher Art von Daten ich es zu tun habe:
let isType = (value, Cls) => {
try {
return Object.getPrototypeOf(value).constructor === Cls;
} catch(err) {
return false;
}
};
Beachten Sie, dass diese Funktion den Polymorphismus ignoriert - sie erwartet value
, dass es sich um eine direkte Instanz von Cls
und nicht um eine Instanz einer Unterklasse von handelt Cls
. Ich vermeide instanceof
aus zwei Hauptgründen:
([] instanceof Object) === true
("Ein Array ist ein Objekt")
('' instanceof String) === false
("Ein String ist kein String")
Beachten Sie, dass dies Object.getPrototypeOf
verwendet wird, um einen Fall wie zu vermeiden. let v = { constructor: String };
Die isType
Funktion gibt für isType(v, String)
(false) und isType(v, Object)
(true) immer noch korrekt zurück .
Insgesamt empfehle ich die Verwendung dieser isType
Funktion zusammen mit den folgenden Tipps:
- Minimieren Sie die Anzahl der Codeverarbeitungswerte unbekannten Typs. Zum Beispiel
let v = JSON.parse(someRawValue);
ist unsere v
Variable jetzt von unbekanntem Typ. Wir sollten so früh wie möglich unsere Möglichkeiten einschränken. Der beste Weg, dies zu tun, kann darin bestehen, einen bestimmten Typ zu benötigen: zB if (!isType(v, Array)) throw new Error('Expected Array');
- dies ist ein sehr schneller und ausdrucksstarker Weg, um die generische Natur von zu entfernen v
und sicherzustellen, dass es immer ein ist Array
. Manchmal müssen wir jedoch zulassen v
, dass es mehrere Typen gibt. In diesen Fällen sollten wir v
so früh wie möglich Codeblöcke erstellen, die nicht mehr generisch sind:
if (isType(v, String)) {
/* v isn't generic in this block - It's a String! */
} else if (isType(v, Number)) {
/* v isn't generic in this block - It's a Number! */
} else if (isType(v, Array)) {
/* v isn't generic in this block - it's an Array! */
} else {
throw new Error('Expected String, Number, or Array');
}
- Verwenden Sie zur Validierung immer "Whitelists". Wenn Sie einen Wert benötigen, z. B. eine Zeichenfolge, eine Zahl oder ein Array, suchen Sie nach diesen 3 "weißen" Möglichkeiten und geben Sie einen Fehler aus, wenn keine der 3 erfüllt ist. Wir sollten in der Lage sein zu sehen, dass das Überprüfen auf "schwarze" Möglichkeiten nicht sehr nützlich ist: Sagen wir, wir schreiben
if (v === null) throw new Error('Null value rejected');
- dies ist großartig, um sicherzustellen, dass null
Werte es nicht schaffen, aber wenn ein Wert es schafft , wissen wir es immer noch kaum irgendetwas darüber. Ein Wert, v
der diese Nullprüfung besteht, ist immer noch SEHR generisch - es ist alles andere alsnull
! Blacklists zerstreuen kaum die Generizität.
null
Betrachten Sie niemals "einen leeren Wert", es sei denn, es handelt sich um einen Wert . Betrachten Sie stattdessen "ein leeres X". Denken Sie im Wesentlichen niemals daran, etwas Ähnliches zu tun if (isEmpty(val)) { /* ... */ }
- egal wie diese isEmpty
Funktion implementiert ist (ich möchte es nicht wissen ...), sie ist nicht sinnvoll! Und es ist viel zu allgemein! Die Leere sollte nur mit Kenntnis des val
Typs berechnet werden . Leerheitsprüfungen sollten folgendermaßen aussehen:
- "Eine Zeichenfolge ohne Zeichen":
if (isType(val, String) && val.length === 0) ...
- "Ein Objekt mit 0 Requisiten":
if (isType(val, Object) && Object.entries(val).length === 0) ...
- "Eine Zahl gleich oder kleiner als Null":
if (isType(val, Number) && val <= 0) ...
"Ein Array ohne Elemente": if (isType(val, Array) && val.length === 0) ...
Die einzige Ausnahme ist, wann null
bestimmte Funktionen angegeben werden. In diesem Fall ist es sinnvoll zu sagen: "Ein leerer Wert":if (val === null) ...