Вакуумність
Я не рекомендую намагатися визначити чи використовувати функцію, яка обчислює, чи будь-яке значення у цілому світі порожнє. Що насправді означає бути "порожнім"? Якщо я маю let human = { name: 'bob', stomach: 'empty' }, повинен isEmpty(human)повернутися true? Якщо я маю let reg = new RegExp('');, повинен isEmpty(reg)повернутися true? А як бути isEmpty([ null, null, null, null ])- цей список містить лише порожнечу, тому сам список порожній? Я хочу висунути тут кілька приміток про "вакуумність" (навмисно незрозуміле слово, щоб уникнути раніше існуючих асоціацій) у javascript - і я хочу стверджувати, що "вакуумність" у значеннях JavaScript ніколи не повинна стосуватися загально.
Правдивість / хитрість
Щоб вирішити, як визначити "вакуумність" значень, нам потрібно врахувати вбудований javascript, властивий йому сенс того, чи є значення "truthy" чи "false". Природно, nullі undefinedвони обидва "хибні". Менш природно, число 0(і жодне інше число, крім NaN) теж "хибне". Найменше, природно: ''хибний, але []і {}(і new Set(), і new Map()) є неправдивими, хоча всі вони здаються однаково вакуумними!
Нуль проти невизначеного
Існує також деяка дискусія щодо nullvs undefined- чи нам справді потрібні обидва, щоб виразити нерішучість в наших програмах? Я особисто уникаю того, щоб літери u, n, d, e, f, i, n, e, d відображалися в моєму коді в такому порядку. Я завжди використовую nullдля позначення "вакуумності". Знову ж таки, нам потрібно відповідати властивому JavaSkript почуттю того, як nullі чим undefinedвідрізняються:
- Спроба отримати доступ до неіснуючої власності дає
undefined
- Пропуск параметра при виклику функції призводить до отримання цього параметра
undefined:
let f = a => a;
console.log(f('hi'));
console.log(f());
- Параметри зі значеннями за замовчуванням отримують за замовчуванням лише тоді, коли вони задані
undefined, не null:
let f = (v='hello') => v;
console.log(f(null));
console.log(f(undefined));
Вакуозність, яка не є загальною
Я вважаю, що з вакуумністю ніколи не слід поводитися із загальним способом. Натомість нам слід завжди мати суворість, щоб отримати більше інформації про наші дані, перш ніж визначати, чи є вони вакуумними - я це роблю в основному, перевіряючи, з яким типом даних я маю справу:
let isType = (value, Cls) => {
try {
return Object.getPrototypeOf(value).constructor === Cls;
} catch(err) {
return false;
}
};
Зауважимо, що ця функція ігнорує поліморфізм - вона очікує, valueщо це прямий екземпляр Cls, а не екземпляр підкласу Cls. Я уникаю instanceofз двох основних причин:
([] instanceof Object) === true ("Масив - об'єкт")
('' instanceof String) === false ("Рядок не є рядком")
Зверніть увагу , що Object.getPrototypeOfвикористовується , щоб уникнути випадку , як функція по- , як і раніше повертає правильно для (брехня), і (істина).let v = { constructor: String };isTypeisType(v, String)isType(v, Object)
Загалом, рекомендую використовувати цю isTypeфункцію разом із цими порадами:
- Мінімізуйте кількість значень обробки коду невідомого типу. Наприклад,
let v = JSON.parse(someRawValue);наша vзмінна зараз невідомого типу. Як можна раніше нам слід обмежити наші можливості. Найкращий спосіб зробити це можна, вимагаючи певного типу: наприклад if (!isType(v, Array)) throw new Error('Expected Array');- це дійсно швидкий та виразний спосіб усунути родову природу vта забезпечити її завжди Array. Однак іноді нам потрібно дозволити vбути декількома типами. У цих випадках ми повинні створити блоки коду, де vце вже не є загальним, як можна раніше:
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');
}
- Завжди використовуйте "білі списки" для перевірки. Якщо вам потрібне значення, наприклад, рядок, номер або масив, перевірте ці 3 "білі" можливості і введіть помилку, якщо жоден з 3 не задоволений. Ми повинні бачити, що перевірка "чорних" можливостей не дуже корисна: скажімо, ми пишемо
if (v === null) throw new Error('Null value rejected');- це чудово для того, щоб nullзначення не проробляли, але якщо значення робить це, ми все ще навряд чи знаємо нічого про це. Значення, vяке проходить цю нульову перевірку, все ще ДУЖЕ загальне - це все, алеnull ! Чорні списки навряд чи розвіюють загальну сутність.
Якщо значення не є null, ніколи не вважайте "вакуумним значенням". Замість цього розглянемо "X, який є вакуумним". По суті, ніколи не думайте робити щось подібне if (isEmpty(val)) { /* ... */ }- незалежно від того, як ця isEmptyфункція реалізована (я не хочу знати ...), це не має сенсу! І це занадто загально! Вакуумність повинна обчислюватися лише на основі знань valтипу. Чекові перевірки повинні виглядати так:
- "Рядок без знаків":
if (isType(val, String) && val.length === 0) ...
- "Об'єкт з 0 реквізитами":
if (isType(val, Object) && Object.entries(val).length === 0) ...
- "Число, рівне або менше нуля":
if (isType(val, Number) && val <= 0) ...
"Масив без елементів": if (isType(val, Array) && val.length === 0) ...
Єдиний виняток - коли nullвикористовується для позначення певної функціональності. У цьому випадку має сенс сказати: "Вакуумне значення":if (val === null) ...