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 nullund undefinedsind 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 nullvs 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 nullund undefinedUnterschied 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 Clsund nicht um eine Instanz einer Unterklasse von handelt Cls. Ich vermeide instanceofaus zwei Hauptgründen:
([] instanceof Object) === true ("Ein Array ist ein Objekt")
('' instanceof String) === false ("Ein String ist kein String")
Beachten Sie, dass dies Object.getPrototypeOfverwendet wird, um einen Fall wie zu vermeiden. let v = { constructor: String };Die isTypeFunktion gibt für isType(v, String)(false) und isType(v, Object)(true) immer noch korrekt zurück .
Insgesamt empfehle ich die Verwendung dieser isTypeFunktion zusammen mit den folgenden Tipps:
- Minimieren Sie die Anzahl der Codeverarbeitungswerte unbekannten Typs. Zum Beispiel
let v = JSON.parse(someRawValue);ist unsere vVariable 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 vund 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 vso 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 nullWerte es nicht schaffen, aber wenn ein Wert es schafft , wissen wir es immer noch kaum irgendetwas darüber. Ein Wert, vder diese Nullprüfung besteht, ist immer noch SEHR generisch - es ist alles andere alsnull ! Blacklists zerstreuen kaum die Generizität.
nullBetrachten 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 isEmptyFunktion 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 valTyps 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 nullbestimmte Funktionen angegeben werden. In diesem Fall ist es sinnvoll zu sagen: "Ein leerer Wert":if (val === null) ...