Próżnia
Nie polecam próbować definiować ani używać funkcji, która oblicza, czy jakaś wartość na całym świecie jest pusta. Co to tak naprawdę znaczy być „pustym”? Jeśli tak let human = { name: 'bob', stomach: 'empty' }, czy powinienem isEmpty(human)wrócić true? Jeśli tak let reg = new RegExp('');, czy powinienem isEmpty(reg)wrócić true? Co z isEmpty([ null, null, null, null ])- ta lista zawiera tylko pustkę, więc czy sama lista jest pusta? Chcę przedstawić tutaj kilka uwag na temat „próżności” (słowo celowo niejasne, aby uniknąć wcześniejszych skojarzeń) w javascript - i chcę argumentować, że „próżności” w wartościach javascript nigdy nie należy traktować ogólnie.
Prawdomówność / fałsz
Aby zdecydować, w jaki sposób określić „próżność” wartości, musimy uwzględnić wbudowane, javascriptowe nieodłączne poczucie, czy wartości są „prawdą”, czy „fałszem”. Oczywiście nulli undefinedoba są „falsy”. Mniej naturalnie liczba 0(i żadna inna liczba oprócz NaN) jest również „fałszem”. ''Przynajmniej naturalnie: jest fałszem, ale []i {}(i new Set(), i new Map()) są prawdomówne - chociaż wszystkie wydają się równie próżne!
Null vs Undefined
Trwa także dyskusja na temat nullvs undefined- czy naprawdę potrzebujemy obu, aby wyrazić pustkę w naszych programach? Osobiście unikam, aby litery u, n, d, e, f, i, n, e, d pojawiały się w moim kodzie w tej kolejności. Zawsze używam nulldo oznaczenia „próżności”. Ponownie, choć musimy pomieścić Javascript w przyrodzone poczucie jak nulli undefinedróżnią się:
- Próba uzyskania dostępu do nieistniejącej właściwości daje
undefined
- Pominięcie parametru podczas wywoływania funkcji powoduje otrzymanie tego parametru
undefined:
let f = a => a;
console.log(f('hi'));
console.log(f());
- Parametry z wartościami domyślnymi otrzymują wartość domyślną tylko wtedy
undefined, gdy są podane , a nie null:
let f = (v='hello') => v;
console.log(f(null));
console.log(f(undefined));
Nieogólna próżnia
Uważam, że pustki nigdy nie powinny być traktowane w sposób ogólny. Zamiast tego powinniśmy zawsze dokładać starań, aby uzyskać więcej informacji o naszych danych przed ustaleniem, czy są one puste - robię to głównie poprzez sprawdzenie, z jakim typem danych mam do czynienia:
let isType = (value, Cls) => {
try {
return Object.getPrototypeOf(value).constructor === Cls;
} catch(err) {
return false;
}
};
Zauważ, że ta funkcja ignoruje polimorfizm - oczekuje valuesię, że będzie to bezpośrednia instancja Cls, a nie instancja podklasy Cls. Unikam instanceofz dwóch głównych powodów:
([] instanceof Object) === true („Tablica to obiekt”)
('' instanceof String) === false („Ciąg nie jest ciągiem”)
Zauważ, że Object.getPrototypeOfjest używany, aby uniknąć przypadku takiego jak let v = { constructor: String };. isTypeFunkcja nadal zwraca poprawnie dla isType(v, String)(fałsz) iisType(v, Object) (prawda).
Ogólnie polecam korzystanie z tej isTypefunkcji wraz z następującymi wskazówkami:
- Zminimalizuj ilość wartości przetwarzania kodu nieznanego typu. Np.
let v = JSON.parse(someRawValue);Nasza vzmienna jest teraz nieznanego typu. Jak najwcześniej powinniśmy ograniczyć nasze możliwości. Najlepszym sposobem na to może być wymaganie określonego typu: np. if (!isType(v, Array)) throw new Error('Expected Array');- jest to naprawdę szybki i ekspresyjny sposób na usunięcie ogólnej natury vi zapewnienie, że zawsze będzie Array. Czasami jednak musimy pozwolić vna bycie wielu typów. W takich przypadkach vjak najwcześniej powinniśmy utworzyć bloki kodu, które nie są już ogólne.
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');
}
- Zawsze używaj „białych list” do walidacji. Jeśli potrzebujesz, aby wartością była np. Ciąg, Liczba lub Tablica, sprawdź te 3 „białe” możliwości i wyrzuć Błąd, jeśli żadna z 3 nie jest spełniona. Powinniśmy być w stanie zobaczyć, że sprawdzanie dla „czarnych” możliwości nie jest bardzo przydatne: Say piszemy
if (v === null) throw new Error('Null value rejected');- to jest wielki dla zapewnienia, że nullwartości nie zrobić to przez, ale jeśli to jest wartość nie zrobić to przez, wciąż wiemy mało cokolwiek na ten temat. Wartość, vktóra przejdzie kontrolę zerową, jest nadal BARDZO ogólna - to nic innegonull ! Czarne listy prawie nie rozpraszają ogólności.
O ile wartość nie jest null, nigdy nie uważaj „wartości pustej”. Zamiast tego rozważ „X, który jest pusty”. Zasadniczo nigdy nie rozważaj robienia czegoś podobnego if (isEmpty(val)) { /* ... */ }- bez względu na to, jak ta isEmptyfunkcja jest zaimplementowana (nie chcę wiedzieć ...), to nie ma znaczenia! I to jest zbyt ogólne! Wakat należy obliczać tylko przy znajomości valrodzaju. Kontrole próżni powinny wyglądać następująco:
- „Ciąg bez znaków”:
if (isType(val, String) && val.length === 0) ...
- „Obiekt z 0 rekwizytami”:
if (isType(val, Object) && Object.entries(val).length === 0) ...
- „Liczba równa lub mniejsza niż zero”:
if (isType(val, Number) && val <= 0) ...
„Tablica bez przedmiotów”: if (isType(val, Array) && val.length === 0) ...
Jedynym wyjątkiem jest sytuacja, gdy nulljest używany do oznaczenia określonej funkcjonalności. W tym przypadku sensowne jest powiedzenie: „Pusta wartość”:if (val === null) ...